Overview: from state reuse to Hooks

Test Hooks' proficiency

Why can't Hooks be used in for loops and if statements

React.memo,React.useCallback,React. The role and comparison of usememo

The value in useState is an object. If you change the value in the object, will the component render? If you use react It's wrapped in memo()

What is the (Implementation) principle of Hooks?

What is the essence of Hooks? Why?

React Hooks, what convenience does it bring?

How does the useEffect in React Hooks distinguish between life cycle hooks

What is the difference between useEffect(fn, []) and componentDidMount

How's the answer? Before understanding a concept, the more doubts, the deeper the understanding

What is it?

React Hooks is a new feature of React 16.8. It allows you to use state and other react features without writing a class

Why are there Hooks

We must have a concept, that is, what is the essence of react? It is characterized by UI=f(data), everything is a component, and declarative programming. Then, since UI=f(data), data drives UI view changes through function. In business, you can't simply show, but also interact. The interaction will update the state. React changes the state through setState. But this is limited to class components, so before Hooks, functional components were used to render components (also known as puppet components), and class components were used to control state

Then, in order to make the state reuse better, a Mixins ,render props and High order component . Admittedly, although render props and high-level components can be solved, they will bring side effects - components will form a "nested hell"

And the life cycle of class components will make complex components difficult to understand, the learning cost of class syntax and so on, which constitute hooks proposed by React team - let functional components have state management

Official website Three motivations for designing Hooks have also been described:

  1. Reusing state logic between components is difficult
  2. Complex components become difficult to understand
  3. Incomprehensible class

Experiment of state reuse

Mixins Era

It existed before the author used React and has been eliminated

Mixins is a way to extend the collection function. In essence, it copies the properties of one object to another object. However, you can copy any method of any number of objects to a new object, which cannot be realized by inheritance. Its appearance is mainly to solve the problem of code reuse

It is not analyzed here. The official document of React is in Mixins Considered Harmful The article mentioned the harm brought by Mixins:

  • Mixins may be interdependent and coupled, which is not conducive to code maintenance
  • Methods in different Mixins may conflict with each other
  • When there are many Mixins, components can be perceived and even need to be processed, which will cause snowball complexity to the code

Render Props

A simple technique for sharing code between React components using a prop whose value is a function

The component with render prop accepts a function that returns the React element, and calls this function inside the component to implement its own rendering logic

<DataProvider render={data=> (
    <h1>Hello, {data.target}</h1>
)}>

Details can be found in Official website understand

HOC (high order component)

The principle of HOC is actually very simple. It is a function, and it accepts a component as a parameter and returns a new component. Put the reused place in the high-level component. When you use it, you only need to do different uses

For example: just like giving you a bottle of water, you will drink it when you are thirsty; When you're handsome, you POSE with it; You give someone a drink when they need it, help them

Writing is cheap. Show me code

function Wrapper(WrappedComponent) {
    return class extends React.Component {
        componentDidMount() {
            console.log('I am a bottle of water')
        }
        render() {
            return (
                <div>
                    <div className="title">{this.props.title}</div>    
                    <WrappedComponent {...this.props} />
                </div>    
            )
        }
    }
}
import "./styles.css";
import React from "react";
import Wrapper from "./Wrapper";

class A extends React.Component {
  render() {
    return <div>Drink it</div>;
  }
}

class B extends React.Component {
  render() {
    return <div>Play handsome POSE</div>;
  }
}

class C extends React.Component {
  render() {
    return <div>Help others</div>;
  }
}

const AA = Wrapper(A);
const BB = Wrapper(B);
const CC = Wrapper(C);

export default function App() {
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <AA title="I'm an ordinary person" />
      <BB />
      <CC />
    </div>
  );
}

In this way, we can clearly see the benefits of HOC. "A bottle of water" is a common code. A, B and C process business codes, and then transfer a, B and C into HOC (a bottle of water), returning a new component AA, BB and CC. The same code is shared

You can check it here demo

The use of HOC is not only code reuse, but also permission control, log printing, etc. However, its defects are not obvious. When HOC is widely used, it will produce a large number of nesting, which makes nesting difficult; Moreover, HOC will hijack props, which may cause conflict in case of non-compliance with the agreement

To summarize the HOC:

  • Usage: create a function that receives a component as input. In addition to the component, it can also pass other parameters and return a different component based on the component
  • Advantages: code reuse, logic reuse
  • Disadvantages: nesting makes debugging more difficult; Will hijack props and may cause conflict

