-
Notifications
You must be signed in to change notification settings - Fork 47.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
getState() hook proposal #14092
Comments
I don't think this is necessary, you need to clean up your With this you can pass the state as the second argument of Btw I think this is wanted that you cannot access new state in useEffect. It's avoiding misuse of it. |
Unsubscribing and subscribing sockets on every message would be a very idea, performance wise the least |
This is intended, though I think there is a way around this by using |
Probably better to split it up in two effects! (connect as a separate effect) |
Yup the idea of @thchia to use Like this: const usersReducer = (users = [], actions = {}) => {
switch (action.type) {
case 'ADD_USER': return [...users, action.payload.user]
case 'UPDATE_USER': {
const { user } = action.payload
return users.map((u) => u.id == user.id ? user : u)
}
default: return users
}
}
const UserList = () => {
const [users, dispatch] = useReducer([])
useEffect(() => {
const socket = io('/dashboard')
socket.on('user:connect', (user) => {
dispatch({ type: 'ADD_USER', payload: { user } })
})
socket.on('user:update', (user) => {
dispatch({ type: 'UPDATE_USER', payload: { user } })
})
}, [])
return (
users.map(({id, email}) => (
<tr key={id}>
<td>{id}</td>
<td>{email}</td>
</tr>
))
)
} EDIT: An example with custom event (instead of sockets connections) |
I have considered useReducer before, but I do not wan't to use 'redux' or any other boilerplate in this instance. I want simplicity and in this case it turns out that it can be better achieved with class components. |
reducer does not mean "Redux", it's just a "reducer" pretty much like "Array.prototype.reduce" |
@ChibiBlasphem this again means boilerplate or jumping through hoops, I expect React Hooks to make my life simpler not more complicated. |
Hum I don't think hooks mean to make life "simplier", your code is better designed with less possibility to break Nothing stops you from doing this function useUserList(initialUsers = []) {
const [users, setUsers] = useState(initialUsers)
const usersRef = useRef(users)
return {
users: usersRef,
setUsers: function(mapper) {
usersRef.current = mapper(usersRef.current)
setUsers(mapper)
},
}
}
const UserList = () => {
const { users, setUsers } = useUserList()
// ...
} Refs are made for this: |
This is a known limitation. We might make
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. |
I filed #14099 to track the root problem. |
@gaearon, it would be really useful to add the const [, dispatch] = useReducer(reducer, initialState)
dispatch(state => state.isLoading || doSomething()) In that case, state would always point to the freshest state. |
How about a 3rd parameter on a useEffect hook, where the 3rd parameter lists variables that should pass in updated values, but not trigger execution of the useEffect. Kind of a hybrid of a useCallback and useEffect. So rewriting the original proposal's example:
|
@furnaceX it can't work. You can't update the variable |
Using the callback-based const UserList = () => {
const [users, setUsers] = useState([])
useEffect(() => {
const socket = io('/dashboard')
socket.on('user:connect', (user) => {
setUsers(currentUsers => [...currentUsers, user]);
})
socket.on('user:update', (user) => {
setUsers(currentUsers => currentUsers.map((u) => u.id == user.id ? user : u));
})
}, [])
return (
users.map(({id, email}) => (
<tr key={id}>
<td>{id}</td>
<td>{email}</td>
</tr>
))
)
} |
We've submitted an alternative proposal to solve this. Would appreciate your input! |
useState()
provided state value currently cannot be used inuseEffect(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:
Upon running this I instantly realised that inside the
socket.on()
handler theusers
initially obtained from useState() did not reflect the changes inflicted bysetUsers()
ran onsocket.on('user:connect')
. Passing[users]
as the second argument ofuseEffect()
wasn't an option as that would cause additionalsocket.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:The
setState()
problem was solved, but I am now wondering that if I will ever need to accessstate
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:
And here is one with a proposed solution:
The text was updated successfully, but these errors were encountered: