Probe into six weird phenomena of react
Front end GOGO 2021-06-04 11:00:04


Today we have an unusual React Advanced articles , In this paper, we will discuss some Unusual The phenomenon , Analyze the reasons with the process of investigation , Find the result , So as to know React, Walk into React The world of , uncover React The veil of , I'm convinced that the , A deeper understanding of , In order to better use .

I admit that the name may be a bit of a headline party , It's inspired by a CCTV program called 《 Into Science 》 Column of , Introduce all kinds of supernatural phenomena every day , It's amazing , At the end of the day, it turned out to be all kinds of pediatric problems , Now I think it's funny . But what I'm introducing today React ' Psychic ' The essence of phenomenon is not pediatrics , Every phenomenon reveals React Operating mechanism and Design principle .( We're talking about react The version is 16.13.1


well , I don't say much nonsense , My great detectives ,are you ready ? Let's start today's journey of revealing secrets .

Case 1 : The component is repeatedly mounted for no reason

Received a report

A former classmate came across a strange situation , He wants to update the components ,componentDidUpdate Do what you want to do after execution , Component updates come from the parent component passing props Changes . But the parent component changes props Find view rendering , however componentDidUpdate No implementation , What's more weird is componentDidMount perform . The code is as follows :

// TODO:  Repeat mount 
class Index extends React.Component{
     console.log(' Component initialization mount ')
     console.log(' Component update ')
     /*  Want to do something  */
      return <div>《React Advanced practice guide 》   { this.props.number } +   </div>

The effect is as follows


componentDidUpdate No implementation ,componentDidMount perform , Explain that the component is basically No update logic , It is Let's go. Repeat the mount .

Check one by one

The subassemblies are confused , No reason at all , We have to start with the parent component . Let's see how the parent component is written .

const BoxStyle = ({ children })=><div className='card' >{ children }</div>

export default function Home(){
   const [ number , setNumber ] = useState(0)
   const NewIndex = () => <BoxStyle><Index number={number}  /></BoxStyle>
   return <div>
      <NewIndex  />
      <button onClick={ ()=>setNumber(number+1) } > give the thumbs-up </button>


Some clues are found in the parent component . In parent component , First, through BoxStyle As a container component , Add the style , Render our subcomponents Index, But each time a new component is formed by combining container components NewIndex, What's really mounted is NewIndex, The truth .

matters needing attention

The nature of the situation , It's every time render In the process , They all form a new component , For new components ,React The processing logic is to unload the old components directly , Remount the new component , So in the process of our development , Pay attention to one problem, that is :

  • For function components , Don't declare new components and render them in their function execution context , In this way, each function update will cause the component to mount repeatedly .
  • For class components , Not in render Function , Do the same thing as above , Otherwise, the child components will be mounted repeatedly .

Case two : Event source disappear mysteriously

Unexpected cases

alias ( Xiao Ming ) On a dark and windy night , Whim write a controlled component . What's written is as follows :

export default class EventDemo extends React.Component{
    return <div>
      <input placeholder=" Please enter a user name ?" onChange={ this.handerChange.bind(this) }  />


input The value of is affected by state in value Attribute control , Xiao Ming wants to pass handerChange change value value , But he expects to be in setTimeout Complete the update in . When he wants to change input It's time , Something unexpected happened .


The console reported an error as shown above .Cannot read property 'value' of null That is to say by null. Event source target How to say no, No ?

Lead tracking

After receiving this case , Let's check the problem first , So let's start with handerChange Print directly, as follows :


It seems that the first step is not handerChange Why , And then we went on to setTimeout I found that :


Sure enough setTimeout Why , Why? setTimeout Event sources in Just disappeared ? First , The source of the incident must not have disappeared somehow , sure React The bottom layer does some extra processing to the event source , First we know that React It's using Event synthesis Mechanism , That's bound onChange Not really bound change event , It's bound by Xiao Ming handerChange It's not really an event handler . So in other words React The bottom layer helps us deal with the source of events . It's all possible that only we can learn from React Find clues in the source code . After checking the source code , I found a very suspicious clue .


function dispatchEventForLegacyPluginEventSystem(topLevelType,eventSystemFlags,nativeEvent,targetInst){
    const bookKeeping = getTopLevelCallbackBookKeeping(topLevelType,nativeEvent,targetInst,eventSystemFlags);
    batchedEventUpdates(handleTopLevel, bookKeeping);

dispatchEventForLegacyPluginEventSystem yes legacy In mode , The main function through which all events must pass ,batchedEventUpdates It's the logic of batch update , It will execute our real event handling functions , We talked about it in the principle of events chapter nativeEvent Namely Truly native event objects event.targetInst Namely Corresponding fiber object . We are handerChange The event source is React Synthetic event sources , So know when the event source is , How to be synthesized ? It may help to solve the case .

In principle of events, we will introduce React Using event plug-in mechanism , Like our onClick The event corresponds to SimpleEventPlugin, So Xiao Ming wrote onChange There are also special ChangeEventPlugin Event plug-ins , One of the most important functions of these plug-ins is to synthesize our event source objects e, So let's take a look at ChangeEventPlugin.


const ChangeEventPlugin ={
   eventTypes: eventTypes,
        const event = SyntheticEvent.getPooled(
            inst, //  Component instance
            nativeEvent, //  Native event sources  e
            target,      //  Native
     accumulateTwoPhaseListeners(event); //  This function handles real event functions in accordance with bubble capture logic , That is to say   handerChange  event
     return event; // 

We see the event source of the composite event handerChange Medium e, Namely SyntheticEvent.getPooled created . So this is the key to solving the case .


SyntheticEvent.getPooled = function(){
    const EventConstructor = this//  SyntheticEvent
    if (EventConstructor.eventPool.length) {
    const instance = EventConstructor.eventPool.pop();,dispatchConfig,targetInst,nativeEvent,nativeInst,);
    return instance;
  return new EventConstructor(dispatchConfig,targetInst,nativeEvent,nativeInst,);

Off the coast : In the event system Chapter , The concept of event pool in this article , I'm in a hurry , general , This section of this article will supplement the event pool in detail Concept .

getPooled The real concept of event pool is introduced , It does two main things :

  • Determine if there are free event sources in the event pool , If there is an event source reuse .
  • without , adopt new SyntheticEvent Create a new event source object in the same way . that SyntheticEvent Is to create the constructor of the event source object , Let's study it together .
const EventInterface = {
    return null;
function SyntheticEvent( dispatchConfig,targetInst,nativeEvent,nativeEventTarget){
  this.dispatchConfig = dispatchConfig; 
  this._targetInst = targetInst;    //  Component correspondence fiber.
  this.nativeEvent = nativeEvent;   //  Native event sources .
  this._dispatchListeners = null;   //  Store all the event listener functions .
  for (const propName in Interface) {
      if (propName === 'target') { = nativeEventTarget; //  What we actually print  target  It's here
      } else {
        this[propName] = nativeEvent[propName];
SyntheticEvent.prototype.preventDefault = function ()/* .... */ }     /*  Component browser default behavior  */
SyntheticEvent.prototype.stopPropagation = function (/* .... */  }  /*  Stop the event from bubbling  */

SyntheticEvent.prototype.destructor = function ()/*  Situation event source object */
      for (const propName in Interface) {
           this[propName] = null
    this.dispatchConfig = null;
    this._targetInst = null;
    this.nativeEvent = null;
const EVENT_POOL_SIZE = 10/*  Maximum number of event pools  */
SyntheticEvent.eventPool = [] /*  Binding event pool  */
SyntheticEvent.release=function ()/*  Clear the event source object , If the event pool upper limit is not exceeded , So put it back in the event pool  */
    const EventConstructor = this
    if (EventConstructor.eventPool.length < EVENT_POOL_SIZE) {

After I refine this piece of code , The truth gradually came to the surface , Let's take a look first SyntheticEvent What did you do :

  • First, give some initialization variables nativeEvent etc. . And then according to EventInterface Rules Native event sources Properties on , Make a copy for React Event source . And then one of the most important things is that we print Namely, When the event source is initialized, the real>nativeEventTarget

  • then React Event source , Bind your own block default behavior preventDefault, To prevent a bubble stopPropagation Other methods . But there's a key approach here, which is destructor, This function is empty React Your own event source object . So we finally got the answer , Our source of events That's why the probability of disappearance is so high destructor,destructor stay release Is triggered , Then put the event source into the event pool , Wait for the next reuse .

Now all the spearheads are pointing at release, that release When did it trigger ?


function executeDispatchesAndRelease(){

When React The event system performs all of the _dispatchListeners, Will trigger this method executeDispatchesAndRelease Release the current event source .

The truth

Back to Xiao Ming's problem , We said above ,React Finally, the event source will be set to null synchronously , And then put it in the event pool , because setTimeout It's asynchronous execution , When executing, the event source object has been reset and the event pool will be released , So we print = null, Only this and nothing more , The truth of the case came out .

Through this case, we understand that React Some concepts of event pooling :

  • React The event system has unique composite events , It has its own source of events , And there are some special cases of processing logic , For example, bubble logic and so on .
  • React To prevent the creation of an event source object for each event , Waste performance , So we introduced The concept of event pool , Each user event will take one from the event pool e, without , Just create one , The event source is then assigned , After the event is executed , Reset event source , Put it back in the event pool , To reuse .

Show... With a flow chart :


Case three : True and false React

crime scene

This is what happened to me , In development React Project time , For logic reuse , I put some packaged custom Hooks Upload to the company's private package On the management platform , Developing another React When the project is , Download the company package , Used inside the component . The code is as follows :

function Index({classes, onSubmit, isUpgrade}{
   /* useFormQueryChange  It's my custom hooks, And upload it to a private library , It is mainly used for the unified management of form controls   */
  const {setFormItem, reset, formData} = useFormQueryChange()
  React.useEffect(() => {
    if (isUpgrade)  reset()
  }, [ isUpgrade ])
  return <form

    <div className='btnbox' >
       { /*  Here is the business logic , Has been omitted  */ }


useFormQueryChange It's my custom hooks , And upload it to a private library , It is mainly used for the unified management of form controls , I didn't expect that the introduction would be popular . The error is as follows :


Check one by one

We are in accordance with the React The content of the error report , Check the problem one by one :

  • The first possible cause of error You might have mismatching versions of React and the renderer (such as React DOM), intend React and React Dom Version inconsistency , To cause this , But in our project React and React Dom All are v16.13.1, So get rid of this .

  • The second possible cause of error You might be breaking the Rules of Hooks It means you broke Hooks The rules , It's also impossible , Because there is no damage in my code hoos The behavior of rules . So the suspicion is also ruled out .

  • The third possible cause of error You might have more than one copy of React in the same app It means in the same application , There may be more than one React. So far, all the suspects point to the third , First, we refer to the custom hooks, Will there be another one inside React Well ?

According to the above tips, I found the custom hooks Corresponding node_modules There's another one in it React, This is it false React( Let's call it fake React) Ghost . We are Hooks principle As mentioned in the article ,React Hooks use ReactCurrentDispatcher.current In component initialization , The component update phase gives different hooks object , After the update, give ContextOnlyDispatcher, If you call the hooks, You'll report the above error , That explains This mistake is because our project , Introduced by execution context React It's the project itself React, But custom Hooks The quotation is false React Hooks Medium ContextOnlyDispatcher

Next, I see in the component library package.json in ,

"dependencies": {

Turned out to be React As dependencies So download custom Hooks When , hold React I downloaded it again . So how to solve this problem . For packaging React Component library ,hooks library , Out-of-service dependencies, Because it will be in the current dependencies Download to custom for dependencies hooks Under the library node_modules in . Instead, we should use peerDependencies, Use peerDependencies, Customize hooks We'll go to our project if we find related dependencies node_modules In looking for , Can fundamentally solve this problem . So we change it like this

"peerDependencies": {

It solved the problem perfectly .

Poke the fog away

This question makes us understand as follows :

  • For some hooks library , Component library , Its own dependence , Already in the project , So use peerDependencies Statement .

  • In the process of development , It's possible to use different versions of the same dependency , For example, the project introduced A Version dependency , The component library introduces B Version dependency . So how to deal with this situation . stay package.json The document provides a resolutions Configuration items can solve this problem , stay resolutions Lock the same import version in , This will not cause the problem caused by the existence of multiple versions of project dependencies .

project package.json That's how it's written

  "resolutions": {

This way, regardless of the dependencies in the project , Or other library dependencies , Will use the same version , Fundamentally solve the problem of multiple versions .

Case four :PureComponet/memo Functional failure problems

Description of the case

stay React When developing , But we want to use PureComponent Do performance optimization , Adjust component rendering , But after writing a piece of code , Find out PureComponent The function failed , The specific code is as follows :

class Index extends React.PureComponent{
     console.log(' Component rendering ')
     const { name , type } = this.props
     return <div>
       hello , my name is { name }
       let us learn { type }


export default function Home (){
   const [ number , setNumber  ] = React.useState(0)
   const [ type , setType ] = React.useState('react')
   const changeName = (name) => {
   return <div>
       <span>{ number }</span><br/>
       <button onClick={ ()=> setNumber(number + 1) } >change number</button>
       <Index type={type}  changeType={ changeName } name="alien"  />


We had expected :

  • about Index Components , Only props in name and type change , To make components render . But the reality is that :

Click on the button effect :


when the water subsides , the rocks emerge

Why does this happen ? Let's check again Index Components , Find out Index There is one on the component changeType, So is it the reason for this ? Let's analyze , First, the state update is in the parent component Home On ,Home Each component update produces a new changeName, therefore Index Of PureComponent Every time Shallow comparison , Find out props Medium changeName It's not equal every time , So it's updated , It gives us an intuitive sense that it's not working .

So how to solve this problem ,React hooks Provided in useCallback, It can be done to props The incoming callback function is cached , Let's change it Home Code .

const changeName = React.useCallback((name) => {

effect :


In this way, the problem is fundamentally solved , use useCallback Yes changeName Function to cache , In every time Home Component execution , as long as useCallback in deps No change ,changeName The memory space also points to the original function , such PureComponent Shallow comparison will find that it is the same changeName, So you don't render components , So far, the case has been solved .


We use function components + Class component development , If used React.memo React.PureComponent etc. api, Note the way events are bound to these components , If it's a function component , So if you want to keep Component only rendering control features Words , Then please use useCallback,useMemo etc. api Handle , If it's a class component , Do not bind events with arrow functions , Arrow functions can also cause failures .

There is a shallow comparison mentioned above shallowEqual, Next let's focus on PureComponent How is it? shallowEqual, Now we're going to take a closer look at shallowEqual The secret of . Then we can start with the update of class rents .


function updateClassInstance(){
    const shouldUpdate =
    checkHasForceUpdateAfterProcessing() ||
    return shouldUpdate

I'm here to simplify updateClassInstance, It's only about PureComponent Part of .updateClassInstance This function is mainly used to , Execution lifecycle , to update state, Determine whether the component is re rendered , Back to shouldUpdate Used to determine whether the current class component is rendered .checkHasForceUpdateAfterProcessing Check if the update source is the same as forceUpdate , If it is forceUpdate Components are bound to be updated ,checkShouldComponentUpdate Check that the component is rendered . Let's take a look at the logic of this function .

function checkShouldComponentUpdate(){
    /*  The life cycle of the class component is executed here  shouldComponentUpdate */
    const shouldUpdate = instance.shouldComponentUpdate(
    /*  Here we determine whether the component is  PureComponent  Pure components , If it's a pure component, it calls  shallowEqual  Shallow comparison   */
    if (ctor.prototype && ctor.prototype.isPureReactComponent) {
        return (
        !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)

checkShouldComponentUpdate There are two crucial roles :

  • The first is if a class component has a lifecycle shouldComponentUpdate, Will execute the life cycle shouldComponentUpdate, Determine whether the component is rendered .
  • If it turns out to be a pure component PureComponent, It will be shallower and older props and state Whether it is equal or not , If equal , The component is not updated . isPureReactComponent It's that we use PureComponent The logo of , It turns out to be pure components .

Next is the point shallowEqual, With props As an example , Let's see .


function shallowEqual(objA: mixed, objB: mixed): boolean {
  if (is(objA, objB)) { // is Sure   Comprehend   objA === objB  So return equal
    return true;

  if (
    typeof objA !== 'object' ||
    objA === null ||
    typeof objB !== 'object' ||
    objB === null
  ) {
    return false;  
  } //  If new and old props There's one that's not an object , Or it doesn't exist , Then go straight back false

  const keysA = Object.keys(objA); //  The old props /  The old state key Array of components
  const keysB = Object.keys(objB); //  new props /  new state key Array of components

  if (keysA.length !== keysB.length) { //  explain props Increase or decrease , So go straight back and don't want to wait
    return false;

  for (let i = 0; i < keysA.length; i++) { //  Go through the old props , Discover new props No, , Or old and new props Different, etc , Then the return does not update the component .
    if (
      !, keysA[i]) ||
      !is(objA[keysA[i]], objB[keysA[i]])
    ) {
      return false;

  return true// The default return is equal

shallowEqual The process goes like this ,shallowEqual return true Then prove equal , So don't update components ; If you return false I don't want to wait , So update the component .is We can understand it as ===

  • First step , Directly through === Judge whether it is equal , If equal , Then the return true. Normally, just call React.createElement Will recreate props, props It's all unequal .
  • The second step , If new and old props There's one that's not an object , Or it doesn't exist , Then go straight back false.
  • The third step , Judge the old and the new props, key I don't want to wait , explain props There is an increase or decrease in , Then go straight back false.
  • Step four , Go through the old props , Discover new props There's no corresponding , Or old and new props Different, etc , Then the return false.
  • Default return true.

This is it. shallowEqual Logic , The code is very simple . Interested students can have a look at .

Case five : useState Update the same State, Function components execute 2 Time

Received a report

It's a real problem , You may not notice , What caught my attention was a question from a friend of nuggets , Questions as follows :

 picture First of all, thank you very much for your report , I am here React-hooks principle As mentioned in , For the method function component of the update component useState And class components setState There's a difference ,useState In the source code, if you encounter the same two times state, By default, the component will be prevented from being updated , But in class components setState If not set PureComponent, Two times the same state Will update .

Let's review hooks How to prevent component updates in .

react-reconciler/src/ReactFiberHooks.js -> dispatchAction

if (is(eagerState, currentState)) { 
scheduleUpdateOnFiber(fiber, expirationTime); //  Schedule updates

If we judge the last time state -> currentState , And this time state -> eagerState equal , Then it will be directly return Prevent components from scheduleUpdate Schedule updates . So we thought if we did it twice useState Trigger the same state, Then the component can only be updated once , But is it true ?.

Put on file an investigation

Follow the clues provided by this digger , So let's start writing demo To verify .

const Index = () => {
  const [ number , setNumber  ] = useState(0)
  console.log(' Component rendering ',number)
  return <div className="page" >
    <div className="content" >
       <span>{ number }</span><br/>
       <button onClick={ () => setNumber(1) } > take number Set to 1</button><br/>
       <button onClick={ () => setNumber(2) } > take number Set to 2</button><br/>
       <button onClick={ () => setNumber(3) } > take number Set to 3</button>

export default class Home extends React.Component{
    return <Index />

Above demo, Three buttons , We expect to click every button in a row , Components are rendered only once , So we started experimenting :

effect :


Sure enough , We go through setNumber change number, Each time you click the button in succession , Components are updated 2 Time , According to our normal understanding , Every time you give number The same value , It'll only be rendered once , But why was it carried out 2 And then ?

It may be in trouble at first , I don't know how to solve the case , But we're thinking hooks In principle , Each function component uses the... Of the corresponding function component fiber Object to save hooks Information . So we can only from fiber Find a clue .


So how to find the corresponding function component fiber What about objects? , This follows the parent of the function component Home Start with , Because we can start with class components Home Find the corresponding fiber object , And then according to child Pointer found function component Index Corresponding fiber. Do as you say , Let's transform the above code into something like this :

const Index = ({ consoleFiber }) => {
  const [ number , setNumber  ] = useState(0)
      consoleFiber() //  Every time fiber After the update , Print  fiber  testing  fiber change
  return <div className="page" >
    <div className="content" >
       <span>{ number }</span><br/>
       <button onClick={ () => setNumber(1) } > take number Set to 1</button><br/>

export default class Home extends React.Component{
     console.log(this._reactInternalFiber.child) /*  Used to print function components  Index  Corresponding fiber */
    return <Index consoleFiber={ this.consoleChildrenFiber.bind(this) }  />

We focus on fiber On these attributes , It's very helpful to solve the case

  • Index fiber Upper memoizedState attribute , react hooks In principle, the article talked about , Function components use memoizedState Save all hooks Information .
  • Index fiber Upper alternate attribute
  • Index fiber Upper alternate On the properties memoizedState attribute . Is it very winding , We'll find out what it is soon .
  • Index On the component useState Medium number.

First of all, let's talk about alternate What does the pointer refer to ?

Speaking of alternate It's from fiber Architecture design , Every React Element nodes , Use two fiber Tree save state , A tree holds the current state , A tree keeps the last state , Two fiber For trees alternate Point to each other . That's what we're familiar with Double buffering .

Initialize printing

design sketch :


Initialization complete the first time render after , Let's see fiber These states on the tree

The first print result is as follows ,

  • fiber Upper memoizedState in baseState = 0 It's initialization useState Value .
  • fiber Upper alternate by null.
  • Index On the component number by 0.

Initialization flow : First, for the first initialization of a component , Will reconcile rendering to form a fiber Trees ( We It's called the tree for short A). Trees A Of alternate The attribute is null.

First click setNumber(1)

For the first time, we found component rendering , And then we print it out as follows :

  • Trees A Upper memoizedState in ** baseState = 0.
  • Trees A Upper alternate Point to Another one fiber( We call it a tree here B).
  • Index On the component number by 1.

Next we print the tree B Upper memoizedState


And we found that trees B On memoizedState Upper baseState = 1.

Come to the conclusion : The update status is all in the tree B On , And trees A Upper baseState Or before 0.

Let's make a bold guess at the update process : The first time you update the rendering , Because of the trees A in , non-existent alternate, So just copy a tree A As workInProgress( We call it here Trees B) All updates are in the current tree B In the middle of , therefore baseState It will be updated to 1, Then use the current Trees B Rendering . When it's over, the tree A And trees B adopt alternate Point to each other . Trees B As the next operation current Trees .

The second click setNumber(1)

Second printing , Components also render , Then we print fiber object , The effect is as follows :

  • fiber On the object memoizedState in baseState Updated to 1.

And then we print it out alternate in baseState It's also updated to 1.


After the second click , Trees A And trees B All updated to the latest baseState = 1

First, let's analyze the process : When we click the second time , It's through the last tree A Medium baseState = 0 and setNumber(1) Incoming 1 Comparison . So I found eagerState !== currentState , The component has been updated again . Next, I will use current Trees ( Trees B) Of alternate Pointing to the tree A As new workInProgress updated , Trees at this time A Upper baseState It's finally updated to 1 , This explains why the above two baseState All equal to 1. Next, the component rendering is complete . Trees A As a new current Trees .

In our second print , What's printed out is actually a post alternate tree B, Trees A And trees B So alternate as the latest state for rendering workInProgress Tree and cache last state for next rendering current Trees .

Click... For the third time ( There are many of them )

So the third click component doesn't render , That's a good explanation , Third click on the last tree B Medium baseState = 1 and setNumber(1) equal , So I went straight away return Logic .

Uncover the mystery ( What we learned )

  • Double buffered trees :React use workInProgress Trees ( Trees built in memory ) and current( Render tree ) To implement the update logic . We console.log Printed fiber It's all in memory workInProgress Of fiber Trees . A double cache is built in memory , In the next rendering , Directly use the cache tree as the next rendering tree , The last rendering tree is used as the cache tree , This can prevent the loss of updating state with only one tree , Speed up again dom Replacement and update of nodes .

  • Renewal mechanism : In an update , First of all, I will get current Treelike alternate As the present workInProgress, After rendering ,workInProgress The tree becomes current Trees . We use the tree above A And trees B And what has been saved baseState Model , To explain the renewal mechanism more vividly .hooks Medium useState Conduct state contrast , It's on the cache tree state And the latest state. All that explains why the same state, Function components execute 2 Time .

We use a flow chart to describe the whole process .


This case has been solved , Through this easily overlooked case , We learned about double buffering and update mechanisms .

Case six :useEffect modify DOM Elements cause weird flashes

doings of ghosts and gods

Xiao Ming ( alias ) When dynamically mounting components , I met the supernatural Dom Flash phenomenon , Let's look at the phenomenon first .

Flash phenomenon :


Code :

function Index({ offset }){
    const card  = React.useRef(null)
    React.useEffect(()=>{ = offset
    return <div className='box' >
        <div className='card custom' ref={card}   >《 React Advanced practice guide  》</div>


export default function Home({ offset = '300px' }){
   const [ isRender , setRender ] = React.useState(false)
   return <div>
       { isRender && <Index offset={offset}  /> }
       <button onClick={ ()=>setRender(true) } >  mount </button>

  • In the parent component, use isRender Dynamic loading Index, Click the button to control Index Rendering .
  • stay Index Accept the dynamic offset of offset. And by manipulating useRef Get the original dom Change the offset directly , Make the paddle slide . But there's a flash like the one above , Very unfriendly , So why is this a problem ?

Deepen understanding

It is preliminarily judged that the problem of this flash should be useEffect Caused by the , Why do you say that , Because the class component life cycle componentDidMount Write the same logic , But it's not going to happen . So why useEffect It's going to cause this , We can only find useEffect Of callback The timing of execution .

useEffect ,useLayoutEffect , componentDidMount The timing of execution is commit Stage execution . We know React There is one effectList Different storage effect. because React For different effect The logic and timing of execution are different . Let's see useEffect When defined , What kind of effect.


function mountEffect(create, deps){
  return mountEffectImpl(
    UpdateEffect | PassiveEffect, // PassiveEffect 

The information for this function is as follows :

  • useEffect To be endowed with PassiveEffect Type of effect .
  • Xiao Ming changed the original dom Function of position , Namely create.

that create When did the function execute ,React How to deal with PassiveEffect What about , This is the key to solving the case . Let's take a look at it Next React How to deal with PassiveEffect.


function commitBeforeMutationEffects({
  while (nextEffect !== null) {
    if ((effectTag & Passive) !== NoEffect) {
      if (!rootDoesHavePassiveEffects) {
        rootDoesHavePassiveEffects = true;
        /*   Asynchronous scheduling  - PassiveEffect */
        scheduleCallback(NormalPriority, () => {
          return null;
    nextEffect = nextEffect.nextEffect;

stay commitBeforeMutationEffects Function , It will be scheduled asynchronously flushPassiveEffects Method ,flushPassiveEffects In the method , about React hooks Will execute commitPassiveHookEffects, And then it will execute commitHookEffectListMount .

function commitHookEffectListMount(){
     if (lastEffect !== null) {
          effect.destroy = create(); /*  perform useEffect I'm hungry  */

stay commitHookEffectListMount in ,create Function will be called . We give dom The position of the element will take effect .

So here comes the question , What does asynchronous scheduling do ?React Asynchronous scheduling of , In order to prevent some tasks from delaying the browser drawing , And causes the card frame phenomenon ,react For some low priority tasks , Use asynchronous scheduling to handle , That is to say, let the browser have free time to perform these asynchronous tasks , Asynchronous task execution on different platforms , Different browsers have different ways of implementation , Let's consider the effect and setTimeout equally .

after rain the sky looks blue

From the above we find that useEffect The first parameter of create, Asynchronous call mode adopted , So flash is easy to understand , During the first rendering of the button component , First, execute the function component render, then commit Replace the real dom node , And then the browser draws . At this point, the browser has drawn once , Then the browser has free time to perform asynchronous tasks , So it's implemented create, Changed the location information of the element , Because the last time the element was drawn , At this time, another position has been changed , So I feel the flash effect , This case has been solved .,

So how do we solve the flash phenomenon , That's it React.useLayoutEffect ,useLayoutEffect Of create It's synchronous , So the browser draws once , Directly updated the latest location .

  React.useLayoutEffect(()=>{ = offset


What we learned in this section ?

From the angle of solving a case , From the point of view of principle React Some unexpected phenomena , Through these phenomena , We learned some React Something inside , I summarize the above cases ,

  • Case 1 - Understanding of some component rendering and component error timing statements
  • Case two - Supplement to the concept of actual event pool .
  • Case three - Is to introduce multiple versions of some component libraries React Thinking and solutions .
  • Case four - Pay attention to memo / PureComponent The binding event , And how to deal with it PureComponent Logic , shallowEqual Principle .
  • Case five - The actual is fiber Explanation of double cache tree .
  • Case six - It's right useEffect create The timing of execution .
 front end GoGoGo
front end GoGoGo
Committed to increasing front-end revenue . Provide in-depth interviews , The front end of promotion .
8 Original content
official account
Please bring the original link to reprint ,thank
Similar articles