You're probably familiar with the concept of using redux HOC called connect
. It probably looks something like this (abbreviation):
import { getUsers, resetUsers } from 'actions/users';
import { selectUsers } from 'store/users';
const App = ({ users, getUsers, resetUsers }) => {
useEffect(() => {
getUsers();
return () => resetUsers();
}, [getUsers])
...
}
App.propTypes = {
users: PropTypes.arrayOf(PropTypes.object),
getUsers: PropTypes.func.isRequired,
resetUsers: PropTypes.func.isRequired,
};
const mapStateToProps = { users: selectUsers() };
const mapDispatchToProps = { getUsers, resetUsers };
export default connect(mapStateToProps, mapDispatchToProps)(App);
A simple component which uses redux's connect
HOC to provide <App />
component with getUsers
, resetUsers
and users
prop.
Let's see how hooks can simplify our code.
Analyzing the code:
1. Imports
In this part of the code, we are importing our actions and selectors used in mapStateToProps
& mapDispatchToProps
import { getUsers, resetUsers } from 'actions/users';
import { selectUsers } from 'store/users';
2. Component itself
In this part of the code, we are defining our App
component and destructuring users
, getUsers
& resetUsers
from the prop. We utilize a simple useEffect
hook to call users on mount, and reset them on unmount.
const App = ({ users, getUsers, resetUsers }) => {
useEffect(() => {
getUsers();
return () => resetUsers();
}, [getUsers])
...
}
3. PropTypes
In this part of the code, we are defining prop types used by our component.
App.propTypes = {
users: PropTypes.arrayOf(PropTypes.object),
getUsers: PropTypes.func.isRequired,
resetUsers: PropTypes.func.isRequired,
};
4. connect HOC
In this part of the code, we are utilizing mapStateToProps
and mapDispatchToProps
through connect
high order component.
const mapStateToProps = { users: selectUsers() };
const mapDispatchToProps = { getUsers, resetUsers };
export default connect(mapStateToProps, mapDispatchToProps)(App);
Using react-redux hooks
If we were to use hooks instead of HOCs, we'd end up with something like this:
import { useSelector, shallowEqual, useDispatch } from 'react-redux';
import { getUsers, resetUsers } from 'actions/users';
import { selectUsers } from 'store/users';
const App = () => {
const dispatch = useDispatch();
const users = useSelector(selectUsers(), shallowEqual)
useEffect(() => {
dispatch(getUsers());
return () => dispatch(resetUsers());
}, [getUsers, dispatch])
...
}
export default App;
Notice how code is much cleaner, tidy, and implementation is done with less code?
Using react-redux hooks we've eliminated the need for mapDispatchToProps
& mapStateToProps
constants, as well as HOCs wrapping of our component.
Further implementation could be implementing custom hooks to handle redux logic, and would be used something like this:
import { useSelector, shallowEqual, useDispatch } from 'react-redux';
import { getUsers, resetUsers } from 'actions/users';
import { selectUsers } from 'store/users';
export const useAppStore = () => {
const dispatch = useDispatch();
const users = useSelector(selectUsers(), shallowEqual);
const actions = useMemo(() => ({
getUsers: dispatch(getUsers()),
resetUsers: dispatch(resetUsers()),
}), [dispatch]);
return { users, actions }
}
import { useAppStore } from './hooks';
const App = () => {
const { users, actions } = useAppStore();
useEffect(() => {
actions.getUsers()
return () => actions.resetUsers()
}, [actions.getUsers, actions.resetUsers])
...
}
With this, we've achieved separation of concerns.
Conclusion
React hooks offer many new ways to solve old problems - one of them being selectors and action dispatches of redux. Most of the libraries which HOCs you're using are providing hooks as well. Make sure to update your packages and check the documentation for the hooks implementation tutorial to keep your codebase fresh!
Top comments (2)
You forgot to return
actions
from theuseAppStore
hook. Nice article!Thanks, Valentin! It's updated.