Things I wish I knew about Redux
I’ve been working on React/Redux apps for about a year. I wish I knew these things when I started.
Looking back, these things should have been obvious to me. But they weren’t. I feel dumb. Don’t be dumb. Learn from my mistakes.
You can return things from thunks. Thunks are basically async action creators. Returning promises from thunks is useful, because then you can chain together async functions.
function loadUser(userId) => {
return (dispatch, getState) => {
return Promise.resolve({name: 'blah blah blah'});
};
};componentDidMount() {
this.props.loadUser(1234)
.then((user) => this.props.loadCompany(user))
}
Of course, after using many thunks and promises, and having errors constantly being swallowed, I really wish I had tried out something like redux-saga instead.
Its okay to connect many components. Redux used to recommend only connecting a few components (and putting them in a containers folder). Now they don’t. Thank goodness. Stuffing all your data into a handful of connected components sucked. Passing a bunch of props over and over sucked even more. There are places where containers make sense. But you shouldn’t feel pressured to structuring all of your code into a few connected containers.
Denormalizing data in mapStateToProps is normal. When I first started using Redux, I wasn’t sure if it was kosher to do “computation” in mapStateToProps functions. I didn’t see examples of this in the Redux repo or docs. It took a few weeks for me to find somebody else using a “selector” in mapStateToProps. You have no idea how excited I was to discover somebody else doing this!
Today, it seems obvious to me that you have to do computations in mapStateToProps. Otherwise, you can’t have a denormalized state tree. And there’s a name for these computations: selectors. Coming from knockout, selectors remind me of computed observables.
If your selectors are slow, check out https://github.com/reactjs/reselect. But be careful with props in your selectors.
Use reselect sparingly, use props in selectors even more sparingly. If you have a selector that uses props and is shared across multiple components, you will end up with a performance nightmare. I explain my reasoning in another post.
Not all state belongs in the Redux store.
Using an absolute import path makes Redux easier.
Compare these two equivalent imports:
import {setSelectedOrderNumber} from ‘../../actions/ui/orderListPage’;import {setSelectedOrderNumber} from ‘actions/ui/orderListPage’;
There are a couple ways to do this. My preferred way is changing the webpack resolve modulesDirectories option.
resolve: {
modulesDirectories: [‘node_modules’, ‘src’],
}
If you use a Atom plugin like js-hyperclick, you’ll need to modify it as well. I should submit a PR sometime, but I’m too lazy for that.
Redux ducks is a cool idea, but it doesn’t scale. I ended up with ducks that were over 500 lines. Another idea that didn’t work for me is grouping code by domain. I had trouble figuring out where to put code that joins together multiple domains.
Here’s the structure we’ve ended up with:
components
reducers
actions
selectors
common
redux/configureStore
components
Our state tree looks kind of like this. Everything is either ui or data.
{
data: {
orders: {},
products: {},
...a bunch of normalized stuff keyed by id,
}
ui: {
currentPage: ‘ORDER_LIST’,
currentDropdown: null,
orderListPage: {
visibleOrderIDs: [],
},
}
Navigating redux code is tough. To add a single feature, I have to add:
- reducer
- action
- component
- sometimes a selector
Some Atom plugins that help are:
- https://github.com/AsaAyers/js-hyperclick lets me cmd-click to jump to where functions/variables/etc. are defined. It even works for code in your node_modules directory.
- https://atom.io/packages/nice-index renames tabs from index.js to their directory. So, opening up actions/orderListPage/index.js shows /orderListPage instead of index.js. This doesn’t help if you have another file called selectors/orderListPage/index.js.
Colocate reducers and selectors. I don’t use this yet in my work code, but I really want to. I have a giant selectors directory that sometimes mimics the shape of my store and sometimes mimics the shape of my components. Because it isn’t consistent, I’ve had trouble finding my selectors.
Don’t use a boilerplate. A lot of people I talk to who are new to React/Redux think they need to find a good boilerplate to get started. I think thats a bad idea. Every time I use somebody else’s boilerplate, I would spend hours trying to get it to work. Once I did eventually get it to work, I wouldn’t be able to update my packages or add a new feature to my build process.
If you’re not sure how to get started with React, don’t be tempted to copy paste somebody else’s boilerplate. Instead spend the time learning about how people use React: https://github.com/petehunt/react-howto.
If the Redux docs don’t have the answers/examples you need, check out https://github.com/markerikson/react-redux-links.
Update 8/2/16: And now there is https://github.com/facebookincubator/create-react-app.
Use Twitter and YouTube to stay up to date. Follow https://twitter.com/dan_abramov and https://twitter.com/acemarke. Watch videos from React.js Conf 2016 and React Europe.
I hope this is useful if you’re new to React/Redux. I probably have a bunch of mistakes in this post, so let me know.
Written by Luqmaan Dawoodjee. github.com/luqmaan
Reviewed by Dustin Moore. github.com/dustinmoorenet