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?
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>