React component life cycle and DOM Diffing algorithm

1. Export life cycle

Requirements: Define components to implement the following functions

  1. Make the specified text do show/hide gradient animation
  2. From being fully visible to completely disappearing, it takes 2S
  3. Click the “Dead” button to uninstall the component from the interface

screenshots.gif

 <!-- Prepare a "container" -->
<div id="test"></div>
\t
<script type="text/babel">
//Create component
//Lifecycle callback function <=> lifecycle hook function <=> lifecycle function <=> lifecycle hook
class Life extends React. Component{<!-- -->

state = {<!-- -->opacity:1}

death = ()=>{<!-- -->
//Uninstall the component
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}

//The component is hung
componentDidMount(){<!-- -->
console.log('componentDidMount');
this.timer = setInterval(() => {<!-- -->
//Get the original state
let {<!-- -->opacity} = this.state
//decrease by 0.1
opacity -= 0.1
if(opacity <= 0) opacity = 1
// set the new transparency
this.setState({<!-- -->opacity})
}, 200);
}

//The component will be unmounted
componentWillUnmount(){<!-- -->
//clear timer
clearInterval(this. timer)
}

//After initial rendering and state update
render(){<!-- -->
console.log('render');
return(
<div>
<h2 style={<!-- -->{<!-- -->opacity:this.state.opacity}}>What if you can't learn React? </h2>
<button onClick={<!-- -->this.death}>Not alive</button>
</div>
)
}
}
// render component
ReactDOM.render(<Life/>,document.getElementById('test'))
</script>

2. Understanding of life cycle

  • Lifecycle callback function <=> lifecycle hook function <=> lifecycle function <=> lifecycle hook
 1. A component will go through some specific stages from creation to death.
    2. React components contain a series of hook functions (life cycle callback functions), which will be called at specific moments.
    3. When we define a component, we will do specific work in a specific life cycle callback function

image.png

3. Life cycle (old)

1. Life cycle flowchart (old)

react life cycle (old).png

2. Phases of the lifecycle

Initialization phase: Triggered by ReactDOM.render()-first render

 1. constructor()
    2. componentWillMount()
    3. render()
    4. componentDidMount() ===> commonly used
    Generally, do some initialization in this hook, such as: start timer, send network request, subscribe message

Update phase: triggered by this.setSate() inside the component or the render of the parent component

 1. shouldComponentUpdate()
    2. componentWillUpdate()
    3.render() =====> must use one
    4. componentDidUpdate()

Unmount component: Triggered by ReactDOM.unmountComponentAtNode()

 componentWillUnmount() =====> Commonly used
    Generally, do some final things in this hook, such as closing the timer and unsubscribing from the message

3. Life cycle validation

 <!-- Prepare a "container" -->
<div id="test"></div>
\t
<script type="text/babel">
//Create component
class Count extends React. Component{<!-- -->

//constructor
constructor(props){<!-- -->
console.log('Count---constructor');
super(props)
//Initialize state
this.state = {<!-- -->count:0}
}

//callback for plus 1 button
add = ()=>{<!-- -->
//Get the original state
const {<!-- -->count} = this.state
\t\t\t\t//update status
this.setState({<!-- -->count:count + 1})
}

//Callback for uninstall component button
death = ()=>{<!-- -->
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}

//Callback for force update button
force = ()=>{<!-- -->
this. forceUpdate()
}

//The hook that the component will mount
componentWillMount(){<!-- -->
console.log('Count---componentWillMount');
}

//The hook for the component to be mounted
componentDidMount(){<!-- -->
console.log('Count---componentDidMount');
}

//The hook for the component to be uninstalled
componentWillUnmount(){<!-- -->
console.log('Count---componentWillUnmount');
}

//The "valve" that controls component updates
shouldComponentUpdate(){<!-- -->
console.log('Count---shouldComponentUpdate');
return true
}

//The hook that the component will update
componentWillUpdate(){<!-- -->
console.log('Count---componentWillUpdate');
}

//The hook for the component to be updated
componentDidUpdate(){<!-- -->
console.log('Count---componentDidUpdate');
}

render(){<!-- -->
console.log('Count---render');
const {<!-- -->count} = this.state
return(
<div>
<h2>The current sum is: {<!-- -->count}</h2>
<button onClick={<!-- -->this.add}>click me + 1</button>
<button onClick={<!-- -->this.death}>Uninstall component</button>
<button onClick={<!-- -->this.force}>Do not change the data in any state, force to update it</button>
</div>
)
}
}
ReactDOM.render(<Count/>,document.getElementById('test'))
</script>

