Reputation: 4353
Say I want to create the often wanted useInputState
hook:
function useInputState(initialValue) {
const [value, setValue] = useState(initialValue);
const onChange = useCallback(
e => setValue(e.target.value),
[ /* ??? */ ]
);
return { value, onChange };
}
Would I need to add the setValue
setter function to the callback's dependencies?
Can we count on the setter to always stay the same?
This seems to work when I try it, but is this good practice? Or should we assume ANYTHING in the callback's closure can change and affect its implementation?
(I can think of more examples that eventually raise the same question)
Upvotes: 6
Views: 693
Reputation: 222354
Yes, useState
state setter is the same function; even if it wouldn't, it would be expected to update the state. It is supposed to be used like the question shows.
The common usage is explained better in useReducer
documentation:
useReducer also lets you optimize performance for components that trigger deep updates because you can pass dispatch down instead of callbacks.
And useState
uses useReducer
internally.
The concerns with a scope where useState
was called are applicable only to useState
state value which stays the same in the scope of a callback where it's used, e.g. this problem. In case current state should be used, it should be retrieved with state updater function, like setValue(currentState => currentState + 1)
.
Upvotes: 1
Reputation: 80986
Yes, the setter from useState
and similarly the dispatch from useReducer
do not change.
This portion of the docs covers the pattern of passing the dispatch
method to descendant components via context and contains the following:
If you use context to pass down the state too, use two different context types — the dispatch context never changes, so components that read it don’t need to rerender unless they also need the application state.
Upvotes: 3