I gave one of the talks on React Helsinki meetup on 8th of November 2017. The title of the talk wasn't very imaginative, but it was quite descriptive: "Lessons learned from three React + Redux projects." These lessons were learned the hard-way aka doing mistakes. If someone in the audience could avoid the some of the pitfalls money and time would be saved. It is not only my mistakes and my learnings, but React ecosystem has changed some of the best practices over the years. In this blog post, I'll summarize some of the ideas shared in the talk.
Overview of the meetup and my talk
There were three talks:
- Aki Lehtinen, "styled-components: Style your React.js apps without stress"
- Harri Sarsa, "React Native goes AppGyver"
- mine, "Lessons learned from three React + Redux projects"
The slides of my talk can be found here.
It was tough to pick which topics to cover as people have different experience levels and also they might be solving a different kind of problems than what I have experienced, but hopefully, everyone got at least something.
I divided the talk into sections called Components and Store.
What is the right size for a component?
When writing React or any other UI components from scratch, it might be difficult to decide what is the right size for a component. By component size, in this context, I mean granularity of the UI elements.
Mark Seemann's SOLID course had a chapter where he explained why smaller interfaces lead to better results.
To visualize why smaller (fine-grained) interfaces lead to a better outcome, Mark used Duplo and Lego bricks as an example. If you need to build a dragon using Duplo bricks, the result is not as optimal as with high-grained Lego bricks.
I used the same analogy but in context of React components. If you have a target outcome by designer, you can think this as the dragon in the Lego example. By using fine-grained components, you can achieve a better outcome than using huge building blocks (Duplo).
Another way to think about the React components is the single responsibility principle which states that ""A class should have only one reason to change." In the sentence, Class can be replaced with component.
Which component type should I use?
I have had good experiences when having only two types of components: classic components and stateless functional components. In addition to legacy types like React.createClass, the only thing missing from my list is PureComponent.
Classic component I use when:
- React life-cycle events are needed, for example componentDidMount
- I need component state
Stateless functional components I use for the rest.
What I normally do is that I write the page 95% ready and then start to optimize the page. Stateless function components (SFP) will render on any props change, and sometimes this is not an optimal situation. As the SFP doesn't have life-cycle events like shouldComponentUpdate, I use a library called recompose to provide a Higher-order Component that allows writing different "rules" for updates.
const HyperOptimizedComponent = onlyUpdateForKeys(['propA', 'propB'])(ExpensiveComponent)
How to evaluate 3rd party components
I made a short checklist what the library should allow you to do:
- pass your own components
- style components easily
- has lots of extension points
- type definitions
Make it flat as possible
One of the big mistakes I made during my first React app was that I didn't change the data I received from AJAX call to a flat data structure.
One example of this was a view that had a complex tree control with unlimited depth. I used a legacy API that provided data that matched very well what I needed to render; it had branches and leafs. I stored this tree structure in my store and started build up the UI tree.
Everything was good until I had to dispatch actions that affected one of the leafs many levels down in the tree, for example selecting a leaf or expanding a branch. I sent an event that had an ID of the element that was selected.
The problems start to occur when you need to write a reducer to a deep data structure.
How do I find an item with particular id if it can be in any branch and any depth?
I could pass all the parents as an array, but it makes the components and actions much more complicated.
The better approach is to have a flat list of items and then to search by ID is much faster and easier to implement.
Note: one could also use object and ID would be a key.
I then would create an unflatten function that would create a tree structure on the fly. Redux documentation has a chapter called Computing Derived Data that is very helpful.
Store is good place to start adding (TypeScript) types
If you start using TypeScript or Flow, then Redux store is an excellent place to start. Make the application state typed, add types to the reducer functions and proceed from there to component props.
I also mentioned about TypeScript FSA library that allows you to write typed actions from dispatching to receiving.