The birth of Hooks

The former has the power of state reuse (Mixins is suck out, render props and HOC have side effects). After that, complex components of class components are difficult to understand and maintain (excessive life cycle). this attribute caused by class attribute is also troublesome. So Hooks shouted: I'll come too

It has at least three benefits

  • Logical reuse

    • Second kill render props, hoc
  • Business codes are more aggregated

    • Second kill components
  • Concise writing

    • Second kill components

useState

Function: make function components have the ability to maintain state, and replace the constructor initialization state of class components

example:

const Counter = () => {
    const [count, setCount] = useState(0)
    return (
        <div onClick={() => setCount(count+1)}>{count}</div>
    )
}

Features: Logic reuse

When using useState, there are two derivative problems:

1: Capture Value property

stay How are functional components different from class components The values that can be used in the captured rendering function. For example, add three times at the midpoint of the component, pop up the number after setTimeout for 3 seconds, add twice at the midpoint, and display 3 instead of 5 after 3 seconds. But the class component can get the latest data. Why?

Because functional components have the feature of Capture Value. From the perspective of source code, each call to setXX will cause re render to re render the component

If you want to get the latest value, you can save the value in memory through useRef

2: The value in useState is an object. If you change the value in the object, will the component render? How to optimize?

Generally, we use useState to follow a single value as much as possible, but it is inevitable to encounter some special cases. If the value is an object, change one of the attributes in the object and the other attributes remain unchanged, will the components that reference other attributes render?

const DemoSon = (props) => {
  console.log("render", props);
  return <div>{props.name}</div>;
};

const Demo = () => {
  const [data, setData] = useState({ foo: { name: "johan", bar: { baz: 1 } } });
  const handleClick = () => {
    setData({
      ...data,
      foo: {
        ...data.foo,
        bar: {
          baz: 2
        }
      }
    });
  };

  return (
    <div onClick={handleClick}>
      {data.foo.bar.baz}
      <DemoSon name={data.foo.name} />
    </div>
  );
};

Click div and modify the value of baz. Will DemoSon render? The answer is yes, why render? Because your reference value changes, a new virtual DOM is generated. When it is rendered to the view, the sub components will render. How to optimize so that components with unchanged data do not render repeatedly? I think there are two ways: one is to split data into foo object and name. Because setData does not change the name, DemoSon will not render. The other is to wrap DemoSon through memo, because memo can avoid re rendering

Can view online demo

useEffect

Function: handle side effects and replace componentDidMount, componentDidUpdate and componentWillUnmount of class components

Usage:

// No second parameter
// Both the mount phase and the update phase are executed
useEffect(fn)

// The second parameter is an empty array
// When the mount phase is executed
useEffect(fn,[])

// The second parameter is a dependency
// Is executed when dependency (deps) data is updated
useEffect(fn, [deps])

// Eliminate side effects 
useEffect(() => {
  const subscription = props.source.subscribe();
  return () => {
    // Clear subscription
    subscription.unsubscribe();
  };
});

PS: mount stage in the above notes, that is, when the component is loaded; update refers to the change of data (including props)

When using useEffect, you will face several problems:

1. What is the difference between useeffect (FN, []) and componentDidMount?

Although both useEffect(fn, []) and componentDidMount can represent the execution when the component is loaded, they are different in detail. If you want to talk about the details, you need to start from the source code. For details, see this article by the magician Kasong of the source code of React - [what is the difference between use effect (FN, []) and cDM?] ( https://mp.weixin.qq.com/s?__... )Yes, let me talk about my understanding here

In the source code, the rendering of virtual Dom and virtual DOM to real DOM is divided into two stages. The virtual DOM exists in the memory. If the data is added, deleted or modified in JSX, the virtual DOM will label the corresponding data. This stage is called the render stage; The operation of mapping the virtual DOM to the real DOM is called the commit phase, which is responsible for converting these tags into specific DOM operations

In the render phase

  • The inserted DOM element is labeled with Placement;
  • Update DOM elements are labeled with update tags;
  • The deleted DOM element is labeled with Deletion;
  • The update ref attribute is labeled Ref
  • The useEffect callback is labeled Passive

The commit phase is divided into three sub phases

  • Before rendering the view (before mutation stage)
  • When rendering a view (mutation phase)
  • After rendering the view (layout phase)

If it is labeled with Placement, the corresponding appendChild operation will be executed in the mutation stage, which means that the DOM node is inserted into the view, and then the componentDidMount is called in the layout stage

If it is labeled Passive, it will asynchronously call the callback function of useEffect after the execution of the three sub phases of the commit phase

It can be seen that their call timing is different. useEffect(fn, []) is to call the callback function asynchronously after the execution of the commit phase, while componentDidMount will be called synchronously in the layout phase after the view update (mutation phase) is completed in the commit phase

There is also a hooks in hooks that is called at the same time as componentDidMount - uselayouteeffect

Secondly, useEffect(fn, []) will capture props and state, while componentDidMount will not. The function using useEffect(fn, []) will get the initial props and state, which is the same as capture value

Conclusion: there are two differences: first, the implementation time is different; 2, useEffect(fn, []) will capture props and state

The capture value feature will be described below with a demo

2. Each rendering has its own props and state

Let's talk about rendering first. Let's take a look at a Counter component, Counter

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>click {count} second</p>
      <button onClick={() => setCount(count + 1)}>
        click
      </button>
    </div>
  );
}