image.png

screenshots.gif

The render process of the parent component

image.png

 <!-- Prepare a "container" -->
<div id="test"></div>

<script type="text/babel">
//Parent component A
class A extends React. Component{<!-- -->
//Initialize state
state = {<!-- -->carName:'Benz'}

changeCar = ()=>{<!-- -->
this.setState({<!-- -->carName:'Alto'})
}

render(){<!-- -->
return(
<div>
<div>I am A component</div>
<button onClick={<!-- -->this.changeCar}>Change car</button>
<B carName={<!-- -->this.state.carName}/>
</div>
)
}
}
\t\t
//Subcomponent B
class B extends React. Component{<!-- -->
// the hook that the component will receive new props
componentWillReceiveProps(props){<!-- -->
console.log('B---componentWillReceiveProps',props);
}

//The "valve" that controls component updates
shouldComponentUpdate(){<!-- -->
console.log('B---shouldComponentUpdate');
return true
}
//The hook that the component will update
componentWillUpdate(){<!-- -->
console.log('B---componentWillUpdate');
}

//The hook for the component to be updated
componentDidUpdate(){<!-- -->
console.log('B---componentDidUpdate');
}

render(){<!-- -->
console.log('B---render');
return(
<div>I am component B, and the received car is: {<!-- -->this.props.carName}</div>
)
}
}
\t\t
// render component
ReactDOM.render(<A/>,document.getElementById('test'))
</script>

image.png

4. Life cycle (new)

1. Life cycle flowchart (new)

react lifecycle (new).png

2. Phases of the lifecycle

Initialization phase: Triggered by ReactDOM.render()-first render

 1. constructor()
    2. getDerivedStateFromProps
    3. render()
    4. componentDidMount() =====> Commonly used. Generally, do some initialization in this hook, such as: start timer, send network request, subscribe message

Update phase: triggered by this.setSate() inside the component or re-render of the parent component

 1. getDerivedStateFromProps
    2. shouldComponentUpdate()
    3. render()
    4. getSnapshotBeforeUpdate
    5. componentDidUpdate()

Unmount component: Triggered by ReactDOM.unmountComponentAtNode()

 componentWillUnmount() =====> Commonly used
        Generally, do some final things in this hook, such as closing the timer and unsubscribing from the message

3. Life cycle validation

 <!-- Prepare a "container" -->
<div id="test"></div>

<script type="text/babel">
//Create component
class Count extends React. Component{<!-- -->
//constructor
constructor(props){<!-- -->
console.log('Count---constructor');
super(props)
//Initialize state
this.state = {<!-- -->count:0}
}

//callback for plus 1 button
add = ()=>{<!-- -->
//Get the original state
const {<!-- -->count} = this.state
\t\t\t\t//update status
this.setState({<!-- -->count:count + 1})
}

//Callback for uninstall component button
death = ()=>{<!-- -->
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}

//Callback for force update button
force = ()=>{<!-- -->
this. forceUpdate()
}
\t\t\t
//If the value of state depends on props at any time, you can use getDerivedStateFromProps
static getDerivedStateFromProps(props,state){<!-- -->
console.log('getDerivedStateFromProps',props,state);
return null
}

// Get a snapshot before updating
getSnapshotBeforeUpdate(){<!-- -->
console.log('getSnapshotBeforeUpdate');
return 'atguigu'
}

//The hook for the component to be mounted
componentDidMount(){<!-- -->
console.log('Count---componentDidMount');
}

//The hook for the component to be uninstalled
componentWillUnmount(){<!-- -->
console.log('Count---componentWillUnmount');
}

//The "valve" that controls component updates
shouldComponentUpdate(){<!-- -->
console.log('Count---shouldComponentUpdate');
return true
}

//The hook for the component to be updated
componentDidUpdate(preProps, preState, snapshotValue){<!-- -->
console.log('Count---componentDidUpdate', preProps, preState, snapshotValue);
}
\t\t\t
render(){<!-- -->
console.log('Count---render');
const {<!-- -->count} = this.state
return(
<div>
<h2>The current sum is: {<!-- -->count}</h2>
<button onClick={<!-- -->this.add}>click me + 1</button>
<button onClick={<!-- -->this.death}>Uninstall component</button>
<button onClick={<!-- -->this.force}>Do not change the data in any state, force to update it</button>
</div>
)
}
}
\t\t
// render component
ReactDOM.render(<Count count={<!-- -->199}/>,document.getElementById('test'))
</script>

