(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