1. What is react-hooks?

* * the react – hooks is react16.8, react the new hook API, the purpose is to increase the reusability of the code, logic, make up for the stateless component life cycle, there is no data management status, the defects of the state. In the author’s opinion, the react-hooks’ idea and original intention is to form an independent rendering environment by integrating components, granulation and unitization to reduce rendering times and optimize performance.

UseCallback ✅

UseContext ✅

UseEffect ✅

UseLayoutEffect ✅

UseMemo ✅

UseReducer ✅

UseRef ✅

UseState ✅ These are the main react-hooks apis. I’m going to share with you how to use them and what to do with them.

2. Why do you use hooks

Q: Why do we use react-hooks, first of all, which have significant advantages over traditional class declarations that are stateful

1. React-hooks allow us to make our code more logical, to pull out public methods, public components.

2. React-hooks ideas are much closer to functional programming. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

3. React-hooks can break large class components into small pieces. Methods like useMemo allow components or variables to have their own separate render space, which can improve performance and reduce render times to some extent. It is important to note that the react-hooks, which are responsible for requesting updates to the ➡️ view, will work even better if they are written using react-hooks, which are immutable. Brings all sorts of unexpected problems).

3. How to use hooks

I’m going to talk to you about the react-hooks API

1 useState Data store to distribute updates

The react stateless component can have its own state, just like the stateless component. The argument to the React stateless component can be a specific value, or it can be a function for determining complex logic. The function returns an initial value, and useState returns an array. The first item of the array is used to read the state value at this time, and the second item is the function that dispatches the data update, rendered by the component. The parameter of the function is the value that needs to be updated. UseState and useReduce are hooks that can trigger component re-render. When using useState, we should pay special attention to the fact that the execution of the update function sent by useState will make the entire function component execute once from beginning to end, so we need to cooperate with useMemo. Usecallback, which is one of the reasons WHY overuse of hooks can backfire. The following code is the usestate basic application

const DemoState = (props) = > {
   /* setNumber is the function that sends updates */
   let [number, setNumber] = useState(0) /* 0 is the initial value */
   return (<div>
       <span>{ number }</span>
       <button onClick={() = >{setNumber(number+1) console.log(number) /* Here the number is not able to change even */}} ></button>
   </div>)}Copy the code

The simple example above illustrates useState, but when we call the update function, the value of state is not changed immediately, only the next time the context executes.

const a =1 
const DemoState = (props) = > {
   /* useState the first argument is a function that handles complex logic and returns the initial value */
   let [number, setNumber] = useState(() = >{
      // number
      return a===1 ? 1 : 2
   }) /* 1 is the initial value */
   return (<div>
       <span>{ number }</span>
       <button onClick={() = >setNumber(number+1) } ></button>
   </div>)}Copy the code

The useEffect component updates the side effect hook

If you want to do some manipulation of the DOM and request data in a function component when the component is mounted and the DOM is rendered, then useEffect is a good option. If you want to request data when the component is first rendered, So useEffect can act as componentDidMount in a class component, but it’s important to note that if you don’t qualify useEffect execution, effect will be triggered every time the component is updated, which means that with every state update, Or props update trigger useEffect execution, effect and ACTS as the componentDidUpdate and componentwillreceiveprops, so reasonable for useEffect will give effect to join the limit condition of execution, In this case, useEffect’s second parameter, which is called a limit, is the memory of some changes in the data collected in the last useEffect update. In the next update, useEffect will compare the previous memory with the current value. If there is a change, a new round of useEffect side effects is executed. The second argument to useEffect is an array that collects multiple constraints.

/* Simulate data interaction */
function getUserInfo(a){
    return new Promise((resolve) = >{
        setTimeout(() = >{ 
           resolve({
               name:a,
               age:16,}}),500)})}const Demo = ({ a }) = > {
    const [ userMessage , setUserMessage ] :any= useState({})
    const div= useRef()
    const [number, setNumber] = useState(0)
    /* Simulate event listener handler */
    const handleResize =() = >{}
    /* useEffect (); /* useEffect ()
    useEffect(() = >{
        /* Request data */
       getUserInfo(a).then(res= >{
           setUserMessage(res)
       })
       /* Operate dom */
       console.log(div.current) /* div */
       /* Event listener */
        window.addEventListener('resize', handleResize)
    / * only when the props - > a and the state - > number change, useEffect side effect function to perform, if the array is empty [], that function only at the time of initialization performs a equivalent componentDidMount * /
    },[ a ,number ])
    return (<div ref={div} >
        <span>{ userMessage.name }</span>
        <span>{ userMessage.age }</span>
        <div onClick={() = > setNumber(1) } >{ number }</div>
    </div>)}Copy the code

