Reputation: 33
I would like to ask to the community what is the best practice to combine existing code using react-redux connect()
and dependency management in hooks like useEffect()
.
Let's look at the following example:
/* ... */
const mapStateToProps = (state) => ({
todos: getTodos(state),
currentUserId: getCurrentUserId(state)
})
const mapDispatchToProps = (dispatch) => ({dispatch})
const mapMergeProps = (stateProps, {dispatch}, ownProps) => {
return {
...stateProps,
...dispatchProps,
...ownProps,
fetchTodos: () => dispatch(fetchTodos(stateProps.currentUserId))
}
}
const TodosListContainer = connect(mapStateToProps, mapDispatchToProps, mapMergeProps)
const TodosList = ({todos, fetchTodos, ...props}) => {
const _fetchTodos = useCallback(fetchTodos, [])
useEffect(() => {
_fetchTodos()
}, [_fetchTodos])
return (
<ul>
{todos && todos.map((todo) => <li key={todo.id}>{todo.name}</li>)}
</ul>
)
}
In the code above useEffect has all the dependencies, and useCallback ensures that useEffect is triggered only once.
It seems however like an extra layer of boilerplate: Without useCallback, the functions passed down as props that are coming from connect's mapMergeToProps
will be recreated on every change of state, and will trigger useEffect
.
My question is whether the code above is correct, and there's a better way to handle useEffect in the context described.
Upvotes: 3
Views: 1043
Reputation: 18739
I would change it to the following
// Leave this one like it is
const mapStateToProps = (state) => ({
todos: getTodos(state),
currentUserId: getCurrentUserId(state)
})
// Map the fetchTodos here
const mapDispatchToProps = {
fetchTodos,
}
// Completely remove the mergeProps
const TodosListContainer = connect(mapStateToProps, mapDispatchToProps)
// Then use the hook like this
const TodosList = ({ todos, fetchTodos, currentUserId, ...props }) => {
useEffect(() => {
fetchTodos(currentUserId)
}, [fetchTodos, currentUserId])
return (
<ul>
{todos && todos.map((todo) => <li key={todo.id}>{todo.name}</li>)}
</ul>
)
}
This will not trigger useEffect
on each render because the props stay the same. It removes (some of the) boilerplate code. I also think it makes it easier to understand what is going and it will run the effect when the currentUserId
changes, which is probably what is meant to happen.
Upvotes: 3