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:
- The suboptions are not fully selected
- The secondary option is full or the primary option is selected
- 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