If we need to do something during the component destruction phase, such as cancel the DOM listener, clear the timer, etc., then we can return a function at the end of the first argument to useEffect to clear these side effects. Equivalent to componentWillUnmount.

const Demo = ({ a }) = > {
    /* Simulate event listener handler */
    const handleResize =() = >{}
    useEffect(() = >{
       /* Timer delay, etc. */
       const timer = setInterval(() = >console.log(Awesome!),1000)
       /* Event listener */
       window.addEventListener('resize', handleResize)
       /* This function is used to remove side effects */
       return function(){
           clearInterval(timer) 
           window.removeEventListener('resize', handleResize)
       }
    },[ a ])
    return (<div  >
    </div>)}Copy the code

Async effect?

UseEffect cannot be treated directly with async await syntax

/* Error, effect does not support */ decorated with direct async await
 useEffect(async() = > {/* Request data */
      const res = await getUserInfo(payload)
    },[ a ,number ])

Copy the code

If we want to use async Effect we can wrap effect in a layer

const asyncEffect = (callback, deps) = >{
   useEffect(() = >{
       callback()
   },deps)
}
Copy the code

3useLayoutEffect render update before useEffect

UseEffect Execution sequence Component update is mounted -> browser DOM drawing is complete -> Perform the useEffect callback.

UseLayoutEffect execution sequence Component update mount complete -> Execute useLayoutEffect callback -> Browser DOM drawing complete So useLayoutEffect code may block browser drawing if we are in useEffect If you use useLayoutEffect, the code of the callback function will block the browser drawing, so it will cause the screen to stop, and other effects, so to use useLayoutEffect or useEffect. Depending on the actual situation of the project, most of the useEffect can be satisfied.

const DemoUseLayoutEffect = () = > {
    const target = useRef()
    useLayoutEffect(() = > {
        /* We need to move the DOM to the specified position before the DOM is drawn */
        const { x ,y } = getPositon() /* Get the x,y coordinates */ to move
        animate(target.current,{ x,y })
    }, []);
    return (
        <div >
            <span ref={ target } className="animate"></span>
        </div>)}Copy the code

4 useRef Obtains elements and caches data.

Like the traditional class component ref, react-hooks provide useRef, which takes an argument that can be used as an initial value for cached data. The return value can be marked by the DOM element ref, which retrieves the element node that was marked.

const DemoUseRef = () = >{
    const dom= useRef(null)
    const handerSubmit = () = >{
        /* 
      
Form component
DOM node */
console.log(dom.current) } return <div>{/* ref mark the current DOM node */}<div ref={dom} >Form components</div> <button onClick={()= >Submitted handerSubmit ()} ></button> </div> } Copy the code

Higher-order usage caches data

Of course, useRef also has an important role in caching data. We know that usestate and useReducer can save the current data source, but if they update the function execution of the data source, the whole component will be executed from the new rendering. If you declare variables inside the function component, The next update will reset as well, and useRef is a great choice if you want to quietly save data without triggering function updates.

** const currenRef = useRef(InitialData)

Currenref. current = newValue

The first parameter of useRef can be used to initialize the stored data. This data can be retrieved from the current property, or we can assign a new data source to current.

Let’s take a look at the react-redux source code to see how useRef works. As you can see, react-hooks have the advantage of limiting data updates to higher-order components, and their source code makes heavy use of useMemo for data determination

      /* None of the userefs used here are bound to the DOM element, but all are used for data caching */
      /* react-rex_props */
      const lastChildProps = useRef()
      // lastWrapperProps uses useRef to hold the real props for the component
      const lastWrapperProps = useRef(wrapperProps)
      // Whether to save the props to be updated
      const renderIsScheduled = useRef(false)
Copy the code

This is the react-Redux cache using useRef, so how to update the data, we’ll see next


// Get props for the wrapper
function captureWrapperProps(lastWrapperProps, lastChildProps, renderIsScheduled, wrapperProps, actualChildProps, childPropsFromStoreUpdate, notifyNestedSubs) {
   // We want to capture the wrapper props and the child props so we can compare them later
  lastWrapperProps.current = wrapperProps  / / props
  lastChildProps.current = actualChildProps // Merge props to form a prop
  renderIsScheduled.current = false

}
Copy the code

As we can see from the above, React-Redux changes the cache data source by reassigning, so as to avoid unnecessary data update. If useState is used to store data, it will inevitably cause the component to re-render, so useRef is used to solve this problem. As for how to implement the react-Redux source code, we can refer to another article of the author react-Redux source code parsing.

5 useContext Obtains the context freely

We can use useContext to get the value of the context passed by the parent component. The current value is the value set by the most recent parent component Provider. The useContext parameter is usually introduced by the createContext mode and can also be passed by the parent context context (the parameter is context). UseContext can be used instead of context.Consumer to get the value stored by the Provider

