I'm experiencing problems with the useEffect() method!
I have a ParentComponent with a function doSomething()
that prints out a variable called names.
Inside of this component I have a <Text>
component (which displays the names) and below it I have a ChildComponent.
In the ChildComponent I have a TextInput
which updates the parent variable name on change.
I want to trigger the doSomething()
function from the ChildComponent, whenever the component unmounts.
// Parent Component
const ParentComponent = () => {
const [names, setNames] = useState('BOB');
const childRef = useRef(null);
const doSomething = () => {
console.log('Did something with the names:' names)
};
return (
<Text>The names: {names}</Text>
<ChildComponent
doSomething={doSomething}
names={names}
setNames={setNames}
ref={childRef}
</ChildComponent>
);
};
// Child Component
const ChildComponent = forwardRef((props, ref) => {
const {doSomething, names, setNames } = props
// Load names to input on mount
React.useEffect(() => {
textInput.focus();
textInput.value = names;
}, []);
// Do something on willUnmount
React.useEffect(() => {
return () => {
doSomething();
};
}, []);
return (
<View>
<TextInput
value={names}
onChangeText={text => setNames(text)}
ref={input => {
this.textInput = input;
}}
/>
</View>
);
});
The problem is:
If I edit the names to "Bob, Andrew", the parent <Text>
component updates its value, however on unmount the doSomething()
function logs out the old value "Bob"
Why is that so?
EDIT:
I've tried to pass the names in the function from the ChildComponent
In the ChildComponent:
const [childNames, setChildNames] = useState(names);
// Do something on willUnmount
React.useEffect(() => {
return () => {
doSomething(childNames);
};
}, [childNames]);
But this causes to trigger the doSomething()
every time the text changes (every keyboard press) - but I want it to be triggered only when the component disappears/unmounts
CodePudding user response:
There is an closure problem. When the callback function in useEffect is initialized, the first value of the doSomething is kept in the memory.
This is what happens:
first render:
- doSomething knows about the value of names (Bob)
- useEffect stores in the memory the reference of that doSomething, that knows Bob.
Second render:
doSomething is reinitialized again. Now it knows the value of names as 'Bob, Andrew'
useEffect - since the dependencies does not change, it does not store the new reference of doSomething. It still keeps the old one that knows 'Bob'. This is why it logs you always the initial value.
You can avoid it like this:
const ref = useRef()
ref.current = childNames
const doSomething = () => {
// here use ref.current instead of childNames
// it has the updated value.
}
useEffect(() => doSomething, [])
ref has value {current: ...}. doSomething stores in the memory the reference of the ref. It knows it's reference. The thing useRef returns you never changes its reference. You can only change the property called 'current'