Hooks


Know the Hooks

1. The Hook is introduced

Hook is a new feature in React 16.8 that allows us to use state and other React features (such as lifecyclic) without writing classes.

2. Class versus function components

  • So let’s think about it a little bitclassWhat are the advantages of components over functional components? More common are the following advantages:
contrast The class components The function components
state classComponents can define their ownstateTo save the component’s own internal state Functional components do not, because new temporary variables are created each time the function is called
The life cycle classComponents have their own life cycle, and we can do our own logic in the corresponding life cycle (e.gcomponentDidMountTo send a network request, and the lifecycle function is executed only once. Functional components are learninghooksPreviously, if the network request was sent in a function, it meant that the network request would be re-sent each time the rendering was redone
Render to render classComponents can change state whenWill onlyTo performrenderFunction and the lifecycle functions that we want to call againcomponentDidUpdateEtc. When functional components are rerendered, the whole function is executed, and there seems to be no place for them to be called only once
  • So, inHookBefore that, we used to write things like thisclassComponents.

3. Problems with the Class component

  • Complex components become difficult to understand:

    • Let’s write one initiallyclassComponent, often logic is relatively simple, will not be very complex, but with the increase of business, ourclassComponents become more and more complex
    • Such ascomponentDidMountMay contain a lot of logical code, including network requests, some events listening (also need to be incomponentWillUnmountRemoved)
    • And for thisclassIn practice, they are very difficult to break down: because their logic tends to get mixed up, forcing them apart can lead to over-design and increased code complexity
  • Unintelligible class:

    • Many people find learningES6theclassIs to learnReactAn obstacle to.
    • For example, inclassWe must make it clearthisSo it takes a lot of effort to learnthis
    • Although front-end developers must master itthisBut it is still very troublesome to deal with
  • Component reuse state is difficult:

    • Previously in order to reuse some of the states we need to use higher-order components orrender props
    • As we learned beforereduxIn theconnectorreact-routerIn thewithRouterThese higher-order components are designed for state reuse
    • Or something like thatThe Provider and ConsumerTo share some state, but use it multiple timesConsumer“, our code will have a lot of nesting
    • This code makes it very difficult to write and design

4. The emergence of the Hook

  • HookCan solve the problems mentioned above
  • Simple summaryHooks
    • It allows us to write without writingclassIn case of usestateAnd so forthReactfeatures
    • But we can extend a lot of usage from this to solve the problem we mentioned earlier
  • HookThe use scenario of
    • The emergence of Hook can basically replace all our previous useclassComponent places (except for some very uncommon scenarios)
    • But if it’s an old project, you don’t need to directly refactor all the code into Hooks, because it’s fully backward compatible and you can use it incrementally
    • Hooks can only be used in function components, not in class components, or outside of function components


The experience of Hooks

Counter case comparison

Use a counter example to compare class components with functional components that combine hooks



State Hook

Know useState

  • useStatefromreact, need fromreact, which is a Hook
    • Parameter: initialization value, if not set toundefined
    • Return value: array, containing two elements
      • Element 1: Value of current state (first call is initialization value)
      • Element 2: A function that sets state values
import React, {  useState } from 'react'
const [counter, setCounter] = useState(0)
Copy the code

Hook added

  • HookisJavaScript function, this function helps you check into (hook into) React StateAnd features like the life cycle
  • useHookTwo additional rules for:
    • A Hook can only be called from the “outermost layer of the function”. Do not call in loops, conditional judgments, or subfunctions
    • You can only call a Hook in the React function component. Do not call it from another JavaScript function

UseState parsing

  • useState

    • useStateWill help us define onestateVariables,useStateIs a new method, which is associated withclassThe inside of thethis.stateThe functionality is exactly the same.
    • In general, the variable “disappears” after the function is finished executing, andstateThe variable inReactRetained.
    • useStateReceives a unique parameter to be used as an initialization value the first time the component is called
    • useStateIt’s an array that passes throughDestruct assignment of arraysTo use the
  • FAQ: Why useState and not createState?

    • “Create” may not be very accurate, since state is created only when the component is first rendered
    • The next time I rerender,useStateReturn to our currentstate
  • If you create a new variable every time, it won’t be “state”

    • This is alsoHookThe name of thealwaysIn order touseOne reason at the beginning
UseState Process analysis


UseState supplement

  • useStateFunction of theArguments can be passed to a functionthe
const [counter, setCounter] = useState((a)= > 10)
Copy the code
  • setStateFunction of theArguments can also pass a functionthe
// the sum will work
setCounter(prevState= > prevState + 10)
Copy the code

Effect Hook

Know the Effect the hooks

UseEffect is an Effect Hook that gives function components the ability to manipulate side effects.

It serves the same purpose as componentDidMount, componentDidUpdate, and componentWillUnmount in the Class component, but has been consolidated into an API

  • Effect Hook allows you to perform some of the lifecycle functions of a class

  • In fact, things like network requests, manually updating the DOM, and listening for certain events are Side Effects of React updating the DOM.

  • Therefore, hooks to complete these functions are called Effect hooks

import React, { useEffect } from 'react'
useEffect(() = > {
  console.log('useEffect executed ')})Copy the code

UseEffect analytical

  • Function:throughuseEffectHook, can tellReactSomething needs to be done after rendering
  • Parameters: useEffectRequires us to pass in a callback function inReactUpdate doneDOMAfter the operation, the function is called back
  • Timing: This callback is executed after the first rendering or after each state update

Need to clear Effect

During the class component writing process, some side effects of the code, we need to remove in componentWillUnmount

  • For example, we called SUBSCRIBE manually in the event bus or Redux

  • All require corresponding unsubscribe at componentWillUnmount

  • In what way does Effect Hook simulate componentWillUnmount?

  • useEffectThe passed callback function A can itself have A return value that is another callback function B
type EffectCallback = () = > (void | (() = > void | undefined))

import React, { useEffect, useState } from 'react'
useEffect(() = > {
  console.log('Subscribe to some events')
  // Unsubscribe
  return () = > {
    console.log('Unsubscribe')}})Copy the code
  • Why return a function in Effect?

    • This is the optional clearing mechanism for Effect. Each effect can return a cleanup function

    • This puts the logic for adding and removing subscriptions together

    • They are all part of Effect

  • React When to remove an Effect

  • React performs cleanup operations when components are updated and uninstalled

  • As you learned earlier, an effect is executed every time it is rendered

Using multiple Effects

One of the purposes of using hooks is to solve the problem of classes where the lifecycle often puts too much logic together:

Things like network requests, event listeners, and manual DOM modifications are often placed in componentDidMount

  • useEffect HookWe can separate them into different onesuseEffectIn:
useEffect(() = > {
  console.log('modify the DOM')
})

useEffect(() = > {
  console.log('Subscribe to events')
}, [])

useEffect(() = > {
  console.log('Network Request')}, [])Copy the code
  • Hooks allow us to separate code according to its purpose, as opposed to life cycle functions:
    • ReactWill be in accordance with theeffectThe order of the declarations is called in the componenteach effect

Effect performance optimization

By default, the useEffect callback is reexecuted on each render, but this causes two problems:

  • Some code we just want to execute once, something like thatcomponentDidMountandcomponentWillUnmountTo do STH. (such as network requests, subscriptions, and unsubscriptions)
  • In addition, multiple executions can cause performance issues
  • How do we decide when useEffect should and should not be executed?

    • useEffectThere are actually two parameters
    • Parameter 1: the callback function to execute
    • Parameters of the two:useEffectIn reliance onstateReexecute the callback when it changes (affected by whom)
  • However, if a function does not want to depend on any content, we can also pass in an empty array []

    • So the two callbacks here correspond tocomponentDidMountandcomponentWillUnmountLife cycle function

useContext

Why useContext?

  • In previous development: we wanted to use shared in the componentContextThere are two ways
    • Class components can be made available via:Class name contextType = myContextMethod in a classcontext
    • multipleContextOr through functional componentsMyContext.consumerWay to sharecontext
  • But more than oneContextShare bring into existenceA large number of nested
    • Context HookAllow us to passHookTo get something directlyContextvalue

The use of useContext

import React, { useContext } from 'react'
import { Theme, User } from '.. /App'
export default function UseContextDemo() {
/ / useContext use
  const user = useContext(User)
  const theme = useContext(Theme)
  console.log(user, theme)
  return (
    <div>
      <h3>UseContextDemo</h3>
    </div>)}Copy the code
  • Matters needing attention:
    • When the component is closest to the upper layer<MyContext.Provider>Update when theHookWill trigger a rerender and use the latest pass toMyContext providercontext valueValue.

useReducer

UseRducer introduction

The first thing many people think of useReducer is that it’s some sort of replacement for Redux. It’s not.

  • useReducerjustuseStateAn alternative to:
  • Usage scenario: In some scenarios, ifstateThe processing logic is more complex, we can passuseReducertoI’m going to break it up
  • Or this modified onestateYou have to rely on the previous onestate, can also be used

UseReducer use

// reducer.js
export default function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { ...state, count: state.count + 1 }
    case 'decrement':
      return { ...state, count: state.count - 1 }
    default:
      return state
  }
}

