As a React developer, can you answer these two questions:

  1. For the following function components:
function Child() {
  useEffect(() = > {
    console.log('child');
  }, [])

  return <p>hello</p>;
}

function Parent() {
  useEffect(() = > {
    console.log('parent');
  }, [])

  return <Child/>;
}

function App() {
  useEffect(() = > {
    console.log('app');
  }, [])

  return <Parent/>;
}
Copy the code

What is the print order of the console when rendering
?

  1. Are the following two callbacks called at the same time?
// componentDidMount Life cycle hook
class App extends React.Component {
  componentDidMount() {
    console.log('hello'); }}UseEffect dependent on []
useEffect(() = > {
  console.log('hello');
}, [])
Copy the code

The answer:

👉 Swipe right to see the answers1. child -> parent -> app
                                                                2.differentCopy the code

In fact, the two questions respectively examine:

  • useEffectOrder of execution
  • useEffectHow to interveneReactThe working process

The rest of this article takes you through the source code.

That’s what the useEffect is all about.

UseEffect Execution order

The React source code can be split into three pieces:

  • Scheduler: Schedules updates
  • Coordinator: Decides what to update
  • Renderer: Renders updated content to the view

Only the renderer performs the render view operation.

For the browser environment, only the renderer performs DOM operations like appendChild and insertBefore.

How does the coordinator decide what to update?

The answer is: he flags the fiber (aka virtual DOM) that needs to be updated.

These labeled fibers form a linked effectList.

The renderer iterates through the effectList to perform the actions associated with the tag.

  • For example, Placement tags correspond to DOM insertion

  • For example, the Update tag corresponds to updating a DOM attribute

UseEffect works the same way:

  1. When an update is triggered, the FunctionComponent is executed and useEffect is executed to see if its second parameter, deps, has changed.

  2. If the DEPS changes, the fiber corresponding to the FunctionComponent is marked as Passive (useEffect is required).

  3. In the renderer, when the fiber is traversed through the effectList and Passive flag is found, useEffect destroy (the return value of the useEffect callback) and useEffect create (the useEffect callback) are executed in sequence.

The first two steps take place in the coordinator.

Therefore, the order in which effectLists are built is the order in which Useeffects are executed.

effectList

The coordinator’s workflow is a recursion implemented using traversal. So there are two stages, recursive and recursive.

We know that recursion goes from the root all the way down to the leaf, and recursion goes from the leaf all the way up to the root.

Construction of effectLists occurs in the attribution phase. So, the order of the effectList goes up from the leaf node.

UseEffect corresponds to Fiber as a node in the effectList, and its call logic also follows the flow of return.

Now, we know enough to answer the first question:

Since the attribution stage is from Child to Parent to App, the corresponding effectList is in the same order.

So the useEffect callback is executed in the same order.

Do not use the analogy of a lifecycle hook

When we first learn hooks, we use the ClassComponent lifecycle hook as an analogy for the timing of hook execution.

Even the website teaches it that way.

However, we already know from the above that React is implemented as follows:

Scheduling -- coordinating -- renderingCopy the code

Rendering works according to:

Construct effectList - Iterates through effectLists to perform actionsCopy the code

The whole process has nothing to do with lifecycle hooks.

In fact, lifecycle hooks are simply hook functions attached to the process.

So, a better way to understand the useEffect timing is to run the React process.

Apply colours to a drawing

As per the flow, the effectList is processed in the renderer.

For useEffect, when iterating through the effectList, we will find all the fibers that contain the Passive flag.

Execute destroy corresponding to useEffect.

After all destroy is executed, all create is executed.

The whole process is performed asynchronously after the page has been rendered.

Answer the second question:

If useEffect dePS is [], fiber will only be marked Passive when it is mounted because dePS will not change.

This is similar to componentDidMount.

However, handling Passive effect is executed asynchronously after rendering, whereas componentDidMount is executed synchronously after rendering, so they are different.

UseEffect and useLayoutEffect

More similar to componentDidMount is useLayoutEffect, which executes synchronously after rendering is complete.

Here is an online Demo. You can replace useLayoutEffect with useEffect to see the difference.

conclusion

Through this article, we learned the complete execution process of useEffect.

The rest of this series of articles will continue to look at the React feature that is often used in businesses, using examples and source code.

Learn React source code easily