Home > Software design >  Give ref to functional component to use inner functions
Give ref to functional component to use inner functions

Time:01-13

I have a Component let's say:

ParenComp.js

const ParentComp = (props) => {
  const [isTrue, setIsTrue] = useState(false);
  const setToTrue = () => {
    setIsTrue(true);
  };
  const setToFalse = () => {
    setIsTrue(false);
  };
  return isTrue ? (
  
      <Text >
        This is True
      </Text>
    
  ) : (
       <Text >
        This is False
      </Text>
  );
};
export default ParentComp;

Main Question

How can I use the setToTrue and setToFalse function in other functional component in any other file for example (Login.js)?

What I tried

I want to use the inner functions in another file, I know I can not export the functions like this:

export const setToTrue = () => { setIsTrue(true); };

^ This is invalid

But what I was trying to do is (in ParentComp.js) create a reference using createRef, export it and create and export two functions that call the inside functions like this:

export const trueRef = React.createRef();
export function setToTrue() {
  let ref = trueRef.current;
  if (ref) {
    ref.setToTrue();
  }
}

export function setToFalse() {
  let ref = trueRef.current;
  if (ref) {
    ref.setToFalse();
  }
}

Now when I want to use this in my (Login.js). This is what I do:

const Login = ({ navigation }) => {
return (
<View>
<ParentComp ref={trueRef}/>
</View>
)
}

But the problem is, ref is not being passed to ParentComp here

<ParentComp ref={trueRef}/>

So, without using CLass Components, how can I pass ref to my functional component to utilize the functions inside it?

CodePudding user response:

Use the useImperativeHandle hook with ref forwarding to give an external component access to the methods.

As noted by @technophyle and @Drew Reese in the comments, useImperativeHandle is an escape hatch that is usually used in specific cases that require direct access to a DOM node. For example, focusing an input, or scrolling to an element.

Example:

const { forwardRef, useState, useImperativeHandle, useRef } = React;

const ParentComp = forwardRef((props, ref) => {
  const [isTrue, setIsTrue] = useState(false);
  const setToTrue = () => { setIsTrue(true); };
  const setToFalse = () => { setIsTrue(false); };
  
  useImperativeHandle(ref, () => ({
    setToTrue,
    setToFalse
  }));
  
  return (
    <div>This is {isTrue ? 'True' : 'False'}</div>
  );
});

const Login = () => {
  const trueRef = useRef();
  
  return (
    <div>
      <ParentComp ref={trueRef}/>
      <button onClick={() => trueRef.current.setToTrue()}>True</button>
      <button onClick={() => trueRef.current.setToFalse()}>False</button>
    </div>
  );
};
  
ReactDOM
  .createRoot(root)
  .render(<Login />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>

<div id="root"></div>

A better way to reuse the logic, is to create a custom hook that encapsulates the behavior and use it in Login:

const { useState } = React;

const useIsTrue = () => {
  const [isTrue, setIsTrue] = useState(false);
  
  return {
    setToTrue() { setIsTrue(true); },
    setToFalse() { setIsTrue(false); },
    isTrue
  };
}

const ParentComp = ({ isTrue }) => (
  <div>This is {isTrue ? 'True' : 'False'}</div>
);

const Login = () => {
  const { setToTrue, setToFalse, isTrue } = useIsTrue();
  
  return (
    <div>
      <ParentComp isTrue={isTrue}/>
      <button onClick={setToTrue}>True</button>
      <button onClick={setToFalse}>False</button>
    </div>
  );
};
  
ReactDOM
  .createRoot(root)
  .render(<Login />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>

<div id="root"></div>

  • Related