// home.js
import reducer from './reducer'
export default function Home() {
  Reducer Parameter 2: initial state
  const [state, setState] = useReducer(reducer, { count: 0 })
  return (
    <div>
      <h2>Current count: {state.count}</h2>
      <button onClick={e= > setState({ type: 'increment' })}>+</button>
      <button onClick={e= > setState({ type: 'decrement' })}>-</button>
    </div>)}Copy the code
  • The data is not shared, they just use the same counterReducer function

  • Therefore, useReducer is an alternative to useState and does not replace Redux

useCallback

UseCallback introduction

  • Consider this: when you update the name attribute, all the event handlers after the render call are completely redefined, which is a waste of performance
  • Resolution: Redefine the event function when you don’t want to update render when the dependent properties have not changed

UseCallback use

  • useCallBackThe timing goal is to optimize performance
  • How to optimize performance?
    • useCallBackWill return a functionmemoizedValue of memory
    • In the case of invariant dependencies, the return value is the same for multiple definitions
const increment2 = useCallback(() = > {
  console.log('Increment2 called')
  setCount(count + 1)
}, [count])
Copy the code

UseCallback Usage scenario

  • Scenario: When combining a function in a component,Passed to the child element callback functionWhen used, useuseCallbackI’m going to do something with the function
import React, { useState, useCallback, memo } from 'react'

