Hook is a new feature in React 16.8. It lets you use state and other React features without having to write a class.

Use rules for Hooks

Use hooks only at the top level

Never call hooks in loops, conditions, or nested functions. Make sure you always call them at the top of your React function.

Only the React function calls the Hook

Never call a Hook in a normal JavaScript function. You can:

✅ calls the Hook in the React function component

✅ calls other hooks in custom hooks

The Linter plugin is also provided with rules to enforce hooks.

useState useEffect useRef

useState

Returns a state and a function to update the state.

During initial rendering, the state returned is the same as the value of the first parameter passed in.

The setState function is used to update state. It receives a new state value and queues a re-rendering of the component.

const ClickCount = () = > {
  const [ count, setCount ] = useState(0)
  const [ num, setNam ] = useState(1)
  const handleClick = () = > {
    setCount(count+1)
    setNam(num+2)}return (
    <div>
      <p>count:{count}---num:{num}</p>
      <button onClick={handleClick}>+ 1</button>
    </div>)}Copy the code

UseState also provides two ways to update state asynchronously, similar to the way setState updates state in class components:

  1. Use setCount() directly
setCount(count+1)
Copy the code
  1. Use the setCount function for updates
setCount((prevCount) = > {
    return prevCount + 1
})
Copy the code

useEffect

The Hook receives a function that contains imperative code that may have side effects. Changing the DOM, adding subscriptions, setting timers, logging, and performing other side effects within the body of a function component (in this case, during the React rendering phase) are not allowed, as this can cause unexplained bugs and break UI consistency. Use useEffect for side effects. The function assigned to useEffect is executed after the component is rendered to the screen.

UseEffect is equivalent to the three life cycle functions componentDidMount, componentDidUpdate, and componentWillUnmount

UseEffect takes two arguments:

  1. The first argument is a function that performs some operation.returnA function to clear resources such as timers or subscriptions. – simulationcomponentWillUnmountThe life cycle
  2. The second argument is an array that sets the useEffect function execution dependencies and controls whether the useEffect function is re-executed. – simulationcomponentDidUpdateThe life cycle