/* Use useContext */
const DemoContext = () = > {
    const value:any = useContext(Context)
    /* my name is alien */
return <div> my name is { value.name }</div>
}

/* Use context. Consumer */
const DemoContext1 = () = >{
    return <Context.Consumer>
         {/*  my name is alien  */}
        { (value)=> <div> my name is { value.name }</div> }
    </Context.Consumer>
}

export default() = > {return <div>
        <Context.Provider value={{ name:'alien',age:18 }} >
            <DemoContext />
            <DemoContext1 />
        </Context.Provider>
    </div>
}
Copy the code

6 useReducer Redux in stateless components

UseReducer is an API provided by React-hooks that can run in stateless components like Redux. As for react-redux, I don’t think it can replace redux. Redux has the power to be used in complex logic. Redux’s middleware pattern is also excellent. We can enhance dispatch, redux-Thunk, Redux-Sage, Redux-Action, redux-Promise by means of middleware. You can program a synchronous reducer into an asynchronous reducer. The first argument that useReducer accepts is a function, We can think of it as a reducer, where the parameters are the state and action in the regular reducer, and the reducer returns the changed state. UseReducer’s second parameter is the initial value of state and returns an array. The first item in the array is the value of state after the update, and the second argument is the dispatch function that dispatches the update. The trigger of the dispatch will trigger the update of the component. The one that can cause the component to dispatch the update function from the new rendering is useState, and the other is Dispatch in the useReducer

const DemoUseReducer = () = >{
    /* Number is the updated state value. DispatchNumbner is the current dispatcher function */
   const [ number , dispatchNumbner ] = useReducer((state,action) = >{
       const { payload , name  } = action
       /* The return value is the new state */
       switch(name){
           case 'add':
               return state + 1
           case 'sub':
               return state - 1 
           case 'reset':
             return payload       
       }
       return state
   },0)
   return <div>Current value: {number} {/* Dispatch update */}<button onClick={()= >DispatchNumbner ({name:'add'})} > Is added</button>
      <button onClick={()= >DispatchNumbner ({name:'sub'})} > Reduced</button>
      <button onClick={()= >DispatchNumbner ({name:'reset',payload:666})} > Assignment</button>{/* pass the dispatch and state to the child components */}<MyChildren  dispatch={ dispatchNumbner } State={{ number}} / >
   </div>
}
Copy the code

Of course, the actual business logic may be more complex, so we need to do more complex logical operations in reducer.

7 useMemo small and fragrance performance optimization

In my opinion, useMemo is one of the most sophisticated hooks in React. The advantage of useMemo is that it creates separate render Spaces and allows components and variables to be updated according to conventions. The render condition depends on the second parameter, deps. We know that updates to stateless components are updates from beginning to end. If you want to rerender a portion of the view, not the entire component, then useMemo is the best way to avoid unwanted updates and unnecessary context execution. Let’s start with memo. We know that a class declared component can use componentShouldUpdate to limit the number of updates, so memo is a stateless componentShouldUpdate, And the useMemo we’re going to talk about today is the smaller ShouldUpdate unit,

The memo function combines the functions of pureComponent () and componentShouldUpdate (). The memo function compares the props that are passed in, and then uses the return value of the second function to determine which props need to be updated.

/* If the memo is wrapped in a component, you have a condition that limits the update of the component. If the memo is not updated, the Boolean value returned by the second argument is */
const DemoMemo = connect(state= >
    ({ goodList: state.goodList })
)(memo(({ goodList, dispatch, }) = > {
    useEffect(() = > {
        dispatch({
            name: 'goodList',}}), [])return <Select placeholder={'Please select '}style={{ width: 200.marginRight: 10 }} onChange={(value)= > setSeivceId(value)} >
        {
            goodList.map((item, index) => <Option key={index + 'asd'+item.itemId} value={item.itemId} > {item.itemName} </Option>)}</Select>
    /* Determine if the previous goodList and the new goodList are equal, and if they are, do not update the component so that you can make your own rendering convention, making the component rerender only if it meets a predetermined date */
}, (pre, next) = > is(pre.goodList, next.goodList)))
Copy the code

The second argument to useMemo is an array of deps. The changes in the parameters in the array determine whether useMemo updates the callback function. The useMemo return value is the result of the decision update. It can be applied to elements, components, or contexts. UseMemo would be a good choice if it were a looping list element. Let’s explore the benefits of useMemo

/* A list wrapped in useMemo can be restricted to updating the list if and only if the list changes, thus avoiding selectList recycling */
 {useMemo(() = > (
      <div>{
          selectList.map((i, v) => (
              <span
                  className={style.listSpan}
                  key={v} >
                  {i.patentName} 
              </span>
          ))}
      </div>
), [selectList])}
Copy the code