const JMButton = memo(props= > {
  console.log('HYButton re-render: ', props.title)
  return <button onClick={props.increment}>JMButton+1</button>
})

export default function CallBackHomeDemo2() {
  // useCallback: When you want to update the state of the parent component, the child component is not rendered by render
  // 1. Use the memo to wrap the subcomponent for performance optimization. The subcomponent has no props or state modification, and will not render
  // 2. A question: why is BTN1 still rendered?
  // (1) Because the child depends on the increment1 function, the parent component is not cached (increment1 was redefined when the function was rerendered)
  // (2) Increment2 is cached in the parent component, so memo relies on the same increment2 for shallow comparisons and is not rerender
  // 3. When is useCallback used?
  // Scenario: useCallback is used to process functions in a component when they are passed to child elements for callback use.

  console.log('CallBackHomeDemo2 re-render ')
  const [count, setCount] = useState(0)
  const [show, setShow] = useState(true)

  const increment1 = () = > {
    console.log('Increment1 called')
    setCount(count + 1)}const increment2 = useCallback(() = > {
    console.log('Increment2 called')
    setCount(count + 1)
  }, [count])

  return (
    <div>
      <h2>CallBackHomeDemo: {count}</h2>
      <JMButton increment={increment1} title="btn1" />
      <JMButton increment={increment2} title="btn2" />

      <button onClick={e= >setShow(! The show)} > show switch</button>
    </div>)}Copy the code

useMemo

