getState() hook proposal
See original GitHub issueuseState() provided state value currently cannot be used in useEffect(fn, []) - (componentDidMount-like scenario) with asynchronous functions after state has been updated following the initial [] run.
Upon trying to give Hooks a try for a real world application I was initially confused with accessing state. Here is a little example what I tried to do:
const UserList = () => {
const [users, setUsers] = useState([])
useEffect(() => {
const socket = io('/dashboard')
socket.on('user:connect', (user) => {
setUsers([...users, user])
})
socket.on('user:update', (user) => {
let newUsers = users.map((u) => u.id == user.id ? user : u)
setUsers(newUsers)
})
}, [])
return (
users.map(({id, email}) => (
<tr key={id}>
<td>{id}</td>
<td>{email}</td>
</tr>
))
)
}
Upon running this I instantly realised that inside the socket.on() handler the users initially obtained from useState() did not reflect the changes inflicted by setUsers() ran on socket.on('user:connect'). Passing [users] as the second argument of useEffect() wasn’t an option as that would cause additional socket.on() binds. I became skeptical about Hooks for this use case and sadly thought this would be where my journey with using hooks instead of the class components would end.
Fortunately I then found a solution to this problem (with someone indirectly having helped me by accident in the reactflux channel) by using an updater function with setState() which made it all work:
socket.on('user:update', (user) => {
setUsers(users => users.map((u) => u.id == user.id ? user : u))
})
The setState() problem was solved, but I am now wondering that if I will ever need to access state outside of an updater function, i.e. to just reply to a WebSocket message with some value from the state I will be unable to do so and this will force me and other users to revert to class components for such cases.
I therefore would like to suggest that a getState() hook would be an ideal solution to this problem.
Here is another mini example demonstrating the problem in a more concise manner:
const HooksComponent = () => {
const [value, setValue] = useState({ val: 0 });
useEffect(() => {
setTimeout(() => setValue({ val: 10 }), 100)
setTimeout(() => console.log('value: ', value.val), 200)
}, []);
}
//console.log output: 0 instead of 10
And here is one with a proposed solution:
const HooksComponent = () => {
const [state, setState, getState] = useState({ val: 0 });
useEffect(() => {
setTimeout(() => setState({ val: 10 }), 100)
setTimeout(() => {
getState(state => {
console.log('value: ', state.val)
})
}, 200)
}, [])
Issue Analytics
- State:
- Created 5 years ago
- Reactions:10
- Comments:16 (3 by maintainers)

Top Related StackOverflow Question
@gaearon, it would be really useful to add the
updaterFnargument to thedispatchmethod returned byuseReducer, so you could do something like:In that case, state would always point to the freshest state.
This is a known limitation. We might make
useCallbackhandle these cases better in the future. We know that in practice it invalidates too often. The current recommended workaround is one of three options:useReducerorsetState(updaterFn)to avoid closing over the variables. This doesn’t solve all use cases (especially when you also need to perform side effects) but gets you pretty close.There is also a more hacky workaround using refs that essentially emulates what classes do. I only recommend it as last resort since it may cause some issues in the future, but at least it’s no worse than classes.
We want to provide a better solution but it will take some time.