When rendering for the first time, the initial value of count is obtained from useState(0). When setCount(count + 1) is called, React re renders the component, and the value of count becomes 1. As follows:

// Mount first render
function Counter() {
  const count = 0; // The default is obtained from useState
  // ...
  <p>click {count} second</p>
  // ...
}

// Update click once
function Counter() {
  const count = 1; // Modify count through setCount
  // ...
  <p>click {count} second</p>
  // ...
}

// Click Update twice
function Counter() {
  const count = 2; //  Modify count through setCount
  // ...
  <p>click {count} second</p>
  // ...
}

Whenever we update the status, React re renders the component. Get the count state at the moment (snapshot) every rendering

Values are not captured in class components

for instance:

class ClassDemo extends React.Component {  state = {    count: 0  };  componentDidMount() {    setInterval(() => {      this.setState({ count: this.state.count + 1 });    }, 1000);  }  render() {    return <div>I am Class Component, {this.state.count}</div>;  }}

The count on the page will be incremented by 1 every second and replaced with functional components

const FunctionDemo = () => {  const [count, setCount] = useState(0);  useEffect(() => {    const id = setInterval(() => {      setCount(count + 1);    }, 1000);    return () => clearInterval(id);  }, []);  const handleClick = () => {    setCount(count + 1);  };  return <div onClick={handleClick}>I am Function Component, {count}</div>;};

Always 1

This is the capture value of hooks. Similar examples are in How are functional components different from class components Introduced

Accessible Online demo see

useLayoutEffect

Effects: side effects of synchronous execution

In most cases, using useEffect can help us deal with side effects, but if you want to synchronously call some side effects, such as DOM operations, you need to use useLayoutEffect. The side effects in useLayoutEffect will be executed synchronously after DOM update

Consistent with the componentDidMount effect in class components, they are synchronously called in the layout phase after the view update (mutation phase) is completed in the commit phase

useCallback

Function: memorize functions and avoid function regeneration. When a function is passed to a subcomponent, you can avoid repeated rendering of the subcomponent

example:

const memoizedCallback = useCallback(  () => {    doSomething(a, b);  },  [a, b],);

Cacheable references

In class components, the binding problem of this often puzzles people

  1. Use bind in render method
class App extends React.Component {    handleClick() {        console.log('this > ', this);    }    render() {        return (            <div onClick={this.handleClick.bind(this)}>test</div>        )    }}
  1. Use arrow function in render method
class App extends React.Component {    handleClick() {        console.log('this > ', this);    }    render() {        return (            <div onClick={e => this.handleClick(e)}>test</div>        )    }}
  1. bind in constructor
class App extends React.Component {    constructor(props) {        super(props);        this.handleClick = this.handleClick.bind(this);    }    handleClick() {        console.log('this > ', this);    }    render() {        return (        <div onClick={this.handleClick}>test</div>        )    }}

4. Use arrow function binding in the definition phase

class App extends React.Component {    handleClick = () => {        console.log('this > ', this);    }    render() {        return (            <div onClick={this.handleClick}>test</div>        )    }}

The first three will trigger rendering again due to changes in props or state of App components to render new handleclicks. The fourth method takes handleClick out and assigns it as a variable, and points to the storage function through this, which plays the role of cache

Functional components will render

function App() {    const handleClick = () => {        console.log('Click');    }    return (        <div onClick={handleClick}>test</div>    )}