UseMemo introduction

  • useMemoThe practical purpose is also to optimize performance.
  • How do you optimize performance?
  • useMemoAnd it returns onememoizedThe value of memory;
  • In the case of invariant dependencies, the return value is the same when defined multiple times;
// Local variables are not redefined if the dependency is not changed
const info = useMemo(() = > {
  return { name: 'kobe'.age: 18}}, [])Copy the code

UseMemo usage scenarios

  • Scenario: When combining a function in a component,Local variables passed to child elementsWhen used, useuseMemoI’m going to do something with the function
import React, { useState, memo, useMemo } from 'react'

const User = memo(props= > {
  console.log('User is rendered ')
  return (
    <h3>Name :{props.info.name} Age :{props.info.age}</h3>)})export default function MemoHookDemo2() {
  console.log('MemoHookDemo2 is rendered ')
  // Requirement: When updating a local variable of the parent component, the props or state on which the child depends does not want to be rendered by render
  // 1. Memo wraps sub-components
  // 2. Why is the child User still rendered?
  // (1) Because: The parent component will re-render the info object, and the memo will re-render the info object after the comparison
  // const info = { name: 'kobe', age: 18 }
  // (2) Solution: Use useMemo
  const info = useMemo(() = > {
    return { name: 'kobe'.age: 18}}, [])const [show, setShow] = useState(true)
  return (
    <div>
      <User info={info} />
      <button onClick={e= >setShow(! The show)} > switch</button>
    </div>)}Copy the code

useRef

UseRef introduction

  • Introduction:useRefReturns arefObject, returnedrefObjects remain constant throughout the life of the component
  • The most commonly usedrefThere are two scenarios:
    • Scenario 1: IntroductionDOM(Or component, it needs to beclassComponent) element
    • Scenario 2: Save a piece of data that can be kept unchanged throughout its life cycle
const refContainer = useRef (initialvalue);
Copy the code

Reference to the DOM

import React, { useRef } from 'react'

