Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does React.useState triggers re-render?

import { useState } from 'react';  function Example() {   const [count, setCount] = useState(0);    return (     <div>       <p>You clicked {count} times</p>       <button onClick={() => setCount(count + 1)}>         Click me       </button>     </div>   ); } 

In the above example whenever setCount(count + 1) is invoked a re-render happens. I am curious to learn the flow.

I tried looking into the source code. I could not find any reference of useState or other hooks at github.com/facebook/react.

I installed react@next via npm i react@next and found the following at node_modules/react/cjs/react.development.js

function useState(initialState) {   var dispatcher = resolveDispatcher();   return dispatcher.useState(initialState); } 

On tracing back for dispatcher.useState(), I could only find the following ...

function resolveDispatcher() {   var dispatcher = ReactCurrentOwner.currentDispatcher;   !(dispatcher !== null) ? invariant(false, 'Hooks can only be called inside the body of a function component.') : void 0;   return dispatcher; } 
var ReactCurrentOwner = {   /**    * @internal    * @type {ReactComponent}    */   current: null,   currentDispatcher: null }; 

I wonder where can I find dispatcher.useState() implementation and learn how it triggers re-render when setState setCount is invoked.

Any pointer would be helpful.

Thanks!

like image 683
Sarbbottam Avatar asked Oct 27 '18 17:10

Sarbbottam


People also ask

How does React know when to re-render?

React components automatically re-render whenever there is a change in their state or props. A simple update of the state, from anywhere in the code, causes all the User Interface (UI) elements to be re-rendered automatically.

Does React Rerender when useState changes?

Whenever a React component uses any state via useState , it will need to be rendered again, or re-rendered, in response to any state change. If a component does not re-render whenever state is updated, it will not be able to display any state updates to the user.

How does React hooks re renders a function component?

As we already saw before, React re-renders a component when you call the setState function to change the state (or the provided function from the useState hook in function components). As a result, the child components only update when the parent component's state changes with one of those functions.

Does setState always re-render?

setState() will always lead to a re-render unless shouldComponentUpdate() returns false . To avoid unnecessary renders, calling setState() only when the new state differs from the previous state makes sense and can avoid calling setState() in an infinite loop within certain lifecycle methods like componentDidUpdate .


1 Answers

The key in understanding this is the following paragraph from the Hooks FAQ

How does React associate Hook calls with components?

React keeps track of the currently rendering component. Thanks to the Rules of Hooks, we know that Hooks are only called from React components (or custom Hooks — which are also only called from React components).

There is an internal list of “memory cells” associated with each component. They’re just JavaScript objects where we can put some data. When you call a Hook like useState(), it reads the current cell (or initializes it during the first render), and then moves the pointer to the next one. This is how multiple useState() calls each get independent local state.

(This also explains the Rules of Hooks. Hooks need to be called unconditionally in the same order, otherwise the association of memory cell and hook is messed up.)

Let's walk through your counter example, and see what happens. For simplicity I will refer to the compiled development React source code and React DOM source code, both version 16.13.1.

The example starts when the component mounts and useState() (defined on line 1581) is called for the first time.

function useState(initialState) {   var dispatcher = resolveDispatcher();   return dispatcher.useState(initialState); } 

As you have noticed, this calls resolveDispatcher() (defined on line 1546). The dispatcher refers internally to the component that's currently being rendered. Within a component you can (if you dare to get fired), have a look at the dispatcher, e.g. via

console.log(React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher.current) 

If you apply this in case of the counter example, you will notice that the dispatcher.useState() refers to the react-dom code. When the component is first mounted, useState refers to the one defined on line 15986 which calls mountState(). Upon re-rendering, the dispatcher has changed and the function useState() on line 16077 is triggered, which calls updateState(). Both methods, mountState() on line 15352 and updateState() on line 15371, return the count, setCount pair.

Tracing ReactCurrentDispatcher gets quite messy. However, the fact of its existence is already enough to understand how the re-rendering happens. The magic happens behind the scene. As the FAQ states, React keeps track of the currently rendered component. This means, useState() knows which component it is attached to, how to find the state information and how to trigger the re-rendering.

like image 110
sauerburger Avatar answered Oct 08 '22 10:10

sauerburger