But useCallback can cache the function and make it "remember"“

function App() {    const handleClick = useCallback(() => {        console.log('Click');    }, [])    return (         <div className="App">          <Demo handleClick={handleClick} />        </div>    )}

But useCallback must use shouldComponentUpdate or react Memo to ignore the same parameters and render repeatedly

Therefore, useCallback cannot be used alone. It needs to be connected with react Memo fit

function Demo(props) {  return <div onClick={props.handleClick}>test</div>;}const MemoDemo = memo(Demo);function App() {    const handleClick = useCallback(() => {        console.log('Click');    }, [])    return (         <div className="App">          <Demo handleClick={handleClick} />        </div>    )}

However, useCallback will make the code less readable, so try not to use it

How to improve performance without useCallback?

useMemo

Function: memory component. Replace shouldComponentUpdate of class components

The function of useCallback can be replaced by useMemo. If you want to return a memory function through useMemo, it is also possible

useCallback(fn, deps) is equivalent to usememo (() = > FN, DEPs)

example:

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

By default, if the React parent component is re rendered, all of its child components will be re rendered, even if the child components have not changed

The parameters accepted by useMemo and useCallback are the same. They are executed after their dependencies change and return cached values. The difference is that useMemo returns the result of function operation and useCallback returns the function

useMemo returns a value to avoid expensive calculations at each render

useCallback VS useMemo

Similarities: useCallback and useMemo are both means of performance optimization, similar to shouldComponentUpdate of class components. shouldComponentUpdate is used in sub components to judge whether the props and state of the component have changed, so as to avoid re rendering the sub components every time the parent component renders

Difference: the difference between useCallback and useMemo is that useCallback returns a function. When this function is used as a child component, it can avoid re rendering this child component every time the parent component is updated

memo

Effect: avoid re rendering

Sub components are re rendered only when props changes

After being wrapped by memo, when props remains unchanged, the sub components will not render

React. The memo () method can prevent sub components from rendering unnecessarily, thus providing component performance.

About performance optimization, Dan once wrote an article: Before you write memo() In fact, before we use useCallback, useMemo and memo, we might as well try moving down the state and improving the content. The purpose is to make components that do not need to be rendered do not render repeatedly

useRef

effect:

Save reference values, similar to createRef. We are used to saving DOM with ref

Using useRef to save and update some data has certain advantages. It can save data without memory, so that these data will not be cleared when re rendering

It is not only used to manage DOM ref, but also equivalent to this. It can store any variable and solve the inconvenience caused by closures

If we want to use ordinary variables to track data changes during re rendering, it is not feasible, because it will be re initialized every time the component is rendered. However, if ref is used, the data in it can remain unchanged every time the component is rendered.

example:

const [count, setCount] = useState<number>(0)const countRef = useRef<number>(count)

stay How are functional components different from class components Introduced the use method

other

useContext: reduce component level

useReducer: a method similar to redux. useState is based on its expansion

ForwardRef: forward ref

useImperativeHandle: transparently pass Ref, and the parent component gets the methods in the child component

Customize Hooks

Because useState and useEffect are function calls, we can combine them into our own Hooks

function MyResponsiveComponent() {    const width = useWindowWidth();    return (        <p> Window width is {width}</p>        )}function useWindowWidth() {    const [width, setWidth] = useState(window,innerWidth);    useEffect(() => {        const handleResize = () => setWidth(window.innerWidth)        window.addEventListener('resize', handleResize)        return () => {            window.removeEventListener('resize', handleResize)        }    })    return width;}

Custom Hooks allow different components to share reusable state logic. Note that the state itself is not shared. Each call to Hook only declares its own independent state

Deficiency of React Hooks

Although the functions of most class components are implemented, the two API s getSnapshotBeforeUpdate and componentDidCatch cannot be implemented

Appendix: rules of use

Hooks is essentially a JavaScript function, which should be followed when using it Two rules

Use Hook only at the top

Do not call Hook in loops, conditions or nested functions, make sure that they are always invoked before the top level of your React function and any return. By following this rule, you can ensure that hooks are called in the same order in each rendering. This allows react to maintain the correct hook state between multiple useState and useEffect calls

Call Hook only in React function

Instead of calling Hook in the ordinary JavaScript function, you can:

  • Calling Hook in the function component of React
  • Call other Hook in custom Hook

reference material

Keywords: Javascript Front-end React hooks

Added by pppppp on Fri, 04 Mar 2022 06:29:40 +0200