class ChildCpn extends React.Component {
  render() {
    return <div>ChildCpn</div>}}export default function RefHookDemo01() {
  const titleRef = useRef()
  const cpnRef = useRef()

  function changeDOM() {
    / / modify the DOM
	titleRef.current.innerHTML = 'hello world console.log(cpnRef.current) } return ( 
      
{/* 1. Modify the DOM elements * /} -- -- -- -- -- - < h2 ref = {titleRef} > RefHookDemo01 < / h2 > -- -- -- -- -- - {/ * 2. Bookmark onClick={changeDOM}>
)}
Copy the code

Use ref to save a previous value

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

export default function RefHookDemo02() {
  // Use ref to save a previous value
  const [count, setCount] = useState(0)

  // Save the last count. If the count changes, save it again
  // Why: useEffect is called when a button is clicked and count is increased, DOM is rendered and the last value is saved again, while ref is used to save the last value without triggering render
  const numRef = useRef(count)
  useEffect(() = > {
    numRef.current = count
  }, [count])

  return (
    <div>
      <h3>Count last value: {numref.current}</h3>
      <h3>Count The value of this time {count}</h3>
      <button onClick={e= > setCount(count + 10)}>+10</button>
    </div>)}Copy the code

useImperativeHandle

UseImperativeHandle introduced

  • Let’s start with a recaprefandforwardRefCombined use:
    • throughforwardRefCan berefForward to child components
    • The child component is created by the parent componentrefBinds to one of its own elements
import React, { useRef, forwardRef } from 'react'

// forwardRef forwards refs to child components
const JMInput = forwardRef((props, ref) = > {
  return <input type="text" ref={ref} />
})

export default function ForwardDemo() {
  // Forward is used to get the DOM element of the functional component
  const inputRef = useRef()
  const getFocus = () = > {
    inputRef.current.focus()
  }

  return (
    <div>
      <button onClick={getFocus}>Focusing on the</button>
      <JMInput ref={inputRef} />
    </div>)}Copy the code
  • forwardRefThere’s nothing wrong with that in itself, but are we going to subcomponent itDOMDirectly exposed to the parent component:
    • The problem with direct exposure to the parent component is that some situations are out of control
    • The parent component is availableDOMAnd then do any operation
    • We just want the parent component to operatefocusOthers do not want it to operate freely on other methods

UseImperativeHandle introduction

useImperativeHandle(ref, createHandle, [deps])
Copy the code
  • throughuseImperativeHandlecanOnly specific operations are exposed
    • throughuseImperativeHandleThe Hook that passes in the parent componentrefanduseImperativeHandleThe objects returned by the second parameter are bound together
    • So in the parent component, callinputRef.currentWhen, in fact, is returnBack to the object
  • useImperativeHandleUse a simple summary:
    • Role: Reduce exposure to parent component acquisitionDOMElement attributes that are only exposed for use by the parent componentDOMmethods
    • Parameter 1: Ref property passed by the parent component
    • Parameter 2: Returns an object to be passed by the parent componentref.currentCall a method in that object
import React, { useRef, forwardRef, useImperativeHandle } from 'react'

const JMInput = forwardRef((props, ref) = > {
  const inputRef = useRef()
  // Reduces the DOM element attributes acquired by the parent component, exposing only DOM methods that the parent component needs
  // Argument 1: the ref property passed by the parent component
  // Argument 2: Returns an object in which the parent component calls the method through ref.current
  useImperativeHandle(ref, () = > ({
    focus: () = > {
      inputRef.current.focus()
    },
  }))
  return <input type="text" ref={inputRef} />
})

export default function ImperativeHandleDemo() {
  UseImperativeHandle is used to reduce the number of attributes exposed in the parent component's DOM element obtained by forward+useRef
  // Why: Because too many DOM attributes are exposed when using forward+useRef to retrieve the DOM of a subfunctional component
  // Solution: Use uesImperativeHandle to solve the problem that DOM manipulation is required to define the parent component in the child functional component to reduce the number of DOM exposed attributes
  const inputRef = useRef()

  return (
    <div>
      <button onClick={()= >InputRef. Current. The focus ()} > focus</button>
      <JMInput ref={inputRef} />
    </div>)}Copy the code

useLayoutEffect

UseLayoutEffect introduction

  • useLayoutEffectLook anduseEffectVery similar, in fact, with only one difference:
    • useEffectWill update the rendered content toDOMonafterExecute without blockingDOMupdate
    • useLayoutEffectWill update the rendered content toDOMonbeforeExecution, blocksDOMupdate
  • Usage scenario: if we wish inAfter performing some operationsDOM, then the operation should be placed inuseLayoutEffect, notice that it blocks the page rendering

UseLayoutEffect use

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

export default function LayoutEffectCountChange() {
  const [count, setCount] = useState(10)
  // DOM rendering blocks page rendering after performing some operations
  useLayoutEffect(() = > {
    if (count === 0) {
      setCount(Math.random())
    }
  }, [count])

  return (
    <div>
      <h2>The Numbers: {count}</h2>
      <button onClick={e= > setCount(0)}>change 0 state for count</button>
    </div>)}Copy the code

Customize the Hook

Introduction to custom hooks

A custom Hook is essentially an extraction of function code logic. Strictly speaking, it is not a React feature.

Usage scenario: You can extract the repetitive logic of components into reusable functions

Custom Hook usage

  • How to customize: A custom Hook is a function whose name starts with “use” that can call other hooks from within.
  • The following definitionHookThe component is used bycreateanduninstallIs printed to the console “Current Component Lifecycle Information”
import React, { useEffect } from 'react'

// Add use to the front of the function as a custom Hook, you can use the Hook feature
function useLifeFlow(name) {
  useEffect(() = > {
    console.log(`${name}Be created `)

    return () = > {
      console.log(`${name}Uninstalled `)}}, [])}function Home() {
  useLifeFlow('Home')
  return <h2>Home</h2>
}

function Profile() {
  useLifeFlow('Profile')
  return <h2>Profile</h2>
}

export default function CustomLifeHookDemo() {
  useLifeFlow('CustomLiftHookDemo')
  return (
    <div>
      <h2>CustomLiftHookDemo</h2>
      <Home />
      <Profile />
    </div>)}Copy the code

Custom Hook scenarios

Requirement 1: Context sharing

// Add use (share multiple contexts, encapsulate multiple contexts)
export default function useUserContext() {
  // Obtain the contetx provide Value of the ancestor or parent component
  const user = useContext(UserContext)
  const token = useContext(TokenContext)

  // Return contetx provide Value of the same type
  return [user, token]
}
Copy the code

Requirement 2: Obtain the mouse scroll position

export default function useScrollPosition() {
  const [scrollY, setScrollY] = useState(0)
  // Register the Scroll event after DOM is mounted
  useEffect(() = > {
    const handleScroll = () = > {
      setScrollY(window.scrollY)
    }
    document.addEventListener('scroll', handleScroll)

    // Remove scroll event after component uninstallation
    return () = > {
      document.removeEventListener('scroll', handleScroll)
    }
  }, [])

  return scrollY
}
Copy the code

Requirement 3: localStorage Data storage

function useLocalStorage(key) {
  const [data, setData] = useState(() = > {
    const data = JSON.parse(window.localStorage.getItem(key))
    return data
  })

  useEffect(() = > {
    window.localStorage.setItem(key, JSON.stringify(data))
  }, [data])

  return [data, setData]
}
export default useLocalStorage
Copy the code

Redux Hook

useDispatch

  • useuseDispatchThis allows you to never define dependencies in a component againdispatchDistributing theactionfunction
  • It can be used directly in componentsdispatchdistributedaction
function JMRecommend(props) {
  // Redux Hook components are associated with REdux: retrieving data and performing operations
  const dispatch = useDispatch()
  useEffect(() = > {
    dispatch(getTopBannersAction())
  }, [dispatch])

  return (
    <div>
      <h2>JMRecommend</h2>
    </div>)}export default memo(JMRecommend)
Copy the code

useSelector

  • useuseSelectorDo not define dependencies in the componentstate, used directly in componentsuseSelectorThe arguments to the transfer function arestate
  • The function returns an object in which to define dependenciesstate
function JMRecommend(props) {
  // Redux Hook components are associated with REdux: retrieving data and performing operations
  const { topBanners } = useSelector(state= > ({
    topBanners: state.recommend.topBanners,
  }))

  return (
    <div>
      <h2>JMRecommend</h2>
      <h3>{topBanners.length}</h3>
    </div>)}export default memo(JMRecommend)
Copy the code

UseSelector performance optimization

  • Both components rely on and use state in Redux and it’s normal for one component to change state and the other component to be rerendered

  • UseSelector questions:

    • As long asreducerIn thestateChanges, whether the component is dependent or notstate, will be re-rendered
  • UseSelector problem (icon)


UseSelector question?

Why does useSelector have this problem?

  • Because of usinguseSelectorinDetermines whether the component is rerendered before
  • A reference comparison will be made: it will be returned from the previous functionstateObject for a reference comparison (third order operator)
    • Because every time a function is called, the object created is a brand new object
    • So every timeThe state of the storeChanged, regardless of whether the current component has dependencies on thisstateComponents are all re-rendered

UseSelector optimization

  • UseSelector optimization: The second argument to useSelector passes a ShallowEqual

  • ShallowEqual: performs a shallow comparison with the object returned by useSelector

  • UseSelector Performance Optimization (graphic)


import React from 'react'
import { shallowEqual, useSelector } from 'react-redux'

export default function About(props) {
  console.log('About component is re-rendered ')
  // Use shallowEqual to solve the useSelector problem
  const { banners, recommends } = useSelector(state= > ({
    banners: state.banners,
    recommends: state.recommends
  }), shallowEqual)

  return (
    <div>
      <h1>About</h1>
      <h4>The component does not rely on count: but is rerendered anyway</h4>
      <h4>Use shallowEqual to solve useSelector rendering problems</h4>
    </div>)}Copy the code
  • Use useSelector in conjunction with ShallowEqual in the future as long as the current component does not want to rerender as long as it does not depend on the changed state

  • Note: The connect function does not have this problem