1. setState
There are actually two ways to write a setState update
The setState method is called synchronously, but causes React state updates asynchronously.
Why is setState designed to be asynchronous?
SetState is designed to be asynchronous and can significantly improve performance:
- If setState is updated every time, it means that the render function will be called frequently and the interface will be re-rendered, which is inefficient; The best thing to do is to get multiple updates and then batch them;
- If the synchronization updates state, but the render function is not executed, and peOPS depends on data in state, then state and props cannot be kept in sync; The inconsistency between pstate and props may cause a lot of problems in development.
1.1 object type
setState(stateChange, [callback])
- StateChange is a stateChange object (this object can represent a stateChange and is asynchronous)
- Callback is an optional callback function that will not be called until the state is updated and the render is updated.
1.2 functional
setState(updater, [callback])
- Updater is a function that returns a stateChange object.
- The updater can receive both state and props
- Callback is an optional callback function that will be called only after the render is called and the status is updated.
1.3 code
Asynchronous update
Functional component
import React, { Component } from 'react'
export default class Demo extends Component {
state = { count: 0 }
add = () = > {
// Object setState
Const {count} = this.state //2. SetState ({count:count+1},()=>{console.log(this.state.count); }) //console.log('12 lines of output ',this.state.count); //0 setState changes asynchronously. React does not change the state until the code is executed
// Function setState
this.setState((state, props) = > { return { count: state.count + 1}})}render() {
return (
<div>
<h1>The current sum is: {this.state.count}</h1>
<button onClick={this.add}>I + 1 point</button>
</div>)}}Copy the code
The component class type
import React, { Component } from 'react';
function Home(props) {
// Hello World
return <h1>{props.message}</h1>
}
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
message: "Hello World"}}render() {
return (
<div>
<h2>Current count: {this.state.message}</h2>
<button onClick={e= >This.changetext ()}> Change the text</button>
<Home message={this.state.message}/>
</div>)}componentDidUpdate() {
// Method 2: Obtain the state of the asynchronous update
console.log(this.state.message);
}
changeText() {
// 2.setState is an asynchronous update
// this.setState({
// message: "Hello, Li Yinhe"
// })
// console.log(this.state.message); // Hello World
// Method 1: Obtain asynchronously updated data
// setState(update state, callback function)
this.setState({
message: "Hello, Li Yinhe."
}, () = > {
console.log(this.state.message); }}})Copy the code
Synchronous update
SetState is normally updated asynchronously, but is updated synchronously in the following cases:
// Add setState to the timer
setTimeout(() = > {
this.setState({
message: "Hello, Li Yinhe."
})
console.log(this.state.message);
}, 0);
// Case 2: native DOM event
componentDidMount() {
document.getElementById("btn").addEventListener("click".(e) = > {
this.setState({
message: "Hello, Li Yinhe."
})
console.log(this.state.message); })}Copy the code
1.4 Merger of setState data and itself
If I modify message through setState, it will not affect name. In fact, there is a merge of the original object and the new object:
Data merge code:
The latter two parameters are copied to the target Object using the object. assign method. If the second parameter has the same attribute as the third parameter, it is replaced with the content of the third parameter
import React, { Component } from 'react';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
message: "Hello World".name: "coderwhy"}}render() {
return (
<div>
<h2>{this.state.message}</h2>
<h2>{this.state.name}</h2>
<button onClick={e= >This.changetext ()}> Change the text</button>
</div>)}changeText() {
// Know the truth and you will be truly free
this.setState({
message: "Hello, Li Yinhe."
});
// object. assign({}, this.state, {message: "Hello, Li Yinhe "})}}Copy the code
Merge code itself:
import React, { Component } from 'react'
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0}}render() {
return (
<div>
<h2>Current count: {this.state.counter}</h2>
<button onClick={e= > this.increment()}>+1</button>
</div>)}increment() {
// 1.setState itself is merged and can only be incremented by 1 no matter how many times it is called
// this.setState({
// counter: this.state.counter + 1
// });
// this.setState({
// counter: this.state.counter + 1
// });
// this.setState({
// counter: this.state.counter + 1
// });
// 2. SetState is accumulated when merging
// this.setState((previous state, props)
this.setState((prevState, props) = > {
return {
counter: prevState.counter + 1}});this.setState((prevState, props) = > {
return {
counter: prevState.counter + 1}});this.setState((prevState, props) = > {
return {
counter: prevState.counter + 1}}); }}Copy the code
The reference data of 1.5 state should not be directly modified
Friends is an array, a reference type, and stores data as a pointer to memory in the heap. Push an object adds a reference to an object to memory in the heap, and setState{friends: this.state. Friends} assigns the pointer to friends. Using PureComponent (shouldComponentUpdate should compare new and old states) causes problemsCopy the code
import React, { PureComponent } from "react";
export default class App extends PureComponent {
// Reference type
state = {
friends: [{name: "lilei".age: 20 },
{ name: "lily".age: 25 },
{ name: "lucy".age: 22},]};// shouldComponentUpdate(newProps, newState) {
// if (newState.friends ! == this.state.friends) {
// return true;
/ /}
// return false;
// }
render() {
return (
<div>
<h2>Friends list:</h2>
<ul>
{this.state.friends.map((item, index) => {
return (
<li key={item.name}>Name: {item.name} Age: {item.age}<button onClick={()= > this.incrementAge(index)}>age+1</button>
</li>
);
})}
</ul>
<button onClick={()= >This.insertdata ()}> Add data</button>
</div>
);
}
insertData() {
// 1. Don't do this in development
// const newData = {name: "tom", age: 30}
// this.state.friends.push(newData);
// this.setState({
// friends: this.state.friends
// });
// 2
const newFriends = [...this.state.friends];
newFriends.push({ name: "tom".age: 30 });
this.setState({
friends: newFriends,
});
}
incrementAge(index) {
const newFriends = [...this.state.friends];
newFriends[index].age += 1;
this.setState({
friends: newFriends, }); }}Copy the code
1.6 summarize
Object setState is short for function setState.
Using the principle of
- If the new state does not depend on the original state
- If the new state depends on the old state
- If you need to retrieve the latest state data after setState() is executed, you can read the asynchronously updated value in the second callback function
- In the component life cycle or React composite event, setState is asynchronous;
- In setTimeout or native DOM events, setState is synchronous;
- Do not directly modify the reference data in state
2. lazyLoad
LazyLoad of routing components
//1. Use the React lazy function with the import() function to load the routing components dynamically
const Login = lazy(() = >import('@/pages/Login'))
//2. Use
to specify a custom loading interface to be displayed before loading the route package file
<Suspense fallback={<h1>loading.....</h1>} ><Switch>
<Route path="/xxx" component={Xxxx}/>
<Redirect to="/login"/>
</Switch>
</Suspense>
Copy the code
case
import React, { Component,lazy,Suspense} from 'react'
import {NavLink,Route} from 'react-router-dom'
// import Home from './Home'
// import About from './About'
// The Loading component is used to display content when it is not loaded
import Loading from './Loading'
// Lazy load introduces the Home, About component, note that the arrow function cannot use {}
const Home = lazy(() = > import('./Home'))const About = lazy(() = > import('./About'))
export default class Demo extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<div className="page-header"><h2>React Router Demo</h2></div>
</div>
</div>
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">{/* React switch components with routing links */<NavLink className="list-group-item" to="/about">About</NavLink>
<NavLink className="list-group-item" to="/home">Home</NavLink>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">{by<Suspense>Wrap and return a custom loading interface}<Suspense fallback={<Loading/>}> {/* Register route */}<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
</Suspense>
</div>
</div>
</div>
</div>
</div>)}}Copy the code
3. Fragment
Use:
<Fragment></Fragment>
/ / phrases
<></>Note: Fragment only has key property, <></> no propertyCopy the code
Function:
You don’t have to have a real DOM root tag instead of the outermost div
import React, { PureComponent, Fragment } from "react";
export default class App extends PureComponent {
state = {
counter: 0.friends: [{name: "why".age: 18 },
{ name: "lilei".age: 20 },
{ name: "kobe".age: 25},]};render() {
return (
<>
{this.state.friends.map((item, index) => {
return (
<Fragment key={item.name}>
<div>{item.name}</div>
<p>{item.age}</p>
<hr />
</Fragment>
);
})}
</>); }}Copy the code
4. StrictMode
JSX enables strict mode checking for all components
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>.document.getElementById('root'));Copy the code
- StrictMode is a tool for highlighting potential problems in your application.
- Like fragments, StrictMode does not render any visible UI.
- It triggers additional checks and warnings for its descendant elements.
- Strict mode checking runs only in development mode; They do not affect production builds.
- You can enable strict mode for any part of your application.
- As shown below:
No strict schema checking is run on Header and Footer components; However, ComponentOne and ComponentTwo and all their descendants will be checked
What does strict mode check for?
- Identify unsafe lifecycles:
- Use the outdated REF API
- Using the deprecated findDOMNode method:
In the React API, you could get the DOM from findDOMNode, but this is no longer recommended
- Check for unexpected side effects:
In strict mode the component’s constructor is called twice; This is intentional in strict mode, to let you see if some of the logic written here has any side effects when called multiple times; In a production environment, it is not called twice;
- Detecting stale context APIS:
Context is used by declaring the properties of the Context object through the static property, returning the Context object through getChildContext, etc. This method is no longer recommended
5. Context
understand
A method of communication between components, often used for communication between ancestor components and descendant components
The Context related API
React.createContext
:
- Create a shared Context object:
- If a component subscribes to the Context, it reads the current Context value from the nearest matching Provider;
- DefaultValue indicates that the component did not find the corresponding Provider during the top-level lookup, so the defaultValue is used
Context.Provider
:
- Each Context object returns a Provider React component that allows the consuming component to subscribe to changes to the Context:
- The Provider receives a value property and passes it to the consuming component.
- A Provider can be associated with multiple consumer components.
- Multiple providers can also be nested. Data in the inner layer overwrites data in the outer layer.
- When a Provider’s value changes, all its internal consumer components are rerendered;
Class.contextType
- The contextType property mounted on the class is reassigned to a Context object created by react.createcontext () :
- This allows you to use this.context to consume the value of the most recent context; You can access it in any lifecycle, including the render function;
Context.Consumer
- Here, the React component can also subscribe to context changes. This allows you to subscribe to the context in a functional component.
- Here we need function as child;
- This function takes the current context and returns a React node;
use
1Create a Context container object:const XxxContext = React.createContext()
2Provider: <xxxContext.Provider value={data}> </xxxContext.Provider>3Descendant components read data:// The first method applies only to class components
static contextType = xxxContext // Declare to receive the context
this.context // Read the value data in context
// The second way: function components and class components can be used, but only this one (function, class) if multiple nesting is used.
<xxxContext.Consumer>
{
value= > ( // Value is the value data in contextWhat to display)} </ xxxcontext.consumer >Copy the code
Pay attention to
Context is usually not used in application development. It is usually packaged with the React pluginCopy the code
case
import React, { Component } from 'react'
import './index.css'
// Create the Context object
constMyContext = React. CreateContext (default, used when value is invalid)// Add Provider and Consumer
const { Provider, Consumer } = MyContext
export default class A extends Component {
state = { username: 'tom'.age: 18 }
render() {
const { username, age } = this.state
return (
<div className="parent">
<h3>I'm component A</h3>
<h4>My username is {username}.</h4>
<Provider value={{ username: username.age: age}} >
<B />
</Provider>
</div>)}}// There is no need to write anything
class B extends Component {
render() {
return (
<div className="child">
<h3>I'm component B</h3>
<C />
</div>)}}// Class components
/* class C extends Component {// extends Component static contextType = MyContext render() {const {username,age} = This. Context return (
I am C component
I received from A component :{username}, age is {age}
)}} */
// Functional components
function C() {
return (
<div className="grand">
<h3>I'm component C</h3>
<h4>The username I received from component A:<Consumer>{value => {return '${value.username}, age is ${value.age}'}}</Consumer>
</h4>
</div>)}Copy the code
Multilayer nested
import React, { Component } from 'react';
// Create the Context object
const UserContext = React.createContext({
nickname: "aaaa".level: -1
})
const ThemeContext = React.createContext({
color: "black"
})
function ProfileHeader() {
return (
<UserContext.Consumer>
{
value => {
return (
<ThemeContext.Consumer>
{
theme => {
return (
<div>
<h2 style={{color: theme.color}} >Nickname: {value.nickname}</h2>
<h2>User level: {value.level}</h2>
<h2>Color: {theme. Color}</h2>
</div>)}}</ThemeContext.Consumer>)}}</UserContext.Consumer>)}function Profile(props) {
return (
<div>
<ProfileHeader />
<ul>
<li>Set 1</li>
<li>Set up 2</li>
<li>Set of 3</li>
<li>Set of 4</li>
</ul>
</div>)}export default class App extends Component {
constructor(props) {
super(props);
this.state = {
nickname: "kobe".level: 99}}render() {
return (
<div>
<UserContext.Provider value={this.state}>
<ThemeContext.Provider value={{ color: "red}} ">
<Profile />
</ThemeContext.Provider>
</UserContext.Provider>
</div>)}}Copy the code
6. Component performance optimization
Component 2 problems
As long as setState() is executed, the component will rerender () even without changing the state data ==> inefficient
If the current component rerenders (), it automatically rerenders the child component, even if the child doesn’t use any data from the parent
Efficient practice
Rerender () only if the component’s state or props data changes
why
The lifecycle hook shouldComponentUpdate() in Component always returns true
React provides a life cycle method shouldComponentUpdate (in many cases, we call it SCU for short), which takes arguments and returns values
This method takes two parameters:
- Parameter 1: nextProps Specifies the latest props property after modification
- Parameter 2: nextState Indicates the latest state attribute after modification
The return value of this method is a Boolean type
- Return true, then call the render method;
- Return false, there is no need to call the render method;
The default returns true, meaning that the render method is called whenever state changes;
To solve
Way to1Override the shouldComponentUpdate() method to compare old and new state or props data and only return if there are changestrueIf there is no returnfalse
Copy the code
Example:
import React, {Component} from 'react';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0.message: "Hello World"}}render() {
console.log("App render function called");
return (
<div>
<h2>Current count: {this.state.counter}</h2>
<button onClick={e= > this.increment()}>+1</button>
<button onClick={e= >This.changetext ()}> Change the text</button>
</div>)}shouldComponentUpdate(nextProps, nextState) {
// Call render again if counter is updated, otherwise not
if (this.state.counter ! == nextState.counter) {return true;
}
return false;
}
increment() {
this.setState({
counter: this.state.counter + 1})}changeText() {
this.setState({
message: "Hello, Li Yinhe."}}})Copy the code
If we had to implement shouldComponentUpdate manually for all our classes, that would be a lot more work for us developers. What is the purpose of the various judgments in shouldComponentUpdate?
ShouldComponentUpdate returns true or false if the props or state data has changed; React actually takes this into account, so React already implements it for us by default. How? Class inherits from PureComponent.
Way to2PureComponent overrides shouldComponentUpdate() to return only if the state or props data changestrueNote: This is a shallow comparison between the props and state data, if only the internal data of the data object has changed, returnfalseDo not modify state data directly, but instead use PureComponent optimizations in projects that generate new dataCopy the code
Example:
import React, { PureComponent } from 'react' // PureComponent is typically used in projects
import './index.css'
export default class Parent extends PureComponent {
state = { carName: "Mercedes c36".stus: ['zhang'.'xiao li'.'wang'] }
addStu = () = > {
This.setstate ({stus}) */ const {stus} = this.state stus. Unshift (' s ') this.setstate ({stus}) */
// Write it correctly
const { stus } = this.state
this.setState({ stus: ['liu'. stus] }) } changeCar =() = > {
this.setState({ carName: maybach})}/* shouldComponentUpdate(nextProps, nextState) { // console.log(this.props,this.state); // Current props and state // console.log(nextProps,nextState); CarName === nextstate.carname) {return false} else {return true}} */
render() {
console.log('Parent---render');
const { carName } = this.state
return (
<div className="parent">
<h3>I'm the Parent component</h3>
{this.state.stus}
<span>My carName is: {carName}</span><br />
<button onClick={this.changeCar}>Let me change</button>
<button onClick={this.addStu}>Add a small liu</button>
<Child carName="Rolling" />
</div>)}}class Child extends PureComponent {
/* shouldComponentUpdate(nextProps,nextState){ console.log(this.props,this.state); // Current props and state console.log(nextProps,nextState); // Props, target state return! (this.props.carName === nextProps.carName) } */
render() {
console.log('Child---render');
return (
<div className="child">
<h3>I am the Child component</h3>
<span>The car I received was: {this.props. CarName}</span>
</div>)}}Copy the code
One problem, however, is that PureComponent can only optimize the performance of class components, not functional components. The memo component is used to optimize the performance of functional components. The Memo component is the same as the memo component
7. Implement slots with React
React doesn’t have a slot per se, but the flexibility of React allows it to implement slot-like functionality like app.jsx
import React, { Component } from "react";
import NavBar from "./NavBar";
import NavBar2 from "./NavBar2";
export default class App extends Component {
render() {
const leftJsx = <span>aaa</span>;
return (
<div>
<NavBar name="" title="" className="">
<span>aaa</span>
<strong>bbb</strong>
<a href=# "/">ccc</a>
</NavBar>
<NavBar2
leftSlot={leftJsx}
centerSlot={<strong>bbb</strong>}
rightSlot={<a href=# "/">ccc</a>} / ></div>); }}Copy the code
Method 1: navbar.jsx
Implement the slot functionality using the props. Children property
import React, { Component } from "react";
export default class NavBar extends Component {
render() {
console.log(this.props.children[0]);
return (
<div className="nav-item nav-bar">
<div className="nav-left">{this.props.children[0]}</div>
<div className="nav-item nav-center">{this.props.children[1]}</div>
<div className="nav-item nav-right">{this.props.children[2]}</div>
</div>); }}Copy the code
JSX implements slots using properties of props
import React, { Component } from "react";
export default class NavBar2 extends Component {
render() {
const { leftSlot, centerSlot, rightSlot } = this.props;
return (
<div className="nav-item nav-bar">
<div className="nav-left">{leftSlot}</div>
<div className="nav-item nav-center">{centerSlot}</div>
<div className="nav-item nav-right">{rightSlot}</div>
</div>); }}Copy the code
style.css
body {
padding: 0;
margin: 0;
}
.nav-bar {
display: flex;
}
.nav-item {
height: 44px;
line-height: 44px;
text-align: center;
}
.nav-left, .nav-right {
width: 70px;
background-color: red;
}
.nav-center {
flex: 1;
background-color: blue;
}
Copy the code
8. False boundaries
To understand:
Error bounds: Used to catch descendant component errors and render alternate pages
Features:
You can only catch errors generated during the life cycle of descendant components, not errors generated by your own components and errors generated by other components in composite events and timers
Usage:
GetDerivedStateFromError cooperate componentDidCatch
// The lifecycle function is triggered when the background component reports an error
static getDerivedStateFromError(error) {
console.log(error);
// trigger before render
// return the new state
return {
hasError: true}; }componentDidCatch(error, info) {
// Statistics page error. Send the request to the background
console.log(error, info);
}
Copy the code
Parent
import React, { Component } from 'react'
import Child from './Child'
export default class Parent extends Component {
state = {
hasError:' ' // Used to identify whether a child component has an error
}
// The getDerivedStateFromError call is raised with an error message when an error occurs in a Parent child
static getDerivedStateFromError(error){
console.log('@ @ @',error);
return {hasError:error}
}
componentDidCatch(){
console.log('Here statistical errors are fed back to the server to inform the coder to fix the bug');
}
render() {
return (
<div>
<h2>I'm the Parent component</h2>
{this.state.hasError ? <h2>The current network is unstable. Try again later</h2> : <Child/>}
</div>)}}Copy the code
Child
import React, { Component } from 'react'
export default class Child extends Component {
state = {
users:[
{id:'001'.name:'tom'.age:18},
{id:'002'.name:'jack'.age:19},
{id:'003'.name:'peiqi'.age:20},]// users:'abc'
}
render() {
return (
<div>
<h2>I am the Child component</h2>
{
this.state.users.map((userObj)=>{
return <h4 key={userObj.id}>{userObj.name}----{userObj.age}</h4>})}</div>)}}Copy the code
9. Summary of component communication modes
Method:
Props: (1).children props
(2).render props message subscription-publishing: pubs-sub, events, etc. Centralized management: REdux, DVA, etcconText: Producer-consumer modelCopy the code
Relationships between components:
Parent and child components: props Sibling component (non-nested component) : message subscription-publish, centralized management Grandparent component (cross-level component) : Message subscription-publish, centralized management, conText(used less)Copy the code
10. Higher-order functions and higher-order components
1. Higher-order functions
A function is a higher-order function if it conforms to either of the following two specifications.
- If A function takes one or more functions as arguments, then A can be called A higher-order function.
- A function can be called A higher-order function if the return value of the call is still A function.
Common higher-order functions include: Promise, setTimeout, arr.map(), etc.
High-order functions, AOP, partial functions, coriolization have a good record, feel or good coriolization of the function: through the function call to continue to return the function, the realization of multiple received parameters finally unified processing of the function encoding form.
function sum(a){
return(b) = >{
return (c) = >{
return a+b+c
}
}
}
const result = sum(1) (2) (3)
console.log(result);
Copy the code
Case 1:
<script type="text/babel">
// Create a component
class Login extends React.Component{
// Initialization state
state = {
username:' './ / user name
password:' ' / / password
}
// Save the user name to the status
saveUsername = (event) = >{
this.setState({username:event.target.value})
}
// Save the password to the status
savePassword = (event) = >{
this.setState({password:event.target.value})
}
// Form submission callback
handleSubmit = (event) = >{
event.preventDefault() // Block form submission
const {username,password} = this.state
alert('The username you entered is:${username}, your password is:${password}`)}render(){
return(
<form onSubmit={this.handleSubmit}>User name:<input onChange={this.saveUsername} type="text" name="username"/>Password:<input onChange={this.savePassword} type="password" name="password"/>
<button>The login</button>
</form>)}}// Render component
ReactDOM.render(<Login/>.document.getElementById('test'))
</script>
Copy the code
The overhead controlled component is too cumbersome to write, so you can use the function Currie shorthand
<! -- Prepare a "container" --><div id="test"></div>
<script type="text/babel">
// Create a component
class Login extends React.Component{
// Initialization state
state = {
username:' './ / user name
password:' ' / / password
}
// Save the form data to the state
saveFormData = (dataType) = >{
return (event) = >{
this.setState({[dataType]:event.target.value})
}
}
// Form submission callback
handleSubmit = (event) = >{
event.preventDefault() // Block form submission
const {username,password} = this.state
alert('The username you entered is:${username}, your password is:${password}`)}render(){
return(
<form onSubmit={this.handleSubmit}>{/* is a function */} user name:<input onChange={this.saveFormData('username')} type="text" name="username"/>Password:<input onChange={this.saveFormData('password')} type="password" name="password"/>
<button>The login</button>
</form>)}}// Render component
ReactDOM.render(<Login/>.document.getElementById('test'))
</script>
Copy the code
<! -- Prepare a "container" --><div id="test"></div>
<script type="text/babel">
// Create a component
class Login extends React.Component{
// Initialization state
state = {
username:' './ / user name
password:' ' / / password
}
// Save the form data to the state
saveFormData = (dataType,event) = >{
this.setState({[dataType]:event.target.value})
}
// Form submission callback
handleSubmit = (event) = >{
event.preventDefault() // Block form submission
const {username,password} = this.state
alert('The username you entered is:${username}, your password is:${password}`)}render(){
return(
<form onSubmit={this.handleSubmit}>{/* is a function */} user name:<input onChange={event= >This.saveformdata ('username',event)} type="text" name="username"/><input onChange={event= > this.saveFormData('password',event) } type="password" name="password"/>
<button>The login</button>
</form>)}}// Render component
ReactDOM.render(<Login/>.document.getElementById('test'))
</script>
Copy the code
2. Advanced components
Higher-order Components, HOC for short;
A higher-order component is a function that takes a component and returns a new component.
- A higher-order component is not itself a component, but a function;
- This function takes a component as an argument and returns a component;
- Higher-order components are not part of the React API. They are design patterns based on the React composite features.
- Higher-order components are common in some React third-party libraries:
- Like connect in Redux;
- Such as withRouter in react-Router;
Definition of higher-order components
The call process for higher-order components looks something like this:
Higher-order functions are written like this:
Component name problem:
- In ES6, class names can be omitted from class expressions;
- Component names can be changed using displayName;
//App.jsx
import React, { PureComponent } from "react";
// If an App component wants to use props, it must get props from the higher-order component that wraps it
class App extends PureComponent {
render() {
return <div>App: {this.props.name}</div>; }}// Class components
//function enhanceComponent
function enhanceComponent(WrappedComponent) {
class NewComponent extends PureComponent {
render() {
return <WrappedComponent {. this.props} / >; }}// Redefine the component class name
NewComponent.displayName = "Kobe";
return NewComponent;
}
// Functional components
function enhanceComponent2(WrappedComponent) {
function NewComponent(props) {
return <WrappedComponent {. props} / >;
}
// Redefine the function component name
NewComponent.displayName = "Kobe";
return NewComponent;
}
const EnhanceComponent = enhanceComponent2(App);
export default EnhanceComponent;
//index.js
// Note that the App exported here is actually EnhanceComponent, so the name attribute is passed to EnhanceComponent
import App from "./App.jsx";
ReactDOM.render(<App name="why" />.document.getElementById("root"));
Copy the code
Application – props enhancement
Add the same locale properties for components uniformly — add new props without modifying the original code
import React, { PureComponent } from 'react';
// Define a higher-order component
function enhanceRegionProps(WrappedComponent) {
return props= > {
return <WrappedComponent {. props} region="China"/>}}class Home extends PureComponent {
render() {
return <h2>Home: {' nickname: ${this.props. Nickname} Level: ${this.props. Level} region: ${this.props.</h2>}}class About extends PureComponent {
render() {
return <h2>About: {' nickname: ${this.props. Nickname} level: ${this.props. Level} region: ${this.props.</h2>}}const EnhanceHome = enhanceRegionProps(Home);
const EnhanceAbout = enhanceRegionProps(About);
class App extends PureComponent {
render() {
return (
<div>
App
<EnhanceHome nickname="coderwhy" level={90} />
<EnhanceAbout nickname="kobe" level={99} />
</div>)}}export default App;
Copy the code
Example — Using higher-order components to share Context:
The original:
import React, { PureComponent, createContext } from "react";
/ / create the Context
const UserContext = createContext({
nickname: "Default".level: -1That area:"China"});class Home extends PureComponent {
render() {
return (
<UserContext.Consumer>
{(user) => {
return (
<h2>Home: {' nickname: ${user.nickname} Level: ${user.level} Region: ${user.region} '}</h2>
);
}}
</UserContext.Consumer>); }}class About extends PureComponent {
render() {
return (
<UserContext.Consumer>
{(user) => {
return (
<h2>About: {' nickname: ${user.nickname} Level: ${user.level} Region: ${user.region} '}</h2>
);
}}
</UserContext.Consumer>); }}class App extends PureComponent {
render() {
return (
<div>
App
<UserContext.Provider
value={{ nickname: "why", level: 90.region:"China"}} >
<Home />
<About />
</UserContext.Provider>
</div>); }}export default App;
Copy the code
Advanced Component Edition:
import React, { PureComponent, createContext } from "react";
class Home extends PureComponent {
render() {
return (
<h2>Home: {' nickname: ${this.props. Nickname} Level: ${this.props. Level} Region: ${this.props.</h2>); }}class About extends PureComponent {
render() {
return (
<h2>About: {' nickname: ${this.props. Nickname} level: ${this.props. Level} Region: ${this.props.</h2>); }}class Detail extends PureComponent {
render() {
return (
<ul>
<li>{this.props.nickname}</li>
<li>{this.props.level}</li>
<li>{this.props.region}</li>
<li>{this.props.id}</li>
</ul>); }}// Define a higher-order component
function withUser(WrappedComponent) {
return (props) = > {
return (
<UserContext.Consumer>
{(user) => {
console.log(props, user);
return <WrappedComponent {. props} {. user} / >;
}}
</UserContext.Consumer>
);
};
}
const UserHome = withUser(Home);
const UserAbout = withUser(About);
const UserDetail = withUser(Detail);
/ / create the Context
const UserContext = createContext({
nickname: "Default".level: -1That area:"China"});class App extends PureComponent {
render() {
return (
<div>
App
<UserContext.Provider
value={{ nickname: "why", level: 90.region:"China"}} >
<UserHome id={1} />
<UserAbout id={2} />
<UserDetail id={3} />
</UserContext.Provider>
</div>); }}export default App;
Copy the code
Application 2 – Render judgment authentication
In development, we may encounter scenarios where certain pages must be logged in successfully. If the user fails to log in, the login page is displayed. At this point, we can use higher-order components for authentication.
import React, { PureComponent } from 'react';
// Login component
class LoginPage extends PureComponent {
render() {
return <h2>LoginPage</h2>}}// Shopping cart component
class CartPage extends PureComponent {
render() {
return <h2>CartPage</h2>}}// Higher-order components
function withAuth(WrappedComponent) {
const NewCpn = (props) = > {
const {isLogin} = props;
if (isLogin) {
return <WrappedComponent {. props} / >
} else {
return <LoginPage/>
}
}
NewCpn.displayName = "AuthCpn"
return NewCpn;
}
const AuthCartPage = withAuth(CartPage);
export default class App extends PureComponent {
render() {
return (
<div>
<AuthCartPage isLogin={true}/>
</div>)}}Copy the code
Application three – Lifecycle hijacking
We can also use higher-order functions to hijack the lifecycle and do our own logic in the lifecycle:
The default version:
import React, { PureComponent } from 'react';
class Home extends PureComponent {
// The rendering is about to get a beginTime
UNSAFE_componentWillMount() {
this.beginTime = Date.now();
}
// Get a new endTime after rendering
componentDidMount() {
this.endTime = Date.now();
const interval = this.endTime - this.beginTime;
console.log('Home render time:${interval}`)}render() {
return <h2>Home</h2>}}class About extends PureComponent {
// The rendering is about to get a beginTime
UNSAFE_componentWillMount() {
this.beginTime = Date.now();
}
// Get a new endTime after rendering
componentDidMount() {
this.endTime = Date.now();
const interval = this.endTime - this.beginTime;
console.log('About rendering time:${interval}`)}render() {
return <h2>About</h2>}}export default class App extends PureComponent {
render() {
return (
<div>
<Home />
<About />
</div>)}}Copy the code
Advanced Component Edition:
import React, { PureComponent } from 'react';
// Higher-order components
function withRenderTime(WrappedComponent) {
return class extends PureComponent {
// The rendering is about to get a beginTime
UNSAFE_componentWillMount() {
this.beginTime = Date.now();
}
// Get a new endTime after rendering
componentDidMount() {
this.endTime = Date.now();
const interval = this.endTime - this.beginTime;
console.log(`${WrappedComponent.name}Render time:${interval}`)}render() {
return <WrappedComponent {. this.props} / >}}}class Home extends PureComponent {
render() {
return <h2>Home</h2>}}class About extends PureComponent {
render() {
return <h2>About</h2>}}const TimeHome = withRenderTime(Home);
const TimeAbout = withRenderTime(About);
export default class App extends PureComponent {
render() {
return (
<div>
<TimeHome />
<TimeAbout />
</div>)}}Copy the code
Meaning of higher order functions
We’ll find that some React code can be handled more elegantly with higher-order components. In fact, early React provided mixins for reuse between components, which are no longer recommended:
- Mixins can be interdependent and coupled to each other, which is not conducive to code maintenance
- Methods in different mixins can conflict with each other
- There are so many mixins that components can sense and even deal with them, which can snowball in code complexity
Of course, HOC has some drawbacks of its own: it needs to be wrapped or nested on top of the original components, and if you use it in large amounts, you get a lot of nesting, which makes debugging very difficult; HOC can hijack props and can cause conflict if conventions are not followed; Hooks were first introduced to solve many of the problems that existed before React, such as this pointing, hoc nesting complexity, etc
ForwardRef = forwardRef
Ref can’t be applied to functional components: because functional components have no instances, they can’t get the corresponding component object. However, during development, we might want to retrieve the DOM of an element in a functional component. How do we do this?
- Method 1: Pass the REF attribute directly (wrong)
- Method 2: through the forwardRef function;
import React, { PureComponent, createRef, forwardRef } from "react";
class Home extends PureComponent {
render() {
return <h2>Home</h2>; }}// function Profile(props) {
// return Profile
;
// }
// the forwardRef of the higher order component
const Profile = forwardRef(function (props, ref) {
return <p ref={ref}>Profile</p>;
});
export default class App extends PureComponent {
titleRef = createRef();
homeRef = createRef();
profileRef = createRef();
render() {
return (
<div>
<h2 ref={this.titleRef}>Hello World</h2>
<Home ref={this.homeRef} />
<Profile ref={this.profileRef} name={"why} "/ >
<button onClick={(e)= >Enclosing printRef ()} > print ref</button>
</div>
);
}
printRef() {
console.log(this.titleRef.current);
console.log(this.homeRef.current);
console.log(this.profileRef.current); }}Copy the code
12. Use of Portals
In some cases, we want to render content independent of the parent component, and even independent of the DOM element currently mounted (by default, it is mounted to the DOM element with the ID root).
Portal provides an excellent solution for rendering child nodes to DOM nodes that exist outside the parent component:
- The first argument (child) is any renderable React child, such as an element, string, or fragment;
- The second argument (container) is a DOM element;
In general, when you return an element from the render method of a component, the element is mounted to its nearest parent in the DOM node.
However, sometimes it is beneficial to insert child elements at different locations in a DOM node.
For example, we want to develop a Modal component that renders its children to the middle of the screen:
- Step 1: Modify index.html to add a new node
- Step 2: Style the node
- Step 3: Write component code
//App.jsx
import React, { PureComponent } from 'react';
import ReactDOM from 'react-dom';
class Modal extends PureComponent {
render() {
return ReactDOM.createPortal(
this.props.children,
document.getElementById("modal"))}}class Home extends PureComponent {
render() {
return (
<>
<h2>Home</h2>
<Modal>
<h2>Title</h2>
</Modal>
</>)}}export default class App extends PureComponent {
render() {
return (
<div>
<Home/>
</div>)}}//index.html
<div id="root"></div>
<div id="modal"></div>
//index.css
#modal {
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
//index.js
import App from "./App.jsx";
import "./css/index.css";
Copy the code
React transition animation
1. The react – the transition – group is introduced
- During development, we wanted to add some kind of transition animation to the display and disappearance of a component, which would add to the user experience.
- Of course, we could have implemented these transitions using native CSS, but the React community provides a react-transition-group for transition animations.
- React-addons-css-transition-group (react-transition-group)
- The react-transition-group itself is very small and does not add too much burden to our application.
- This library helps us easily implement entry and exit animations for components that require additional installation:
- Liverpoolfc.tv: reactcommunity.org/react-trans…
npm npm install react-transition-group --save
yarn yarn add react-transition-group
Copy the code
2. React-transition-group Main components
React-transition-group consists of four components:
- Transition
- This component is a platform-independent component (not necessarily combined with CSS);
- In front-end development, we are generally combined with CSS to complete the style, so the more commonly used is CSSTransition;
- CSSTransition
- In front-end development, CSSTransition is often used for transition animations
- SwitchTransition
- Use this component when switching between two components to show and hide
- TransitionGroup
- Wrap multiple animation components in it, typically used to animate elements in a list
3. CSSTransition
- CSSTransition is built on the Transition component:
- During the execution of CSSTransition, there are three states: appear, Enter and exit.
- They have three states and need to define the corresponding CSS styles:
- First, the start state: for the classes -appear, -enter, exit;
- The second type: execute animation: the corresponding classes are -appear-active, -enter-active, -exit-active;
- The corresponding classes are -appear-done, -enter-done, and -exit-done.
- CSSTransition common corresponding properties:
- In: Triggers the entry or exit state
- If unmountOnExit={true} is added, the component will be removed after the exit animation is executed;
- When in is true, the state will be triggered, and the -enter and -enter-acitve classes will be added to start the animation. When the animation is finished, the two classes will be removed, and the -enter-done class will be added.
- When in is false, the exit state is triggered, and -exit and -exit-active classes are added to start animation. When animation is finished, both classes are removed, and -exit-done class is added.
- In: Triggers the entry or exit state
CSSTransition common attributes:
- ClassNames: The name of the animation class determines the class names of the CSS: card-enter, card-enter-active, card-enter-done.
- Timeout: indicates the time of transition animation. The animation will be executed after the set time is reached
onEntered
withonExited
- “Appear” : Whether to add the animation at the first entry (it should be true at the same time as “in”, and then add the xxX-Appear class to the CSS to set the animation at the first entry)
- UnmountOnExit: Unmounts components upon exit – true unmounts DOM elements upon exit
- Other attributes can be referred to the official website to learn: reactcommunity.org/react-trans…
- CSSTransition hook function: mainly to check the animation execution process, to complete some JavaScript operations
-
OnEnter: fired before entering the animation;
-
OnEntering: Triggered when the application enters an animation;
-
OnEntered: Triggered when the application enters the animation
-
OnExit: triggered before exiting the animation;
-
Onwithdraw: Being triggered during app exit animation;
-
OnExited: Triggered after the app exit animation ends
-
//CSSTransition.css
/* Enter the start/first enter the start */
.card-enter, .card-appear {
opacity: 0;
transform: scale(6.);
}
/* Enter the process/first enter the process */
.card-enter-active, .card-appear-active {
opacity: 1;
transform: scale(1);
transition: opacity 300ms, transform 300ms;
}
/* Enter complete/first enter complete */
.card-enter-done, .card-appear-done {
opacity: 1;
}
/* Exit start */
.card-exit {
opacity: 1;
transform: scale(1);
}
/* Exit process */
.card-exit-active {
opacity: 0;
transform: scale(6.);
transition: all 300ms;
}
/* Exit complete */
.card-exit-done {
opacity: 0;
}
//CSSTransition.js
import React, { PureComponent } from "react";
import { CSSTransition } from "react-transition-group";
import { Card, Avatar } from "antd";
import {
EditOutlined,
EllipsisOutlined,
SettingOutlined,
} from "@ant-design/icons";
const { Meta } = Card;
export default class CSSTransitionDemo extends PureComponent {
state = {
isShow: true};render() {
const { isShow } = this.state;
return (
<div>
<button
onClick={(e)= >{ this.setState({ isShow: ! isShow }); }} > Show/Hide</button>
<CSSTransition
in={isShow}
classNames="card"
timeout={300}
unmountOnExit={true}
appear
onEnter={(el)= >The console. The log (" started ")} onEntering = {(el) = > console. The log (" is entering ")} onEntered = {(el) = > console. The log (" into the complete ")} onExit = {(el) = > The console. The log (" began to exit ")} onExiting = {(el) = > console. The log (" exit status ")} onExited = {(el) = > console. The log (completed "exit")} ><Card
style={{ width: 300 }}
cover={
<img
alt="example"
src="https://gw.alipayobjects.com/zos/rmsportal/JiqGstEfoWAOHiTxclqi.png"
/>
}
actions={[
<SettingOutlined key="setting" />.<EditOutlined key="edit" />.<EllipsisOutlined key="ellipsis" />,]} ><Meta
avatar={
<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />
}
title="Card title"
description="This is the description"
/>
</Card>
</CSSTransition>
</div>); }}//App.js
import React, { PureComponent } from "react";
import CSSTransitionDemo from "./transition/CSSTransitionDemo";
export default class App extends PureComponent {
render() {
return (
<div style={{ textAlign: "center", padding: "30px}} ">
<CSSTransitionDemo />
</div>); }}//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './transition/CSSTransition.css';
import 'antd/dist/antd.css';
ReactDOM.render(
<App />.document.getElementById('root'));Copy the code
4. SwitchTransition
- SwitchTransition is a cool animation for switching between two components:
- For example, if we have a button that toggles between ON and off, we want to see ON exit from the left and OFF enter from the right.
- This animation is called vUE Transition Modes in VUE;
- The react-transition-group uses SwitchTransition to implement this animation.
- The main attribute in SwitchTransition is mode, which has two values
- In-out: New components are entered and old components are removed.
- Out-in: indicates that components are removed and new components are added.
- How do I use SwitchTransition?
- The CSSTransition or Transition component should be included in the SwitchTransition component. You cannot directly wrap the component you want to switch.
- The CSSTransition or Transition component in SwitchTransition no longer accepts the in attribute as before to determine the state of the element. Instead, it accepts the key attribute.
//SwitchTransition.css
.btn-enter {
opacity: 0;
transform: translateX(100%);
}
.btn-enter-active {
opacity: 1;
transform: translateX(0);
transition: opacity 1000ms, transform 1000ms;
}
.btn-exit {
opacity: 1;
transform: translateX(0);
}
.btn-exit-active {
opacity: 0;
transform: translateX(-100%);
transition: all 1000ms;
}
//SwitchTransition.js
import React, { PureComponent } from "react";
import "./SwitchTransition.css";
import { SwitchTransition, CSSTransition } from "react-transition-group";
export default class SwitchTransitionDemo extends PureComponent {
state = {
isOn: true};render() {
const { isOn } = this.state;
return (
<div>
<SwitchTransition mode="out-in">
<CSSTransition
key={isOn ? "on" : "off"}
classNames="btn"
timeout={1000}
>
<button onClick={(e)= >this.setState({ isOn: ! this.state.isOn })}> {isOn ? "on" : "off"}</button>
</CSSTransition>
</SwitchTransition>
</div>); }}Copy the code
5. TransitionGroup
When we have a set of animations, we need to put these csstransitions into a TransitionGroup to complete the animation:
//TransitionGroup.css
.item-enter, .item-appear {
opacity: 0;
transform: scale(6.);
}
.item-enter-active ,.item-appear-active{
opacity: 1;
transform: scale(1);
transition: opacity 300ms, transform 300ms;
}
.item-enter-done ,.item-appear-done {
color: rgb(255.136.0);
}
.item-exit {
opacity: 1;
transform: scale(1);
}
.item-exit-active {
opacity: 0;
transform: scale(6.);
transition: opacity 300ms, transform 300ms;
}
.item-exit-done {
opacity: 0;
}
//TransitionGroup.js
import React, { PureComponent } from "react";
import { CSSTransition, TransitionGroup } from "react-transition-group";
import "./TransitionGroup.css";
export default class TransitionGroupDemo extends PureComponent {
state = {
names: ["coderwhy"."kobe"."lilei"]};render() {
return (
<div>
<TransitionGroup>
{this.state.names.map((item, index) => {
return (
<CSSTransition key={index} timeout={500} classNames="item" appear>
<div>
{item}
<button onClick={(e)= > this.removeItem(index)}>-</button>
</div>
</CSSTransition>
);
})}
</TransitionGroup>
<button onClick={(e)= > this.addName()}>+name</button>
</div>
);
}
addName() {
this.setState({
names: [...this.state.names, "zgc"]}); }removeItem(index) {
// index indey indez
this.setState({
names: this.state.names.filter((item, indey) = > index !== indey),
});
}
}
Copy the code