ReactT2_1200x303

What exactly is Render in React, how can we force a class or functional component to re-render, and can it be done without calling setState?

The short answer to the question of if you can force a React component to render (and without calling setState) is yes, you can. However, before we get to know how, let’s clear up a few important things first.

From the early days of React, developers worried about unnecessary re-renders of components and tried to optimize them. I can already tell you that premature optimization is not the best idea because React is very fast, and more often the problem is in the way the code is written. Therefore, worry about it when there really is a visible problem. The fact that a component did re-render doesn’t necessary mean that DOM was actually modified. If that is a surprise to you, then bear with me.

Let’s start with an explanation of what exactly happens when we update state in React.

What is Render in React?

React takes over manipulation of DOM with use of React.createElement function so that we don’t have to do it manually. Instead, updates are done only when needed. We only describe how we want the DOM to look with JSX or pure createElement function, and React creates a virtual representation of DOM. Then, based on it, the real DOM will be updated whenever there are differences after state changed. What’s more, if there are many DOM updates scheduled, React can batch them for efficiency. Nevertheless, this whole process consists of three stages: Render, Reconciliation, and Commit.

Render – React calls the render function to gather output from createElement functions
Reconciliation – New elements are compared against previously given elements and the virtual DOM is updated if there are differences
Commit – The real DOM is updated

Like I mentioned before, changing the state does not mean that the commit phase will be executed, as there will be no need for it if there were no changes in the virtual DOM. As you can see on the example below, no matter how many times we click the button, the name property is set to the same value, despite the fact that we call the setState method.

// A simple example
class App extends Components {	
	state = {
	name: ‘Thomas’
}

	onClickHandler = () => {
		this.setState({name: ‘Thomas’})
}
	
	render() {
		<div>
			<p>My name is {this.state.name}</p><br/>
			<button onClick={this.onClickHandler}>Click me</button>
		</div>
	}
}

If you put a console log in the render function, you will see that it will be called. However, if you check the DOM in inspector, you won’t see a flash that indicates a change in DOM. Now, let’s talk about how we can trigger a re-render.

Forcing Re-render of a Component in React

If you are using a React class component then it is as easy as using this.forceUpdate() function.

class App extends Components {	
	onClickHandler = () => {
		this.forceUpdate()
}
	
	render() {
		<button onClick={this.onClickHandler}>Click me</button>
	}
}

Just make sure that the this context refers to the component instance. In the example below, this refers to the scope of innerFunction and not of the React component instance, and because of that it won’t work.

// This won’t work
class App extends Components {	
	onClickHandler = () => {
		function innerFunction() {
			this.forceUpdate()
		}
		innerFunction()
}
	
	render() {
		<button onClick={this.onClickHandler}>Click me</button>
	}
}

Now you know how easy it is, but be aware that in 99.99% of cases you should not need it. If you do, then you might be doing something wrong, and probably there is a better solution to what you are trying to achieve. The benefit of the forceUpdate function over setState is the fact that it will update a component even if the shouldComponentUpdate lifecycle hook is implemented.

If you are updating state values, but they are not rendered correctly, then instead of providing a new value, you might be directly mutating current state. There is also a possibility that you are passing the same reference. Remember that when updating state, you should always provide a new value. For example, strings are immutable; however, objects and arrays are passed as a reference, so:

// Equality check is done by checking if values are the same
const str1 = ‘hello’
const str2 = ‘hello’

str1 == str2	// true

// Equality check is performed by checking if values have the same reference
const obj1 = {str: ‘hello’}
const obj2 = {str: ‘hello’}
const obj3 = obj1
ob1 == obj2	// false
obj3 == obj1	// true

Forcing a Re-render in a Functional Component

In a function component there is no forceUpdate method. However, we can mimic this functionality with the code below.

import React, {useState} from ‘react’

const App = props => {
const [count, setCount] = useState(0)
const onClickHandler = e = => {
	setCount(prevCount => prevCount + 1)
}

return (
	<button onClick={onClickHandler}>Click me</button>
)
}

As you can see, whenever we need the component to be re-rendered, we just increment the counter. To be honest, we can go even further than that and create a custom hook for it.

import React, {useState} from ‘react’

const useForceUpdate = () => {
	const [count, setCount] = useState(0)
	
	const increment = () => setCount(prevCount => prevCount + 1)
	return [increment, count]
}

const App = props => {
const [forceUpdate] = useForceUpdate()

const onClickHandler = e => {
	forceUpdate()
}

return (
	<button onClick={onClickHandler}>Click me</button>
)
}

Now you have seen how to force re-render of a component. If for any reason you would like to re-render a child component from a parent, then you can do it by changing its prop as shown below.

const ChildComponent = props => {
	return (
		// child markup
)
}

const App = props => {	
const [forceUpdate, forceUpdateValue] = useForceUpdate()

const onClickHandler = e => {
	forceUpdate()
}

return (
	<div>
		<ChildComponent key={forceUpdateValue} />
		<button onClick={onClickHandler}>Click me</button>
	</div>
)
}

In this article we covered what render is in React, what happens when state is updated, and how to force a re-render in class and functional components. For the final note, remember, if you ever think that you need to force a re-render, think again as there might be a better way.


Thomas Findlay
About the Author

Thomas Findlay

Thomas Findlay is a web and mobile developer, mentor, technical writer and consultant with almost six years of experience. He specializes in frontend web technologies including Vue.js, React.js, React Native for mobile applications, along with backend: PHP, Laravel, Python, Flask, Node.js and Express.js. Thomas has designed and developed websites and mobile applications for individuals and small and large businesses. Learn more about Thomas at https://www.codementor.io/thomas478

Related Posts

Comments

Comments are disabled in preview mode.