Understanding ReactJS re-renders and memoization
Tags: webdev, react, beginners, tutorial
Published: 2022-12-27 13:51:29


Performance is a core factor in any successful web apps, Users won't bother to wait over 2 seconds for websites to finish loading, and because of that you -the web developer- should make sure that your websites doesn't exceed that.

In this tutorial, We're going to take a look at ReactJS rendering cycle, how does it work, what trigger a render, how do we optimize it using methods such as: `useMemo` and `useCallback`, and when we should (or shouldn't) use them.

## Table of Contents

- [What is rendering](#what-is-rendering)
- [The ReactJS rendering cycle](#the-reactjs-rendering-cycle)
    + [When initializing the component](#when-initializing-the-component)
    + [When re-rendering a component](#when-re-rendering-a-component)
- [What triggers a re-render?](#what-triggers-a-re-render-)
    + [The component state has changed](#the-component-state-has-changed)
    + [The component parent is re-rendered](#the-component-parent-is-re-rendered)
    + [Context value has changed](#context-value-has-changed)
    + [What about props change?](#what-about-props-change)
- [Memoization techniques](#memoization-techniques)
    + [`memo`](#-memo-)
    + [Composition](#composition)
    + [Using component's props](#using-component-s-props)
    + [`useMemo`](#-usememo-)
    + [`useCallback`](#-usecallback-)
- [You're trading time with memory!](#you-re-trading-time-with-memory-)
- [Further Reading](#further-reading)
- [Conclusion](#conclusion)


## What is rendering 

Simply means that ReactJS draws the shape (UI) of a component based on its states and props. It does that by converting the component code into html elements and inject it into the DOM.

## The ReactJS rendering cycle


When it comes to converting a component code into html elements ReactJS split this process into two steps: the render step and the commit step. however this process behavior is slightly different depending on if the components are initialized or re-rendered

### When initializing the component

- **the render step**
	Here React will traverse the DOM from the root element to all it's descendants, while traversing React will convert JSX into a React Element using the [createElement()](https://reactjs.org/docs/react-api.html#createelement) method 

- **the commit step**
	here all React Elements are applied to the DOM


### When re-rendering a component

React will traverse the DOM in search of elements that are flagged for update. once finished it will proceed to the render step 

- **the render step**
	Similarly to initializing the component, React will convert JSX into a React Element for each element flagged for update using the [createElement()](https://reactjs.org/docs/react-api.html#createelement) method. Then they'll be compared to the their last render creating a list of all changes that needs to be made in the DOM

- **the commit step**
	Finally all elements that are different from themselves in the last render will be applied to the DOM, Otherwise React will discard the operation

sometimes the rendering step can be slow (ex: component is too big), which causes performance hits, therefore if we need to eliminate those performance hits we should remove unnecessary re-renders. Next we'll discuss what triggers a re-render.

## What triggers a re-render?

There are 3 main factors that cause a re-render

### The component state has changed 

You've probably knows this, Changing the state of a component (either using `useState` or `useReducer`) will cause a re-render so that the website UI match the component, Here is a basic example 

```javascript
import {useState, useEffect} from "react"

function APP() {
	const [count, setCount] = useState(0)

	useEffect(() => console.log('new render'), [])

	return <button onClick={() => setState(count + 1)}>click me</button>
}
```

each time you click on the button will fire a console log implying a re-render.

Speaking of state hooks, You may find this interesting:
{% embed https://dev.to/m0nm/usestate-vs-usereducer-what-are-they-and-when-to-use-them-2c5c %}


#### The component parent is re-rendered

If the parent component triggered a re-render (either by these 4 factors) the component will also re-render, Example

```javascript

function Parent() {
	const [count, setCount] = useState(0)

	return (
		<div>
			<button onClick={() => setState(count + 1)}>click me</button>

			<Child />
		</div>
	)
}

function Child() {
	useEffect(() => {
		console.log("I'm being re-rendered because of my parent 😠")
	}, [])

	return <h1>I'm a child component</h1>
}
```

each time you click on the button it will fire a console log with the message `I'm being re-rendered because of my parent 😠`, implying a re-render of the child component.


#### Context value has changed 
if the component is consuming context provider value then any change in the value will re-render the component

```javascript
import {useState,createContext, useContext} from "react"
	
const CountContext = createContext(null)

function App() {
	const [count, setCount] = useState(1);

	const onClick = () => {
		setCount(prev => prev + 1);
	};

	<CountContext.Provider value={count}>
		<Component />
	</CountContext.Provider>
}

function Component() {
	const value = useContext(CountContext)
	console.log("re-render")
	return <h1>count is: {value}</h1>
}

```

#### What about props change?

Props are passed from parent to child, In most cases props are a state in the parent component, When props changes that means the parent's state has changed which causes the parent to re-render, which means the child will re-render as well as stated above


## Memoization techniques

Now we're going to explore `memo`, `useMemo` and `useCallback` , how they work and how to use them

### `memo`

when we have a `Parent` component and  `Child` component with it's props. We know that when the parent component re-renders then all of it's descendants  will re-render as well, Even if their props hasn't changed. The reason is that React compares the old props  with the new props which do not reference the same object, therefore React will re-render the child component.

If it happens to be a problem to you, You can use React [higher order component](https://reactjs.org/docs/higher-order-components.html) `memo` to memoize the `Child` component:

```javascript
// ./Child.jsx
import {memo} from "react"

function Child({name}) {
	console.log("not gonna re-render when my parent do")
	return <h1>My memoized name is: {name}</h1>
}

export default memo(Child)

// ./Parent.jsx
function Parent() {
	const [count, setCount] = useState(1);
	const name = "john"

	return(
		<button onClick={() => setState(count + 1)}>click me</button>
		<Child name={name} />
	)
}
```

### Composition

Another trick you could use without relying on `memo` is composition, [more about it by Kent Dodds](https://kentcdodds.com/blog/optimize-react-re-renders)
```javascript

function Child({name}) {
	console.log("not gonna re-render when my parent do")
	return <h1>My memoized name is: {name}</h1>
}

// ./Parent.jsx
function Parent({children}) {
	const [count, setCount] = useState(1);

	return(
		<button onClick={() => setState(count + 1)}>click me</button>
		{children}
	)
}

function App() {
	const name = "john"
	
	return (
		<Parent>
			<Child name={name} />
		</Parent>
	)
}
```

### Using component's props

You can achieve the same by passing the `Child` as a prop to `Parent`

```javascript
function Child({name}) {
	console.log("not gonna re-render when my parent do")
	return <h1>My memoized name is: {name}</h1>
}

// ./Parent.jsx
function Parent({child}) {
	const [count, setCount] = useState(1);

	return(
		<button onClick={() => setState(count + 1)}>click me</button>
		{child}
	)
}

function App() {
	const name = "john"
	
	return (
		<Parent child={<Child name={name} />} />
	)
}
```

### `useMemo`

When React re-renders every function within the component will be re-created again, this could cause performance issues if the function is expensive. In order to fix that `useMemo` allows you to memoize expensive functions so that you can avoid calling them on every render.

You can see below we have `expensiveFunction` that is slow, by passing it to `useMemo` we make sure that it's not called again unless we change the `bool` state, so that when we click on "increment count" button there won't be any lag:

```javascript
import {useState, useMemo} from "react"
function App() {
	const [count, setCount] = useState(1);
	const [bool, setBool] = useState(false);
	
	const expensiveFunction = useMemo(() => {
		for(let i = 0; i < 100000000; i++) {}
		// do something
		return bool
	} , [bool])

	return (
		// increment count
		<button onClick={() => setCount(count + 1)}>increment count</button>

		// re render to use expensiveFunction
		<button onClick={() => setBool(!bool)}>re-render</button>
	)
}
```

### `useCallback`

The `useCallback` and `useMemo` Hooks are similar. The main difference is that `useMemo` returns a memoized _value_ and `useCallback` returns a memoized _function_.

```javascript
import {useState, useCallback} from "react"
function App() {
	const [count, setCount] = useState(1);
	const [bool, setBool] = useState(false);
	
	const expensiveFunction = useCallback(() => {
		for(let i = 0; i < 100000000; i++) {}
		// do something
	} , [bool])

	return (
		// increment count
		<button onClick={() => setCount(count + 1)}>increment count</button>

		// re render to use expensiveFunction
		<button onClick={() => setBool(!bool)}>re-render</button>
	)
}
```

## You're trading time with memory!

Now this doesn't mean to use these memoizing hooks in every piece of code of your app, Know that what you're doing is **trading time with memory**. Too much memory usage is just going to make it worse.

**So when should you use them?**

Only when you notice that your app is slow when re-rendering, Otherwise don't touch the code: "If it's not broken don't fix it"


## Further Reading 

If you want to go in depth about this topic, I recommend you read Mark Erikson post:
[A (Mostly) Complete Guide to React Rendering Behavior](https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-mostly-complete-guide-to-react-rendering-behavior/#rules-of-react-rendering)


## Conclusion 

We've reached the end of this post, I hope it was helpful to you, If it was then please consider liking this post. If you have any questions just hit me up in the comment section, I'm not an expert in this subject but I'll try my best.