If you have worked with React or scaffolded a React application from you would have come across this statement most likely in the App.jsx file:
root.render(
<StrictMode>
<App />
</StrictMode>
)
This is the configuration required to enable StrictMode in react. But what is it? And why do we need it?
What does StrictMode do?
StrictMode is a very simple and efficient idea in React to discover potential bugs during development. It does 3 major functions:
- renders every component twice in development mode.
- run effects twice in development mode.
- checks deprecated APIs
But why?
Component behaviour in strict mode
To understand how rendering a component twice will help us discover bugs, we must first understand the philosophy behind react components.
React emphasizes every component in the tree to be pure functions. There are some inherent assumptions in the reconciliation (committing to DOM) logic that every function components are pure.
But what are pure functions?
The idea is simple. A function is pure if,
- it is deterministic (it returns the same output for the same input every time.)
- has no side effects (it does not modify or depend on values outside its scope.)
Let’s take an example:
function areaOfCircle(radius) {
return Math.PI * Math.pow(radius, 2)
}
The above function takes the radius of a circle and calculates and returns its area. This function is a simple example of a pure function.
If you apply the above rule “Same input should return same output” to the above function, it applies to it.
Let’s take another example:
let powerFactor = 2
function areaOfCircle(radius) {
return Math.PI * Math.pow(radius, powerFactor)
}
let powerFactor = 2
function areaOfCircle(radius) {
const area = Math.PI * Math.pow(radius, powerFactor)
powerFactor = 3
return area
}
The above two are impure functions (Although they are not the best examples 🥲). It modifies/depends on a variable outside its scope and the function itself is indeterministic now. It depends on powerFactor variable and if the variable’s value changes, the result of areaOfCircle also changes.
This approach is bad because it can introduce unexpected bugs because of dependant values changing.
But if you think about it, by creating only pure functions, we cannot do so much in programming. After all if you are developing something, you want it to modify some state, somehow, somewhere at some point in time.
A side effect could be anything like:
- Modifying data
- fetch requests
- DOM manipulation
- etc.
Back to strict mode
Now that we understand pure functions, let’s come back to React and StrictMode configuration. As I said earlier, React assumes each functional component is pure. Thus StrictMode renders the same component twice to check if we modify any outside variables and generate a different state for the component the 2nd time.
Consider the example below:
function TodoItems({ todos }) {
todos.push({ id: 4, value: 'buy food' })
return (
<ul>
{todos.map((x) => (
<li key={x.id}>{x.value}</li>
))}
</ul>
)
}
The above function takes a todo list and renders it. But it also pushes a entry to the todo prop during render.
The above code may look correct but it is not. Without strict mode, the 4th entry will be pushed every time the component re-renders. Let’s add a state variable for example,
function TodoItems({ todos }) {
const [isHover, setIsHover] = useState(false)
todos.push({ id: 4, value: 'buy food' })
return (
<ul onPointerEnter={() => setIsHover(true)} onPointerLeave={() => setIsHover(false)}>
{todos.map((x) => (
<li key={x.id}>{x.value}</li>
))}
</ul>
)
}
If you intended that one todo will be pushed when the list is hovered, its not the case. Every time you push, multiple todo will be pushed. This is because the function modified a prop which is an external variable. This also could potentially affect other components as well.
This bug is not obvious the first time, but only with multiple presses. That is where StrictMode helps us. By rendering twice, it brings out hidden bugs like this.
Moreover, modifying prop directly is never a good idea in itself.
useEffect behaviour in strict mode:
StrictMode also modifies the useEffect hook to run an extra time to identify unexpected side effects like the above example.
The problem is evident when you modify an external datasource. It might be triggered multiple times. Here is a simple example:
function BookReader({ book }) {
useEffect(() => {
service.setStartReadingTime()
}, [])
return (
<div>
<h1>{book.title}</h1>
<p>{book.content}</p>
</div>
)
}
Let’s say we track the time a user starts to read a book. This example might look good but it is missing the cleanup. As a result, every time we change the book prop from the parent, the component re-renders causing to restart the tracker in the reading time service.
This can be detected by strict mode without changing the prop itself, because the component will be rendered twice and it makes the bug obvious.
function BookReader({ book }) {
useEffect(() => {
service.setStartReadingTime()
return () => {
service.setEndReadingTime()
}
}, [])
return (
<div>
<h1>{book.title}</h1>
<p>{book.content}</p>
</div>
)
}
The fix is to add the cleanup logic of setting the endTime.
deprecated APIs
StrictMode also helps in detecting usage of deprecated APIs like:
[findDOMNode](https://beta.reactjs.org/reference/react-dom/findDOMNode)
UNSAFE_
class lifecycle methods like[UNSAFE_componentWillMount](https://beta.reactjs.org/reference/react/Component#unsafe_componentwillmount)
- Legacy context (
[childContextTypes](https://beta.reactjs.org/reference/react/Component#static-childcontexttypes)
,[contextTypes](https://beta.reactjs.org/reference/react/Component#static-contexttypes)
, and[getChildContext](https://beta.reactjs.org/reference/react/Component#getchildcontext)
) - Legacy string refs (
[this.refs](https://beta.reactjs.org/reference/react/Component#refs)
)
The usage of these APIs are rare, but StrictMode still detects them.
Conclusion:
To summarize, StrictMode does the following:
- renders every component twice in development mode.
- run effects twice in development mode.
- checks deprecated APIs
It does so to detect any unwanted behaviour in the code during development phase itself.
References: