Closed English3000 closed 5 years ago
Function components capture values they rendered with. So this behavior is expected.
Depending on what you want to do, there are a few workarounds — but usually there's also a way to structure your code in a clearer way.
https://reactjs.org/docs/hooks-faq.html#why-am-i-seeing-stale-props-or-state-inside-my-function
https://overreacted.io/how-are-function-components-different-from-classes/
I see.
To summarize how to debug when defining a function within an SFC (stateless functional component) while using Hooks:
state
~ useRef
instead of useState
when you want to set a value at a different stage in the lifecycle than constructor/1
props
w/ useEffect
~ if it remains stale, make sure you've added that specific stale prop to the dependencies arrayMy issues are now fixed! Thank you for the very helpful links!!
Thanks for the posts, they were really enlightening!
I like the suggestion of using useReducer
and pass the dispatch
to a provider in order to use it in child components. I wonder if creating a custom hook that provides both state and actions, and pass them to the provider, would be a nice approach. Something like:
function useTodosReducer(reducer) {
const [data, dispatch] = useReducer(reducer)
function addTodo(text) {
dispatch({ type: 'add', text })
}
return {
data,
addTodo
}
}
function TodosApp() {
const store = useTodosReducer(todosReducer)
return (
<TodosDispatch.Provider value={store}>
<DeepTree todos={store.data} />
</TodosDispatch.Provider>
)
}
I'm gonna try it during the weekend.
I forgot to put here my findings on the useReducer
hook.
After using hooks for a while now, I've found out that for the useReducer
I'd prefer to create 2 contexts, one for the state and another one for the dispatch function.
const TodosState = createContext()
const TodosDispatch = createContext()
function TodosProvider({ children }) {
const [state, dispatch] = useReducer(todosReducer)
return (
<TodosState.Provider value={state}>
<TodosDispatch.Provider value={dispatch}>
{children}
</TodosDispatch.Provider>
</TodosState.Provider>
)
}
function useTodosState() {
return useContext(TodosState)
}
function useTodosDispatch() {
return useContext(TodosDispatch)
}
Having different contexts allows me to use the dispatch in components that doesn't need to re-render after the state changes.
I've also found pretty nice to implement the actions in the provider. So, instead of passing a dispatch to the context, I pass an actions API:
const TodosActions = createContext()
function TodosProvider({ children }) {
const [state, dispatch] = useReducer(todosReducer)
const actions = useMemo(() => {
function addTodo(text) {
dispatch({ type: 'add', text })
}
return { addTodo }
}, [dispatch])
return (
<TodosState.Provider value={state}>
<TodosActions.Provider value={actions}>
{children}
</TodosActions.Provider>
</TodosState.Provider>
)
}
function useTodosActions() {
return useContext(TodosActions)
}
Do you want to request a feature or report a bug? Bug.
What is the current behavior?
If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem:
Island
w/ HooksuseEffect
What is the expected behavior?
onBoard
state hook updates value.Logic works w/o hooks:
Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
"^16.8.4"
... then tried"^16.8.6"