Arrangement of hooks components

So let’s do a counter example, unoptimized

import React, { useState } from 'react';

const Header = ({ title }) = > {
    console.log('header render')
    return (
        <div>
            {title}
        </div>)}const Footer = () = > {
    console.log('footer render')
    return (
        <div>The bottom of the page</div>)}export default function Home() {

    const [num, setNum] = useState(0);

    console.log('render')

    return (
        <div>
            <Header title={'Header '} />
            <div>
            	<span>{num}</span>
            	<button onClick={()= >SetNum (v => v + 1)}> Click</button>
        		</div>
            <Footer />
        </div>
    );
}
Copy the code

Here, clicking on the button causes num to change to re-render the header and footer child components, resulting in excess render performance loss.

One of the correct ways to write this (wrap components via Memo or useMemo)

import React, { useState, useMemo, memo } from 'react';

const Footer = memo(() = > {
    console.log('footer render')
    return (
        <div>The bottom of the page</div>)})const Head = ({ title }) = > {
    console.log('header render')
    return (
        <div>
            {title}
        </div>)}export default function Home() {

    const [num, setNum] = useState(0);

    console.log('render')

    const Header = useMemo(() = > <Head title={'Header '} />[]),return (
        <div>
            {Header}
            <div>
                <span>{num}</span>
                <button onClick={()= >SetNum (v => v + 1)}> Click</button>
            </div>
            <Footer />
        </div>
    );
}
Copy the code

We all know that the React component will re-render according to changes in the state and props properties (passed by the parent component). The memo or useMemo package will automatically make a shallow comparison to avoid unnecessary rendering.

(2) The idea is to reduce unnecessary rendering of components and do less work, and separate them into separate sub-components.

import React, { useState } from 'react';

const Header = ({ text }) = > {
    console.log('header render')
    return (
        <div>
            {text}
        </div>)}const Footer = () = > {
    console.log('footer render')
    return (
        <div>The bottom of the page</div>)}const Content = () = > {
    const [num, setNum] = useState(0);

    console.log('content render')

    return (
        <div>
            <span>{num}</span>
            <button onClick={()= >SetNum (v => v + 1)}> Click</button>
        </div>)}export default function Home() {

    console.log('render')

    return (
        <div>
            <Header text={'Header '} />
            <Content />
            <Footer />
        </div>
    );
}
Copy the code

Use useReducer instead of useState properly

Sometimes we will have a situation where setState is needed two or three times in a function, but the biggest difference between setState in Hooks and Class is that the setState in Hooks overwrites the original value, so extenders are needed

One solution is to merge state operations, as shown below

	/ / pseudo code
   const [param,setParam] = useState({
          name:' '.age:' '
   })
       
   const onChange = () = > {
   		setParam(state= > ({
        	...state,
            name:'Alan'.age:20}}))Copy the code

But such a practice is not a universal solution, when encountered to deal with complex logic can consider using useReducer, for example, the need to achieve functions: a simple implementation of multiple check boxes, when other options are selected, the main option to be selected, the main option can control whether other options are selected.

So let’s see what I’m doing here

/ / pseudo code
const [checkBoxParam, setCheckBoxParam] = useState({
    // Primary and secondary options
    primaryCheckBox: false.secondCheckBox: []})// The id of all checkboxes
const allCheckBoxId = [1.2.3]

// When selecting the sub-option
const onHandleClick = (checkBoxArray = []) = > {
    const { primaryCheckBox, secondCheckBox } = checkBoxParam;
    // If the suboptions are full
    if (checkBoxArray.length === allCheckBoxId.length) {
        setCheckBoxParam(param= > ({
            ...param,
            primaryCheckBox: true.secondCheckBox: allCheckBoxId
        }))
    } else {
        setCheckBoxParam(param= > ({
            ...param,
            primaryCheckBox: false.secondCheckBox: checkBoxArray
        }))
    }
}

// When selecting the main option
const onPrimaryClick = (bool) = > {

    if (bool) {
        setCheckBoxParam(param= > ({
            ...param,
            primaryCheckBox: true.secondCheckBox: allCheckBoxId
        }))
    } else {
        setCheckBoxParam(param= > ({
            ...param,
            primaryCheckBox: false.secondCheckBox: []}))}}Copy the code

Looking at the code above, three cases can be broken down:

  1. The suboptions are not fully selected
  2. The secondary option is full or the primary option is selected
  3. The primary selection is reversed

According to the above three cases can be divided into switch case condition judgment, from which analysis can use useReducer

After the optimization

/ / pseudo code