UseEffect is executed as soon as the component is loaded, equivalent to the componentDidMount life cycle.

 const Fetch = () = > {
  const [result, setResult] = useState(null)

    useEffect(() = > {
      fetch('./index.html').then(response= >response.text()).then(res= >{
        // When the component is initialized, it fires once.
       ComponentDidMount (componentDidUpdate) {componentDidMount (componentDidUpdate)
              
        console.log(111)
        setResult(res)
      })
    }, []) 
    // useEffect is executed once if the second argument is an empty array, [componentDidMount]
    // [result] executes twice depending on the state of result
    // [a,b] will be executed again if any state changes



  return (
    <div>
      result: {result}
    </div>)}Copy the code
const Fetch1 = () = > {
  const [count, setCount] = useState(0)
  useEffect(() = > {
    const timer = setInterval(() = > {
      setCount(count+1)},500);

    // Executes after each dependency change

    return () = > {
      // Clear the last timer
      clearInterval(timer)
    }
  },[count])
  // useEffect will not be executed again if you do not add a dependency to the system.
  // If a dependency is added, the useEffect callback is repeated
  // Resolve: useRef

  return (
    <div>
      count: {count}
    </div>)}Copy the code

useRef

There are two main uses for useRef:

  1. To mark DOM
  2. It’s used to store data

To mark DOM

A tag name is first declared with const btnRef = useRef(NULL), then marked with ref={btnRef} on the DOM where the tag is needed, and finally called with btnRef.

const Fetch1 = () = > {
  const [count1, setCount1] = useState(0)
 
  const btnRef = useRef(null)
  // const handleOnclick = () => {
  // setCount1(count1+1)
  // } 
  useEffect(() = > {
    console.log(222)
    
    const handleOnclick = () = > {
      setCount1(count1+1)
    } 
    btnRef.current.addEventListener('click',handleOnclick, false)

    return () = > btnRef.current.removeEventListener('click', handleOnclick,false)
    
  },[count1])
  return (
    <div>
       count1: {count1}
      <hr></hr>{/ *<button onClick={handleOnclick}>+ 1</button>* /}<button ref={btnRef}>+ 1</button>

    </div>
}

Copy the code

It’s used to store data

What are the properties in a component that remain constant across the render cycle, that is, after the component has been rendered many times? The first thing that comes to mind is state. Yes, a component’s state can remain the same after multiple renders. The problem with state, however, is that changing it can cause components to be re-rendered.

At this point useRef can be used to store data across the render cycle, and changes to it will not cause the component to render.

import React, { useState, useEffect, useRef } from 'react'

function Counter() {
    const [count, setCount] = useState(0);
    const timer = useRef(null)

    useEffect(() = > {
        console.log('usesEffect');  // Only output once when the component is loaded. Using useRef does not cause the component to be re-rendered
        timer.current = setInterval(() = > {
            setCount(count= > count + 1)},1000)
        return () = > {
            clearInterval(timer.current) }; } []);return (
        <div>
            <p>count: {count}</p>
        </div>)}export default Counter
Copy the code

memo useMemo useCallback

memo

The Memo is used to optimize functionality in functional components to determine whether or not a subcomponent is rerendered by making sure that the data passed to the subcomponent is the same twice when it is called.

Pass a value of a simple data type to a child component

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

const Parent = () = > {
    const [count, setCount] = useState(0)
    const [clickCount, setClickCount] = useState(0)
    return (
      <div>
        {<! -- Clicking the button will not change the data passed to the child component -->}
        count: {count}
        <button onClick={()= >{
          setCount(count+1)
        }}>+1</button>
  		
        {<! -- Click the button to change the data passed to the child component -->}
        <button onClick={()= > {
          setClickCount(clickCount + 1)
        }}>GET CURRENT TIME</button>
        <Child count={clickCount}/>  
      </div>)}const Child = memo((props) = > {
      // console.log(props)
      console.log(123)
      const date = new Date(a)return (
      <div>
          <p>{date.gethours ()}:{date.getminutes ()}:{date.getseconds ()}</p>
      </div>)}//,(prev, next) => {// If the memo is in the same state as the second one, the memo will not be updated. If it is not, the memo will be updated.
    // console.log(prev,next)
    // console.log(prev.count === next.count)
    // return prev.count === next-count // The same effect as not writing
    // }
  )
   
  export { Parent }
Copy the code

Pass values of complex types

const Parent = () = > {
    const [count, setCount] = useState(0)
    const [clickCount, setClickCount] = useState(0)
   
   // Wrap the data that needs to be passed to the child components in an object.
    const timeOption = {
      clickCount
    }

    return (
      <div>
        count: {count}
        <button onClick={()= >{
          setCount(count+1)
        }}>+1</button>
  
        <button onClick={()= > {
          setClickCount(clickCount + 1)
        }}>GET CURRENT TIME</button>
        {<! Pass a complex type of data to a child component -->}
        <Child count={timeOption}/>  
      </div>)}const Child = memo((props) = > {
    console.log(123)
    const date = new Date(a)return (
      <div>
        <p>{date.gethours ()}:{date.getminutes ()}:{date.getseconds ()}</p>
      </div>)}export { Parent }
Copy the code

If you pass data of an object type to a child component, the clickCount in the parent component does not change, but the timeOption object is regenerated each time.

Even if the subcomponent uses memo, the subcomponent will be updated if the prev. Count next. Count address is different because the object is a reference type.

Solution:

  • ClickCount and next. Count. ClickCount (because these two values are the same in the object and are simple data types)
  • Use useMemo()

useMemo

UseMemo receives two parameters, the first for the callback and the second for the data to depend on. When a useMemo callback returns a data result, useMemo caches the return result.

const Parent = () = > {
    const [count, setCount] = useState(0)
    const [clickCount, setClickCount] = useState(0)

    const timeOption = useMemo(() = > {
      return {clickCount}
    }, [clickCount])


    return (
      <div>
        count: {count}
        <button onClick={()= >{
          setCount(count+1)
        }}>+1</button>
  
        <button onClick={()= > {
          setClickCount(clickCount + 1)
        }}>GET CURRENT TIME</button>
        <Child count={timeOption}/>
  
      </div>)}const Child = memo((props) = > {
    console.log(props)
    console.log(123)
    const date = new Date(a)return (
      <div>
        <p>{date.gethours ()}:{date.getminutes ()}:{date.getseconds ()}</p>
      </div>)})Copy the code

At this time: The clickCount in the parent component does not change. UseMemo () allows the result to be cached. TimeOption is not regenerated each time the parent component updates the state, so the props in the memo() second parameter are the same as before and after the comparison. So that updates to child components are not triggered.

useCallback

UseCallback receives two arguments, the first for the callback and the second for the data to depend on.

UseCallback evaluates to a function and is used primarily to cache functions.

     // In the parent component, the text property in the parent component is changed and the parent component is re-rendered when input is entered in the child component's input field.
     // Therefore handleOnChange is repeatedly passed to the child component, in which props is repeatedly printed.
     // When useCallback is used, the handleOnChange passed to the child component is the same event,
     // Therefore will be cached, in the child component does not print the method passed by the parent component
const Parent = () = > {
    const [text, setText] = useState(' ')
  
    const handleOnChange = useCallback((e) = > {
      setText(e.target.value)
    }, [])
  
    return (
      <div>
        <p>text: {text}</p>
        <Child onChange={handleOnChange}/>
  
      </div>)}const Child = memo((props) = > {
    console.log(props)
    console.log(123)
    return (
      <div>
        <input type="text" onChange={props.onChange} />
      </div>)})export { Parent } 
Copy the code

conclusion

A change in state can cause the entire component to be refreshed, and some functions and data should be cached to improve performance and reduce resource waste.

Memo, useMemo, and useCallback can all be used to improve performance. The main differences are:

  • The memo implements whether the props changes before and after the component state changes, and determines whether the component should be reloaded. It stands on the entire component.
  • UseMemo implements caching of attributes and determines whether a child component is reloaded for a change in a property, and its return value is a numeric value.
  • UseCallback implements method caching to determine whether a child component is reloaded for a method change, and its return value is a method.

useReducer useContext

useReducer useContext

Use useReducer useContext to transfer data between components.

import React, { useReducer, useContext } from 'react'

// Create global context
const Ctx = React.createContext(null)

 
// Create a reducer, receive the action and change the state
const reducer = (state, action) = > {
    switch (action.type) {
        case 'ADD':
            return state + 1;
        case 'SUB':
            return state - 1;
        default:
            returnstate; }}const Child = () = > {

    // If you introduce useReducer() into a child component, it can be used by the child component and change the state in the child component

    //const [count, dispatch] = useReducer(reducer, 10)

    // Receive the count,dispatch, passed in the context and send the action via dispatch in the child component

    const [count, dispatch] = useContext(Ctx)
    return (
        <div>
            child:
            count: {count}
            <button onClick={()= > {dispatch({type: 'ADD'})}}>+1</button>
            <button onClick={()= > {dispatch({type: 'SUB'})}}>-1</button>

        </div>)}const Parent = () = > {
    
    // The parent component receives the updated state of the child component
   
    const [count] = useContext(Ctx)

    return (
        <div>
            parent:{count}
            <Child />
        </div>)}function App1() {
// Declare a state count with an initial value of 20,
// Declare the action method dispatch,
// Specify the reducer to handle the actions

  const [count, dispatch] = useReducer(reducer, 20)
    return (
     // Use the Provider in the created context to specify the scope of the context,
     // Both pass the count state and send the Dispatch method of the action for use by child components
  
    <Ctx.Provider value={[count, dispatch]} >
        <div className="App">
            <Parent />
        </div>
    </Ctx.Provider>
    
  );
}

export default App1;

Copy the code

Custom Hooks

Custom hooks

A custom Hook is a function whose name starts with “use” that can call other hooks from within.

import { useEffect, useState } from 'react';

export const useWindowResize = () = > {
    const [width, setWidth] = useState('0px')
    const [height, setHeight] = useState('0px')
    useEffect(() = > {
        setWidth(document.documentElement.clientWidth + 'px')
        setHeight(document.documentElement.clientHeight + 'px')
    
      }, [])
    
      useEffect(() = > {
        const handleResize = () = > {
          setWidth(document.documentElement.clientWidth + 'px')
          setHeight(document.documentElement.clientHeight + 'px')}window.onresize = handleResize
        return () = > {
          window.onresize = null}}, [])return [width,height]
}


import React from 'react'

import { useWindowResize } from './hooks'

const Parent = () = > {
  const [width, height] = useWindowResize() // Can also pass arguments,
  return(
    <div>
      <p>size: {width}*{height}</p>
    </div>)}Copy the code

More Hooks

  1. React-redux:useSelector useDispatch
  2. react-router-domProvided by:useHistory useLocation useParams useRouteMatch