Reputation: 5510
Not getting updated value of state in function which gets triggered by eventListener. Even tried with background timer. What would be the problem?
displayIncomingCall() // this function gets called from UI.
const callApp = () => {
const [calls, setCalls] = useState({});
const addCall = (key, value) => {
setCalls({ ...calls, [key]: value });
};
const displayIncomingCall = number => {
const callUUID = getCurrentCallId();
addCall(callUUID, number);
...
};
const onAnswerCall = () => {
console.log('=== onAnswerCall ::: ===', calls); <--- always initial value. Not getting updated one.
}
useEffect(() => {
console.log('=== useEffect ::: ===', calls); // getting updated value
}, [calls]);
useEffect(() => {
RNCallKeep.addEventListener('answerCall', onAnswerCall);
return () => {
RNCallKeep.removeEventListener('answerCall', onAnswerCall);
};
}, []);
console.log('=== parent ::: ===', calls); // getting updated value
return (...)
}
Upvotes: 1
Views: 130
Reputation: 1842
Your event listener callback is closed over the initial state of calls
. Add it as a dependency to the effect, and you should register a new listener (and remove old ones) each time its value changes.
EDIT: In reality, you should move the entire onAnswerCall
function into the effect, since it is only used there:
useEffect(() => {
const onAnswerCall = () => {
console.log('=== onAnswerCall ::: ===', calls);
};
RNCallKeep.addEventListener('answerCall', onAnswerCall);
return () => {
RNCallKeep.removeEventListener('answerCall', onAnswerCall);
};
}, [calls]);
Upvotes: 1
Reputation: 37288
React hooks state does not persist any state data, you have to use useCallback
and the previous state privided in the set*
state functions to keep up with your state:
const callApp = () => {
const [calls, setCalls] = useState({});
const addCall = (key, value) => {
// Use previous state provided to update the state
setCalls(calls => ({ ...calls, [key]: value }));
};
const displayIncomingCall = number => {
const callUUID = getCurrentCallId();
addCall(callUUID, number);
...
};
// Use useCallback to keep up with the state
const onAnswerCall = useCallback(() => {
console.log('=== onAnswerCall ::: ===', calls); <--- Will get updated
}, [calls]);
useEffect(() => {
console.log('=== useEffect ::: ===', calls); // getting updated value
}, [calls]);
useEffect(() => {
RNCallKeep.addEventListener('answerCall', onAnswerCall);
return () => {
RNCallKeep.removeEventListener('answerCall', onAnswerCall);
};
}, [onAnswerCall]);
console.log('=== parent ::: ===', calls); // getting updated value
return (...)
}
Upvotes: 1
Reputation: 370619
onAnswerCall
is only referenced in the useEffect
callback, and the useEffect
callback has an empty dependency array, so it only runs on the initial mount. Whenever onAnswerCall
is called, the binding of calls
that it can see is in an old closure.
Use a ref instead of state:
const callsRef = useRef({});
const addCall = (key, value) => {
callsRef.current[key] = value;
};
const onAnswerCall = () => {
console.log('=== onAnswerCall ::: ===', callsRef.current);
}
If calls
needs to be in state so that its change results in a re-render, then you can either use both state and a ref, and assign to the ref when you call setCalls
, or you can remove the dependency array from useEffect
, thus adding and removing the listeners to RNCallKeep
on each render.
Upvotes: 1