image.png

4. Usage scenarios of getSnapshotBeforeUpdate

screenshots.gif

 <style>
.list{<!-- -->
width: 400px;
height: 150px;
background-color: skyblue;
overflow: auto;
margin: 0 auto;
}
.news{<!-- -->
height: 30px;
}
</style>
 <!-- Prepare a "container" -->
<div id="test"></div>
\t
<script type="text/babel">
class NewsList extends React. Component{<!-- -->

state = {<!-- -->newsArr:[]}

componentDidMount(){<!-- -->
setInterval(() => {<!-- -->
//Get the original state
const {<!-- -->newsArr} = this.state
//Simulate a news
const news = 'news' + (newsArr.length + 1)
\t\t\t\t\t//update status
this.setState({<!-- -->newsArr:[news,...newsArr]})
}, 1000);
}

getSnapshotBeforeUpdate(){<!-- -->
return this.refs.list.scrollHeight
}

componentDidUpdate(preProps, preState, height){<!-- -->
this.refs.list.scrollTop + = this.refs.list.scrollHeight - height
}

render(){<!-- -->
return(
<div className="list" ref="list">
{<!-- -->
this.state.newsArr.map((n,index)=>{<!-- -->
return <div key={<!-- -->index} className="news">{<!-- -->n}</div>
})
}
</div>
)
}
}
ReactDOM.render(<NewsList/>,document.getElementById('test'))
</script>

image.png

5. Important hooks

 1. render: call to initialize rendering or update rendering
    2. componentDidMount: open monitoring, send ajax request
    3. componentWillUnmount: Do some finishing work, such as: clean up the timer

6. The hook that is about to be discarded

 1. componentWillMount
    2. componentWillReceiveProps
    3. componentWillUpdate
    There will be a warning when using it now. The next major version needs to be prefixed with UNSAFE_ before it can be used. It may be completely abandoned in the future. It is not recommended to use

5. DOM Diffing Algorithm

1. Verify Diffing algorithm

 <!-- Prepare a "container" -->
<div id="test"></div>
\t
<script type="text/babel">
class Time extends React. Component {<!-- -->
state = {<!-- -->date: new Date()}

componentDidMount () {<!-- -->
setInterval(() => {<!-- -->
this.setState({<!-- -->
date: new Date()
})
}, 1000)
}

render () {<!-- -->
return (
<div>
<h1>hello</h1>
<input type="text"/> <br/>
<span>
Now it is: {<!-- -->this.state.date.toTimeString()}
<input type="text"/>
</span>
</div>
)
}
}
ReactDOM.render(<Time/>,document.getElementById('test'))
</script>

2. The function of key

Classic interview questions

 Classic interview questions:
      1). What is the function of the key in react/vue? (What is the internal principle of the key?)
      2). Why is it better not to use index for the key when traversing the list?
      
1. The role of key in virtual DOM:
    1). Simply put: the key is the identifier of the virtual DOM object, and the key plays an extremely important role in updating the display.
    2). In detail: When the data in the state changes, react will generate a [new virtual DOM] according to the [new data]
