A preface

First ask you a few questions, these questions are I was really asked in the interview, true to give me the whole won’t….

  • writehooksCompared to writing class components,hooksWhat are the advantages?
  • How do we encapsulate onehook?
  • hooksHow does it work?

Although the interview cool, but learning still have to continue 😭

Learn more about hooks

useState

  • use

UseState is a simple way to use 🙉, which returns an array with the value of the current state and a function to update the state. The argument to useState is a variable, object, or function. The variable or object is used as the initial value of state, or if it is a function, the return value of the function is used as the initial value.

  • Batch update

Look at the code below

function Count(){
    let [count,setCount] = useState(0)
    const handleAdd = function(){
        setCount(count+1)
        setCount(count+1)}return(
        <div>
            <p>{count}</p>/* Each click adds 1*/<button onClick={handleAdd}>Gal.</button>
        </div>)}Copy the code

If setCount is called twice in the same event, it will not increase the count twice. If setCount is called in the same event every time, then the setCount component will be re-rendered every time, which will undoubtedly affect performance. In fact, if the changed state is the same, the new state in the last setCount function overrides the previous one

useEffect && useLayoutEffect

  • use

The first parameter is a callback function, the second parameter is an array, the contents of the array is the dependency deps, the dependency changes after the callback function; Note that the component is executed once per render by default. If the second argument is not passed, the callback is triggered whenever the state of the component changes. If an empty array is passed, the callback is executed only once during initialization. In addition, if a function is returned with a return, the component will execute that function before calling the callback each time it is rerendered.

  • The difference between

The useEffect callback is executed after the page has been rendered. UseLayoutEffect is executed before the page renders. React handles the two hooks differently. UseEffect is an asynchronous call while useLayoutEffect is a synchronous call. So when do you use useEffect and when do you use useEffect? If the callback changes state and causes the component to re-render, use elayouteffect because useEffect may cause the page to flicker. UseEffect is recommended if the callback function requests data or js execution takes too long. This is when useLayoutEffect blocks the browser rendering.

useMemo && useCallback

These two hooks can be used for performance optimization to reduce repeated rendering of components; Now let’s see how these two magic hooks work.

  • uesMemo
function MemoDemo() {
    let [count, setCount] = useState(0);
    let [render,setRender] = useState(false)
    const handleAdd = () = > {
        setCount(count + 1);
    };
    const Childone = () = > {
        console.log("Child component one is rerendered.");
        return <p>A child components</p>;
    };
    const Childtwo = (props) = > {
        return (
            <div>
                <p>Child component 2</p>
                <p>Count is {props. Count}.</p>
            </div>
        );
    };
    const handleRender = () = >{
        setRender(true)}return (
        <div style={{display:"flex",justifyContent:'center',alignItems:'center',height:'100vh',flexDirection:'column'}} >
            {
                useMemo(() => {
                    return <Childone />
                }, [render])
            }
            <Childtwo count={count} />
            <button onClick={handleAdd}>increase</button>
            <br/>
            <button onClick={handleRender} >Subcomponent - Render</button>
        </div>
    );
}
Copy the code

Childone components will only be re-rendered if render changes

By the way, the react. memo is used for each rendering of a component that uses the react. memo package. The component is not rendered if there is no change; The sample is as follows

const Childone = React.memo((props) = > {
    console.log("Child component one is rerendered.",props);
    return <p>Subcomponent 1 {props. Num}</p>;
})
function MemoDemo() {
    let [count, setCount] = useState(0);
    let [render,setRender] = useState(false)
    let [num,setNum] = useState(2)
    const handleAdd = () = > {
        setCount(count + 1);
    };
   
    const Childtwo = (props) = > {
        return (
            <div>
                <p>Child component 2</p>
                <p>Count is {props. Count}.</p>
            </div>
        );
    };
    const handleRender = () = >{
        setRender(true)}return (
        <div style={{display:"flex",justifyContent:'center',alignItems:'center',height:'100vh',flexDirection:'column'}} >
            {/* {
                useMemo(() => {
                    return <Childone />
                }, [render])
            } */}
            <Childone num={num}/>
            <Childtwo count={count} />
            <button onClick={handleAdd}>increase</button>
            <br/>
            <button onClick={handleRender} >Subcomponent - Render</button>
        </div>
    );
}

Copy the code

This example is the result of removing the Childone from the previous example and putting the react. memo on it. If you click Add, the component will not render again because num has not changed

  • useCallback

Again, we wrap the handleRender with useCallback, that is, num does not change and the same function is passed every time. If we do not wrap the handleRender with useCallback, a new handleRender is generated every time. After a shallow comparison of functions in the react. memo function, a new function was generated, triggering the render

