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>