UseMemo can reduce unnecessary loops and reduce unnecessary rendering

 useMemo(() = > (
    <Modal
        width={'70%} 'visible={listshow}
        footer={[
            <Button key="back" >cancel</Button>.<Button
                key="submit"
                type="primary"
             >determine</Button>} > {/* Reduced rendering of PatentTable component */}<PatentTable
            getList={getList}
            selectList={selectList}
            cacheSelectList={cacheSelectList}
            setCacheSelectList={setCacheSelectList} />
    </Modal>
 ), [listshow, cacheSelectList])
Copy the code

UseMemo can reduce the rendering times of subcomponents

const DemoUseMemo=() = >{
  /* Use useMemo to wrap the log function in useMemo to avoid redeclaring each component update, and to limit context execution */
    const newLog = useMemo(() = >{
        const log =() = >{
            console.log(6666)}return log
    },[])
    return <div onClick={()= >newLog()} ></div>
}
Copy the code

3. UseMemo saves a lot of overhead by making functions run only when a dependency changes. (Note that ⚠️, ⚠️ and ⚠️ cache the previous state value if the context is wrapped in usemo as a separate closure. The value of the updated state cannot be obtained, as shown in 👇⬇️)

const DemoUseMemo=() = >{
    const [ number ,setNumber ] = useState(0)
    const newLog = useMemo(() = >{
        const log =() = >{
            /* Click span to print the number that is not updated in real time */
            console.log(number)
        }
        return log
      /* [] 没有 number */}, [])return <div>
        <div onClick={()= >NewLog ()} > print</div>
        <span onClick={() = >SetNumber (number + 1)} > increment</span>
    </div>
}

Copy the code

UseMemo is great. React-redux uses a lot of useMemo scenarios after react-hooks. I’ll analyze two of them for you

UseMemo uses the Store didStoreComeFromProps contextValue property to specify whether or not subscription is required to reset and update. I’m not going to cover react-Redux here. If you are interested in react-Redux, you can see how to use useMemo


const [subscription, notifyNestedSubs] = useMemo(() = > {
  if(! shouldHandleStateChanges)return NO_SUBSCRIPTION_ARRAY

  const subscription = new Subscription(
    store,
    didStoreComeFromProps ? null : contextValue.subscription // old 
  )
  
  const notifyNestedSubs = subscription.notifyNestedSubs.bind(
    subscription
  )

  return [subscription, notifyNestedSubs]
}, [store, didStoreComeFromProps, contextValue])
Copy the code

React-redux gets the state of the redux store by determining the changes to the redux store

 const previousState = useMemo(() = > store.getState(), [store])
Copy the code

At this point, useMemo can do a great job of optimizing components if we apply it to granulate our components appropriately based on their dependencies.

8 useCallback Callback function of the useMemo version

Both useMemo and useCallback receive the same parameters. Both are executed after their dependencies have changed, and both return cached values. The difference is that useMemo returns the result of a function run, while useCallback returns the function. This callback function is processed after that is the parent component pass a function to child components, because is a stateless components every time to regenerate new props function, which makes every passed to the child components function has changed, this time will trigger a child component update, the update is not necessary, We can then handle this function via usecallback and pass it to the child component as props

/ * the react. Memo * /
const DemoChildren = React.memo((props) = >{
   /* Only when the child component is initialized is printed */
    console.log('Child Component Update')
   useEffect(() = >{
       props.getInfo('Child component')
   },[])
   return <div>Child components</div>
})

const DemoUseCallback=({ id }) = >{
    const [number, setNumber] = useState(1)
    /* The first parameter of usecallback (sonName)=>{console.log(sonName)} is assigned to getInfo */
    const getInfo  = useCallback((sonName) = >{
          console.log(sonName)
    },[id])
    return <div>{/* Click the button to trigger the parent component update, but the child component is not updated */}<button onClick={() = >SetNumber (number+1)} > increment</button>
        <DemoChildren getInfo={getInfo} />
    </div>
}

Copy the code

Note that useCallback must be used with the React. Memo pureComponent, otherwise it will not improve performance and may even degrade performance

4 summarizes

The react-hooks are not meant to replace the class-declared components completely. For complex components, class components are preferred, but we can separate class components into funciton components, which are responsible for logical interaction and which need dynamic rendering according to business requirements. Then with usememo and other apis, to improve performance. There are also restrictions on react-hooks use, such as they cannot be placed in process control statements, and execution context requirements. Overall, react-hooks are pretty good and worth learning and exploring.

Wechat scan code to follow the public account, regularly share technical articles