const Childone = React.memo((props) = > {
    console.log("Child component one is rerendered.",props);
    return <p>Subcomponent 1 {props. Num}</p>;
})
export default function MemoDemo() {
    let [count, setCount] = useState(0);
    let [render,setRender] = useState(false)
    let [num,setNum] = useState(2)
    const handleAdd = () = > {
        setCount(count + 1);
    };
   
    const Childtwo = (props) = > {
        return (
            <div>
                <p>Child component 2</p>
                <p>Count is {props. Count}.</p>
            </div>
        );
    };
    const handleRender = useCallback(() = >{
        setRender(true)
    },[num])
    return (
        <div style={{display:"flex",justifyContent:'center',alignItems:'center',height:'100vh',flexDirection:'column'}} >
            {/* {
                useMemo(() => {
                    return <Childone />
                }, [render])
            } */}
            <Childone num={num} onClick={handleRender}/>
            <Childtwo count={count} />
            <button onClick={handleAdd}>increase</button>
            <br/>
            <button onClick={handleRender} >Subcomponent - Render</button>
        </div>
    );
}
Copy the code

useRef

This hook is usually used to fetch component instances, but can also be used to cache data ❗ Fetching instance is not explained, it is important to note that only class components have instances; Take a look at how useRef caches data:

function UseRef() {
    let [data, setData] = useState(0)
    let initData = {
        name: 'lisa'.age: '20'
    }
    let refData = useRef(initData)   //refData is declared and the component is rendered again without reassigning the initial value
    console.log(refData.current);
    refData.current = {       // The page will not be re-rendered after refData is modified
        name: 'liyang '.age: '18'
    }
    const reRender = () = > {
        setData(data + 1)}return (
        <div>
            <button onClick={reRender}>Click re-render component</button>
        </div>)}Copy the code

After the component is rerendered, the variables will be reassigned, and you can cache the data with useRef. This data change will not trigger the component to rerender. If you save the data with useState, it will cause the component to rerender, so we want to save the data quietly

Custom hook

A custom hook is a wrapper around a hook. The React website has the answer

One of the goals of using hooks is to solve the problem that class lifecycle functions often contain unrelated logic, but separate the related logic into several different methods. With custom hooks, component logic can be extracted into reusable functions.

Take a look at this example:

export default function CustomHook() {
    let refone = useRef(null)
    let X, Y, isMove = false,left,top
    // Implement drag based on mouse events
    useEffect(() = > {
        const dom = refone.current
        dom.onmousedown = function (e) {
            isMove = true
            X = e.clientX - dom.offsetLeft;
            Y = e.clientY - dom.offsetTop;
        }
        dom.onmousemove = function (e) {
            if (isMove) {
                left = e.clientX - X
                top = e.clientY - Y
                dom.style.top = top + "px"
                dom.style.left = left + "px"
            }

        }
        dom.onmouseup = function (e) {
            isMove = false}}, [])return (
        <div style={{ display: "flex", justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
            <div ref={refone} style={{ width: '70px', height: '70px', backgroundColor: '#2C6CF9',position:'absolute' }}></div>
        </div>)}Copy the code

We have a simple drag-and-drop effect using mouse events. What if we need this effect on other pages? 😏 We can then consider encapsulating this same piece of logic as we extract common components. Consider the following example:

import {useEffect, useRef } from "react";
function useDrop() {
    let refone = useRef(null)
    let X, Y, isMove = false,left,top
    // Implement drag based on mouse events
    useEffect(() = > {
        const dom = refone.current
        dom.onmousedown = function (e) {
            isMove = true
            X = e.clientX - dom.offsetLeft;
            Y = e.clientY - dom.offsetTop;
        }
        dom.onmousemove = function (e) {
            if (isMove) {
                left = e.clientX - X
                top = e.clientY - Y
                dom.style.top = top + "px"
                dom.style.left = left + "px"
            }

        }
        dom.onmouseup = function (e) {
            isMove = false}}, [])return refone
}
export default function CustomHook() {
    let refone = useDrop()
    let reftwo = useDrop()
    return (
        <div style={{ display: "flex", justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
            <div ref={refone} style={{ width: '70px', height: '70px', backgroundColor: '#2C6CF9',position:'absolute' }}></div>
            <div ref={reftwo} style={{ width: '70px', height: '70px', backgroundColor: 'red',position:'absolute' }}></div>
        </div>)}Copy the code

In order to reduce the amount of code, it only shows the use of encapsulated hook in the same page. In fact, we only changed a few lines of code to encapsulate this hook, but realized the reuse of logic is not amazing! 😆 It should be noted that the encapsulation function of hook must start with use, because there are rules to use hook itself, such as hook cannot be used in conditional statements, hook cannot be used outside functions; React cannot automatically check whether the hooks used in the function comply with the rules if the use prefix is not used to encapsulate the hook.

Four summarizes

There are still some hooks not mentioned in this article, but it doesn’t matter that it is easy to learn these hooks before starting other hooks; I was prepared to talk about the principle of hook again, but react source code is too difficult to chew, a variety of articles read also failed to understand, can only say next time must……