We use a third party library (over which there is limited control) that takes a callback as argument to a function. What is the correct way to provide that callback with the latest state? In class components, this would be done through the use of this
. In React hooks, due to the way state is encapsulated in the functions of React.useState()
, if a callback gets the state through React.useState()
, it will be stale (the value when the callback was setup). But if it sets the state, it will have access to the latest state through the passed argument. This means we can potentially get the latest state in such a callback with React hooks by setting the state to be the same as it was. This works, but is counter-intuitive.
With the following code, every time the callback accesses it, it's back at its default value. The console will keep printing Count is: 0
no matter how many times I click.
function Card(title) {
const [count, setCount] = React.useState(0)
const [callbackSetup, setCallbackSetup] = React.useState(false)
function setupConsoleCallback(callback) {
console.log("Setting up callback")
setInterval(callback, 3000)
}
function clickHandler() {
setCount(count+1);
if (!callbackSetup) {
setupConsoleCallback(() => {console.log(`Count is: ${count}`)})
setCallbackSetup(true)
}
}
return (<div>
Active count {count} <br/>
<button onClick={clickHandler}>Increment</button>
</div>);
}
const el = document.querySelector("#root");
ReactDOM.render(<Card title='Example Component' />, el);
I've had no problem setting state within a callback, only in accessing the latest state.
If I was to take a guess, I'd think that any change of state creates a new instance of the Card function. And that the callback is referring to the old one. Based on the documentation at https://reactjs.org/docs/hooks-reference.html#functional-updates, I had an idea to take the approach of calling setState in the callback, and passing a function to setState, to see if I could access the current state from within setState. Replacing
setupConsoleCallback(() => {console.log(`Count is: ${count}`)})
with
setupConsoleCallback(() => {setCount(prevCount => {console.log(`Count is: ${prevCount}`); return prevCount})})
Does work. I need to call setState to access the previous state. Even though I have no intention of setting the state.
How can I access the latest state information from within a callback?
this
to the callback if I was using classes, but couldn't see how to do it with Effects. I tried approaches such as enclosing getters to the state variable as argument to the callback. But nothing worked. At any rate, after reviewing all the links everyone shared, something still isn't clear to me. Is there no way to just read the component's state from within another context than through calling its state-setting function (specifically when I do not want to change its state)? – UpanchoruseReducer()
hook for this? In my case, I have a callback passed to a library that is triggered on messages from Server Sent Events. When a message arrives to the client, the callback is triggered and pushes it to the existingmessages
state. – AniseeduseEvent
proposal. – Humphries