When building complex dynamic components using React.js, without a bit of awareness, it's very easy to bump into performance issues. I did quite a lot of work recently with React forms, with dynamic form validation based on the country. Due to the nature of the components architecture, the form would regularly throw a maximum update depth exception when users started entering data into the forms. In this tutorial, I will cover some of the performance issues I had to figure out, so, hopefully, you can avoid the same pain as I did.
Limit The Props You Pass Into Your Components
If you read any good book on how to level-up your coding abilities, when it comes to method/function design, a software engineering 101 principle is to only pass the bare-minimum amount of data your function needs to work. Reasons for this is, it makes unit testing easier, it reduces the chance of bugs occurring, it makes your code easier to read and to maintain.
Adhering to this practice when passing proper into your React render method, is even more important due to how React works. In a React component, when you make any changes to the state, React will automatically re-call render() to update the component. Used wisely, this makes your webpage load a lot quicker. Used without though, it can create performance issues. You can think of a React component, like a state machine if you are up-to-speed with your design patterns.
Another performance issue I experienced occurred when a user- enters data into a textbox. On each keystroke, the state of the form would get re-triggered. This caused re-render to run. In my situation, my component contains the complete form and all its form elements. A lot of unneeded data was passed into the component, data on countries, currencies etc... the component was being forced to do a lot of unneeded calculations each time a user types in a character into the form. The obvious way to speed up the form was to limit the data being passed into the form.
One way you might be able to apply this principle in your code is to consider if you can move any logic out of your component and put it somewhere else, like within a parent component, or, maybe a redux reducer. In my example, moving the logic from my component into a reducer meant I reduce the number fewer of props. This reduced the number of re-render calls.
Can Your Component Be Broken Into Smaller Components?
Ensuring my component only had the minimum number of props passed into it was a good first step, unfortunately, this did not solve my app crashing and this didn't fully resolve my performance issues. Another good practice to adopt when you're building React.js components is to follow a more function programming mindset and write lots of small, well-defined components that do one thing well. Breaking components into as small chunks as possible helps your code in several ways:
- Smaller components that only do one thing well are more declarative and easier to understand by other developers
- Improve reuse. The larger you build your components, the less likely that they will be able to be used in other places. If you make a habit of writing small components and combine that with storybook you can get some good reuse from your code base
- Usually, the simpler a component the easier it is to write a test for it.
- Performance, when React calls a component render() method. It might not re-render(0 child components if it's state/props haven't changed
Within my example, as I had the whole of the form and all its elements in one component, when render was called React was re-rendering and re-calculating more than it needed to. If the user was filling in the first name of the form and a render() was called by the validation, the whole form was being rendered. Splitting the form into a Form component, a CountryFormElement, TitleFormElement meant the main forms render() method had less work to do. Splitting all the form elements into separate components, meant that when the user started typing in the first name component, only that component needed to be re-rendered, rather than the whole form.
Do Not Mutate Your State Data Within Your Render Method
Another React 101 - which is another functional programming recommendation - is that within your render method, never mutate the state of your component's properties. If you need to modify your props, your life will be a lot more enjoyable if instead of manipulating an array directly, you clone it somehow. If you want to change state its better to deal with it in a more suitable place, like component mounted() method.
If you are struggling to grasp why not modifying your state and props is important, then imagine you have an array in you write some simple enough code, like array.splice(). This changes the original array, which causes React to call render. If you have to modify an array, you are better off using something like map, filter or reduce.
Be careful of functions inside the render methods which can have unexpected side-effects. It is common to see functions bound to the context of the component inside the render function to handle events of child components, like:
Both these approaches to passing functions to children can create performance issues, as they can call child components to unnecessarily render multiple times during the lifetime of the application.