(Reading guide: It is recommended that you perform each small demo manually to make sure it’s worth it.)
A Hook is a new feature in React 16.8 (including React-DOM 16.8). It allows you to use state and other React features without writing a class.
React-router has Hook methods and support since V5.1.
React Redux supports the Hook API since V7.1.0 and exposes hooks such as useDispatch and useSelector.
Next detailed talk about each API, and give a demo, feel good that click 👍 to encourage.
Personally, I strongly recommend: the eye over a thousand times, as the hand over.
1. Hook API is broken separately
1, useState
UseState is used to initialize the state parameter
import React from 'react'; Const [count, setCount] = useState(0); function Example(){const [count, setCount] = useState(0); Click the return (< div > < p > {count} time < / p > < button onClick = {() = > setCount (count + 1)} > click < / button > < / div >)}Copy the code
UseState function:
-
Read state: this.state.count in class, count in class;
-
Update state: In class, the count value is updated with this.setstate (), where setCount is used;
-
Use structure assignment to create two variables count and setCount, equivalent to the following:
var demoData = useState(0); var count = demoData[0]; Var setCount = demoDate[1]; // The second value is a function to update stateCopy the code
2, useEffect
UseEffect performs side effects (hook functions) in function components
UseEffect (() => {}, [])
Takes two arguments, the first function and the second array;
import React, { useState, useEffect } from 'React'; function Example(){ const [count, setCount] = useState(0); const btnClick = () => { setCount(count + 1); } useEffect(() => {console.log(' useEffect'); <div> <p> <button onClick={() => {btnClick()}}> </button> </div> ) }Copy the code
UseEffect Hook (componentDidMount, componentDidUpdate, componentWillUnmount)
UseEffect is executed after each page rendering. By default, it is executed after the first render and after every update; Multiple Useeffects can occur in the same function component at the same time. React calls each effect in the component in the order in which it is declared.
Effect that does not need to be cleaned
Class, that is, there is no need to perform operations in componentWillUnmount
ComponentDidMount (){document.title = 'hit {count} times'; } componentDidUpdate(){document.title = 'hit {count} times'; }Copy the code
The hook method
UseEffect (() => {document.title = '{count} times'; })Copy the code
Effects that need to be cleared
That is, it needs to be cleared in componentWillUnmount, for example we use setInterval to update the current time. The following code
class FriendStatus extends React.Component{ constructor(props){ super(props); this.state = { nowTime: null}; this.timer = null; } componentDidMount(){ this.timer = setInterval(() => { this.setState({ nowTime: new Date() }) }, 1000) } componentWillUnmount(){ if (this.timer ! == null) { clearInterval(this.timer); } } render(){ let time = this.state.nowTime; Return (<div>{time.toString()}</div>)}}Copy the code
Use hook as follows:
import React, { useState, useEffect } from 'react'; function FriendStatus(props) { const [nowTime, setNowTime] = useState(new Date()); useEffect(() => { let timer = setInterval(() => { setNowTime(new Date()) }, 1000) return () => {// Return a cleanup function clearInterval(timer); }; } []); return(<div>{nowTime.toString()}</div>) }Copy the code
Optimize performance by skipping effects
We know that cleaning up or executing an effect every time after rendering can cause performance problems. In class we add prevProps and prevState comparison logic to componentDidUpdate.
componentDidUpdate(prevProps, prevState){ if(this.state.count ! == prevstate.count){document.title = 'hit {this.state.count} times'}}Copy the code
The syntax for replacing an effect in hook is as follows:
UseEffect () => {console.log(' done --useEffect') document.title = 'clicked {count} times'; }, [count]); // Update when first render and count changesCopy the code
If the second argument is an empty array [], Effect is executed once in the first render and again with the cleanup function Effect (you can replace [count] with [] in the above code test); Here is the code to execute the two cases:
UseEffect returns a function in the first argument (function) without useEffect + the second argument []; // --> componentDidMount; UseEffect (() => {console.log(' performed --useEffect'); Document. title = '${count} times'; } []); // with clear function + the second argument is []; ComponentDidMount = componentWillUnmount (); UseEffect (() => {console.log(' performed --useEffect'); Document. title = '${count} times'; Return () => {// equivalent to componentWillUnmount; Console. log(' executed -- clean function '); document.title = ""; }}, [])Copy the code
Without the second argument, Effect is executed on every update in addition to the first render.
3, useContext
Receives a context object (defined here as Mycontext) created by react.createcontext () and returns the value of the context property vaule binding; Use useContext to get the value passed by props of the latest < myContext. Provider value=””>. As shown in the following usage:
// Mycontext is the return value of react.createcontext const Mycontext = react.createcontext (); <Mycontext.provider value={}> ... </Mycontext.provider> -------------------------- const value = useContext(Mycontext);Copy the code
Components that call useContext are always rerendered when the context value changes.
The full demo is as follows:
import React, { useContext } from 'react'; const themes = { light: { color: '#ddd', background: 'yellow' }, dark: { color: '#fff', background: '#333' } } const ThemeContext = React.createContext(); function TestContext(){ return ( <ThemeContext.Provider value={themes.dark}> <Father></Father> </ThemeContext.Provider> ) } function Child(){ const theme = useContext(ThemeContext); return ( <div style={{color: theme.color, backgroundColor: UseContext </div>)} function Father(){return (<div> <Child/> </div>)} export default TestContext;Copy the code
4, useReducer
Grammar:
const [state, dispatch] = useReducer(reducer, initalArg, init);
Copy the code
Is an alternative to useState. In some scenarios, useReducer is more appropriate than useState. (If you’re familiar with Redux, you know how it works)
Complete the demo:
import React, { useReducer } from 'react';
const init = {
count: 0
};
function reducer(state, action){
switch(action.type){
case 'add':
return {count: state.count + 1};
case 'minus':
return {count: state.count - 1};
default: throw new Error();
}
}
function TestReducer(){
const [state, dispatch] = useReducer(reducer, init);
return (
<div>
count: {state.count}
<ul>
<li><button onClick={() => dispatch({type: 'add'})}>+</button></li>
<li><button onClick={() => dispatch({type: 'minus'})}>-</button></li>
</ul>
</div>
)
}
export default TestReducer;
Copy the code
5, useRef
Grammar:
const refObj = useRef(initVal);
Copy the code
UseRef returns a mutable ref object whose.current property is initialized as the passed parameter (initValue). The ref object returned remains constant throughout the life of the component;
Complete the demo:
import React, { useRef } from 'react'; export default function TestUseRef(){ // const InputEl = React.createRef(null); const InputEl = useRef(null); Const getInputFocus = () = > {InputEl. Current. Placeholder = "input"; InputEl.current.focus(); } return (<div> <input ref={InputEl} type="text" placeholder=" placeholder "/> UseRef </button> </div>)}Copy the code
Tests found the same function as react.createref ().
6, useCallback
Syntax: Returns a callback function;
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b])
Copy the code
UseCallback (fn, deps) is equivalent to useMemo(() => fn, deps), which is updated only when a dependency changes.
Complete the demo:
import React, { useState, useCallback, useEffect } from 'react'; function TestUseCallback(){ const [ count, setCount ] = useState(0); const [ inputVal, setInputVal ] = useState(''); const backCount = useCallback(() => { return count; }, [count]); Return (<div> <button onClick={() => {setCount(count + 1)}}>Click me-- </button> <input value={inputVal} onChange={(e) => setInputVal(e.target.value)} /> <hr/> <Child count={count} callback={backCount}/> </div> ) } function Child(props){ const {count, callback} = props; const [mycount, setMycount] = useState(() => callback()); useEffect(() => { setMycount(callback()); }, [callback]); Return (<div> <h3> Child component </h3> <p>myCount-- {myCount}</p> </div>)} export default TestUseCallback;Copy the code
7, useMemo
UseMemo takes a custom function and array arguments and only returns the value and triggers render if one of the array arguments changes. This optimization helps avoid costly calculations on every render;
Syntax: Returns a value;
const memoizedValue = useMemo(() => computedExpensiveValue(a, b),[a,b])
Copy the code
Full demo without useMemo:
import React, { useState, useMemo } from 'react'; function TestUseMemo(){ const [count, setCount] = useState(0); const [value, setValue] = useState(''); const AddSum = () => { console.log('addSum'); let sum = 0; for(let i = 0; i < count*1000; i++){ sum += i; } return sum; } the return (< div > < p > Click the {count} time < / p > < p > calculate summation proceeds {AddSum ()} < / p > < button onClick = {() = > {setCount (count + 1)}} > Click me</button> <input value={value} onChange={event => setValue(event.target.value)}/> </div> ) } export default TestUseMemo;Copy the code
The AddSum() function is triggered when either count or value is changed.
Full demo using useMemo:
import React, { useState, useMemo } from 'react'; function TestUseMemo(){ const [count, setCount] = useState(0); const [value, setValue] = useState(''); const AddSum = useMemo(() => { console.log('useMemo'); let sum = count; for(let i = 0; i < count*1000; i++){ sum += i; } return sum; }, [count]); Return (<div> <p> <p> < AddSum}</p> <button onClick={() => {setCount(count +) 1)}}>Click me</button> <input value={value} onChange={event => setValue(event.target.value)}/> </div> ) } export default TestUseMemo;Copy the code
Using useMemo, we found that AddSum is re-computed and re-rendered only when the count value changes, thus avoiding unnecessary performance overhead.
8, useLayoutEffect
The syntax is the same as useEffect, but the execution timing is different.
Complete demo verification:
import React, { useState, useEffect, useRef, useLayoutEffect } from 'react'; export default function TestuseLayout(){ const [count, setCount] = useState(0); const [text, setText] = useState(''); const InputRef = useRef(null); useEffect(() => { console.log(InputRef, 'start useEffect'); document.title = `${count} times`; return () => { console.log(InputRef, 'end useEffect'); document.title = 'remove'; }}); useLayoutEffect(() => { console.log(InputRef, 'start useLayoutEffect'); document.title = `${count} times`; return () => { console.log(InputRef, 'end useLayoutEffect'); document.title = 'Layout remove'; } }) return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}>Click me</button> <p> <input ref={InputRef} value={text} onChange={(e) => setText(e.target.value)}/> </p> </div> ) }Copy the code
The execution result is as follows:
Conclusion:
- UseLayoutEffect is executed before useEffect;
- The return function of the first function argument executes before the initial function;
9, useImperativeHandle
UseImperativeHandle allows you to expose custom instance values to parent components when using refs. Avoid using refs. UseImperativeHandle should be used with the forwardRef.
Grammar:
useImperativeHandle(ref, createHandle, [deps]);
Copy the code
Complete the demo:
import React, { useImperativeHandle, useRef, forwardRef} from 'react'; function TestuseImper(){ const childRef = useRef(null); const btnFn = () => { console.log('childRef=', childRef); childRef.current.focus(); } return (<div> <button onClick={() => {btnFn()}}> </button> <hr/> <Child ref ={childRef}/> </div>)} function Child(props, ref){ const InputRef = useRef(); useImperativeHandle(ref, () => ({ focus: () => InputRef.current.focus(); })) return ( <div> <input ref={InputRef}/> </div> ) } Child = forwardRef(Child); export default TestuseImper;Copy the code
After the demo above, we found that the parent component could get the ref of the child component from the exposed ref and modify it.
10, useDebugValue
Grammar:
useDebugValue(value)
Copy the code
Complete the demo:
import React, { useState, useDebugValue } from 'react'; export default function TestuseLayout(){ function useMyCount(num) { const [ count, setCount ] = useState(0); useDebugValue(count > num ? 'overflow' : 'insufficient '); const myCount = () => { setCount(count + 2); } return [ count, myCount ]; } const [count , myCount] = useMyCount(10); return ( <div> <p>You clicked {count} times</p> <button onClick={() => myCount(count + 1)}>Click me</button> </div> ) }Copy the code
———————————————————
2. Custom Hook
Custom hooks can reuse some state logic between components. In class mode, they are usually implemented by higher-order components. Custom hooks can achieve the same purpose without adding components, by extracting component logic into reusable functions. Here is a complete demo of a simple custom hook:
import React, { useState } from 'react'; Function useChangeCount(init){const [count, setCount] = useState(init); const plus = () => { setCount(count + 1); } const minus = () => { setCount(count - 1); }; const reset = () => { setCount(init); }; return [count, {plus, minus, reset}]; } function SelfHook(){ const [count, setCount] = useChangeCount(0); Return (<div> <p> current count: {count}</p> <div><button onClick={setCount. Plus}> Add </button></div> <div OnClick ={setcount.minus}> Reduce </button></div> <div><button onClick={setcount.reset}> reset </button></div> </div>)} export default SelfHook;Copy the code
For more custom hooks, see react-use
Iii. Hook Usage rules
Use hooks only at the top level
Do not call hooks in loops, conditions, or nested functions;
Only the Hook is called in the component function
Where the hook is called
- Call a hook in a function component
- Call a hook in a custom hook
Version update
Note: To enable Hook, all React related packages must be upgraded to version 16.8.0 or higher. Otherwise, the Hook will not work.
The resources
The official React – Hook
Hooks FAQ