Then React performs a diff comparison between [New Virtual DOM] and [Old Virtual DOM]. The comparison rules are as follows:

    a. The same key as the new virtual DOM is found in the old virtual DOM:
        (1). If the content in the virtual DOM has not changed, use the previous real DOM directly
        (2). If the content in the virtual DOM changes, a new real DOM will be generated, and then the previous real DOM in the page will be replaced

    b. The same key as the new virtual DOM was not found in the old virtual DOM
        Create a new real DOM based on the data, and then render it to the page
\t\t\t\t\t\t\t\t\t
2. Possible problems caused by using index as key:
    1. If the data is destroyed in sequence operations such as adding in reverse order and deleting in reverse order:
            It will generate unnecessary real DOM updates ==> The interface effect is fine, but the efficiency is low.
    2. If the structure also contains the DOM of the input class:
            Will produce an error DOM update ==> There is a problem with the interface.
    3. Attention! If there are no operations that destroy the order of data such as reverse order addition and reverse order deletion,
            It is only used to render the list for display, and there is no problem with using index as the key.
            
3. How to choose a key during development?:
    1. It is best to use the unique identifier of each piece of data as the key, such as id, mobile phone number, ID number, student number and other unique values.
    2. If it is determined that the data is simply displayed, it is also possible to use the index.

the code

<div id="test"></div>

<script type="text/babel">
class Person extends React. Component{<!-- -->

state = {<!-- -->
persons:[
{<!-- -->id:1,name:'Xiao Zhang',age:18},
{<!-- -->id:2,name:'Xiao Li',age:19},
]
}

add = ()=>{<!-- -->
const {<!-- -->persons} = this.state
const p = {<!-- -->id:persons.length + 1,name:'Xiao Wang',age:20}
this.setState({<!-- -->persons:[p,...persons]})
}

render(){<!-- -->
return (
<div>
<h2>Display personnel information</h2>
<button onClick={<!-- -->this.add}>Add a little king</button>
<h3>Use index (index value) as key</h3>
<ul>
{<!-- -->
this.state.persons.map((personObj,index)=>{<!-- -->
return <li key={<!-- -->index}>{<!-- -->personObj.name}---{<!-- -->personObj.age}<input type="text "/></li>
})
}
\t\t\t\t\t</ul>
<hr/>
<hr/>
<h3>Use id (the unique identifier of the data) as the key</h3>
<ul>
{<!-- -->
this.state.persons.map((personObj)=>{<!-- -->
return <li key={<!-- -->personObj.id}>{<!-- -->personObj.name}---{<!-- -->personObj.age}<input type=\ "text"/></li>
})
}
\t\t\t\t\t</ul>
</div>
)
}
}

ReactDOM.render(<Person/>,document.getElementById('test'))
</script>

Rendering

screenshots.gif

Description

 /*
Slow motion playback----use the index index value as the key

\t\t\tInitial data:
{id:1,name:'Xiao Zhang',age:18},
{id:2,name:'Xiao Li',age:19},
Initial virtual DOM:
<li key=0>Xiao Zhang---18<input type="text"/></li>
<li key=1>Xiao Li---19<input type="text"/></li>

Updated data:
{id:3,name:'Xiao Wang',age:20},
{id:1,name:'Xiao Zhang',age:18},
{id:2,name:'Xiao Li',age:19},
Virtual DOM after updating data:
<li key=0>Xiao Wang---20<input type="text"/></li>
<li key=1>Xiao Zhang---18<input type="text"/></li>
<li key=2>Xiao Li---19<input type="text"/></li>

-------------------------------------------------- ---------------

Slow motion playback----Use id unique identification as key

\t\t\tInitial data:
{id:1,name:'Xiao Zhang',age:18},
{id:2,name:'Xiao Li',age:19},
Initial virtual DOM:
<li key=1>Xiao Zhang---18<input type="text"/></li>
<li key=2>Xiao Li---19<input type="text"/></li>

Updated data:
{id:3,name:'Xiao Wang',age:20},
{id:1,name:'Xiao Zhang',age:18},
{id:2,name:'Xiao Li',age:19},
Virtual DOM after updating data:
<li key=3>Xiao Wang---20<input type="text"/></li>
<li key=1>Xiao Zhang---18<input type="text"/></li>
<li key=2>Xiao Li---19<input type="text"/></li>
*/