const checkBoxReducer = (state, action) = > {

    switch (action.type) {
        // The options are not fully selected
        case 'second_check':
            return { ...state, checkedPriceList: action.payload, primaryCheckBox: false };
        // The secondary option is full or the primary option is selected
        case 'all_check':
            return { ...state, checkedPriceList: action.payload, primaryCheckBox: true };
        // The main selection is reversed
        case 'un_all_check':
            return { ...state, checkedPriceList: action.payload, primaryCheckBox: false };
        default:
            throw new Error();

    }

}


const [checkBoxParam, dispatchCheckBoxParam] = useReducer(checkBoxReducer, {
    // Primary and secondary options
    primaryCheckBox: false.secondCheckBox: []})// The id of all checkboxes
const allCheckBoxId = [1.2.3]

// When selecting the sub-option
const onHandleClick = (checkBoxArray = []) = > {

    // If the suboptions are full
    if (checkBoxArray.length === allCheckBoxId.length) {
        dispatchCheckBoxParam({ type: 'all_check'.payload: allCheckBoxId });
    } else {
        dispatchCheckBoxParam({ type: 'second_check'.payload: checkBoxArray }); }}// When selecting the main option
const onPrimaryClick = (bool) = > {

    if (bool) {
        dispatchCheckBoxParam({ type: 'all_check'.payload: allCheckBoxId });
    } else {
        dispatchCheckBoxParam({ type: 'un_all_check'.payload: []}); }}Copy the code

Use the useCallback cache function

UseCallback is mainly used as a cache function. The same idea as useMemo is to determine whether the function is updated by the change of the dependency value

function Parent(props) { const [count, setCount] = useState(0); Const addClick = useCallback(() => {let sum = 0; for (let i = 0; i < count; i++) { sum += i; } return sum; }, [count]); const [value, setValue] = useState(""); return ( <div> <h3>UseCallbackPage</h3> <p>{count}</p> <button onClick={() => setCount(count + 1)}>add</button> <input Value ={value} onChange={event => setValue(event.target.value)} /> {/* Passes functions cached using useCallback to child components, <Child addClick={addClick} /> </div>); } const Child = ({addClick}) => { console.log("child render"); return ( <div> <h3>Child</h3> <button onClick={() => console.log(addClick())}>add</button> </div> ); }Copy the code

In the code above, we use useCallback to cache the addClick function and then pass addClick as props to the Child component to avoid updating the Child component when the Parent component is updated.

Use hooks to customize validation forms

First, let’s talk about the requirements of the use scenario. Because the Form component of the small program is difficult to use and modify, we directly write the original components such as input and checkbox, but this time involves multi-value null check and regular check, and the code will be messy if it is not handled well.

import React, {useCallback,useState} from 'react';
import _isEmpty from 'lodash/isEmpty';
import _debounce from 'lodash/debounce';

// List of options to be checked
const validatorList = [
    {
        value: 'phone'.checkFunc: value= > new RegExp(,5,7,8 / ^ [1] [3] [0-9] {9} $/).test(value),
        msg: 'Please fill in the correct mobile phone number'
    },
    {
        value: 'code'.checkFunc: value= >! _isEmpty(value.trim()),msg: 'Please fill in the verification code'}];/ / input box image stabilization
const handleInput = _debounce((value, handle) = > {
    handle(value);
}, 500);

const LoginComponent = () = > {

    const [phone, setPhone] = useState();
    const [code, setCode] = useState();

    // Collect the form values
    const getFormValue = useCallback(() = > {
        const data = {
            phone, code
        };
        return data;
    }, [phone, code])

    // Validates the value of each item in the form
    const checkFormValue = useCallback(() = > {
        const formValue = getFormValue();
        let checkStatus = true;
        for (let index = 0; index < validatorList.length; index++) {
            const rule = validatorList[index];

            if(! rule.checkFunc(formValue[rule.value])) { message.error(rule.msg); checkStatus =false;
                break; }}return checkStatus;
    }, [getFormValue])

    // Submit the form
    const handleSubmit = useCallback(() = > {

        if(! checkFormValue()) {return;
        }

        const data = getFormValue();

        axios.post('/api', data)
            .then(res= > console.log('Return data:' + res))
            .catch(err= > console.log('Return data failed:' + err))
    }, [checkFormValue, getFormValue])

    return (

        <div>
            <input
                name='phone'
                value={phone}
                onchange={v= > handleInput(v, setPhone)}
            />
            <input
                name='code'
                value={code}
                onchange={v= > handleInput(v, setCode)}
            />
            <button onClick={handleSubmit}>determine</button>
        </div>)}Copy the code