React with reudx hooks

Time:2021-11-30

Hooks full explanation

React with reudx hooks

Meet hooks

1. Hook introduction

Hook is a new feature of react 16.8, which allows us to write withoutclassUse in case ofstateAnd othersReactCharacteristics (such as lifecycle)

2. Comparison between class and function components

  • Let’s think about it firstclassWhat are the advantages of components over functional components? The following advantages are common:
contrast Class component Function component
state classComponents can define their ownstate, which is used to save the internal state of the component itself Functional components are not allowed, because each function call will generate a new temporary variable
life cycle classComponents have their own life cycle, and we can complete our logic in the corresponding life cycle (for example, incomponentDidMountSend network requests in, and the lifecycle function will be executed only once) Functional components in learninghooksPreviously, if you sent a network request in the function, it means that each re rendering will send a network request again
Render rendering classComponents can change state whenOnlyRe executerenderFunction and the lifecycle function we want to call againcomponentDidUpdateetc. When functional components are re rendered, the whole function will be executed. It seems that there is no place to call them only once
  • So, inHookWe usually write these cases before they appearclassComponents.

3. Problems with class components

  • Complex components become difficult to understand:

    • We wrote one at the beginningclassThe logic of components is often simple and not very complex, but with the increase of business, ourclassComponents become more and more complex
    • such ascomponentDidMountIt may contain a lot of logic code, including network requests and monitoring of some events (it also needs to be in thecomponentWillUnmount(remove from)
    • And for suchclassIn fact, it is very difficult to split: because their logic is often mixed, forced splitting will lead to over design and increase the complexity of the code
  • Incomprehensible class:

    • Many people find that learningES6ofclassIt’s learningReactAn obstacle to.
    • Like inclassWe must find outthisWho is the point, so it needs to spend a lot of energy to learnthis
    • Although front-end developers must masterthisBut it’s still very troublesome to deal with
  • Component reuse status is difficult:

    • Previously, in order to reuse some states, we need to use high-order components orrender props
    • Like we learned beforereduxinconnectperhapsreact-routerMediumwithRouter, these high-order components are designed for state reuse
    • Or something likeProvider、ConsumerTo share some state, but use it multiple timesConsumerThere will be a lot of nesting in our code
    • These codes make it very difficult for us to write and design

4. Appearance of hook

  • HookThe emergence of can solve these problems mentioned above
  • Simple summaryHooks

    • It allows us to write withoutclassUse in case ofstateAnd othersReactcharacteristic
    • However, we can extend many usages to solve the problems we mentioned earlier
  • HookUsage scenarios:

    • The appearance of hook can basically replace all our previous useclassComponents (except for some very infrequent scenarios)
    • But if it’s an old project, you don’t need to refactor all the code directly into hooks, because it’s completely downward compatible, and you can use it incrementally
    • Hook can only be used in function components, not in class components or outside function components

Hooks experience

Counter case comparison

Through a counter case, compare itclassCombination of component and functional componenthooksComparison of

React with reudx hooks

State Hook

Recognize usestate

  • useStatecome fromreact, need fromreactIt is a hook

    • Parameter: initialization value, if not set toundefined
    • Return value: array, containing two elements

      • Element 1: the value of the current state (the first call is the initialization value)
      • Element 2: function for setting status value
import React, {  useState } from 'react'
const [counter, setCounter] = useState(0)

Hook supplement

  • HooknamelyJavaScript function, this function can help you hook in(hook into) React StateAnd life cycle
  • useHookTwo additional rules for:

    • Only inFunction outermost layerCall hook. Do not call in loops, conditional judgement, or subfunctions.
    • Only inFunction components of reactCall Hook in. Do not invoke in other JavaScript functions.

Usestate parsing

  • useState

    • useStateWill help us define astateVariables,useStateIs a new method, which is related toclassInsidethis.stateThe functions provided are exactly the same.
    • Generally speaking, the variable will “disappear” after the function is executed, andstateVariables in areReactretain.
    • useStateReceives a unique parameter that is used as the initialization value when the component is called for the first time
    • useStateIs an array, which can be assigned by [array deconstruction]( https://developer.mozilla.org…
      )To use
  • FAQ: whyuseStateInstead of yellingcreateState?

    • “Create” may not be very accurate because state is created only when the component is rendered for the first time
    • The next time you re render,useStateReturn to our currentstate
  • If you create a new variable every time, it is not “state”

    • This tooHookName ofalwayswithuseThe first reason

<details>
< summary > usestate process analysis < / summary >
<img src=” https://gitee.com/xmkm/cloudPic/raw/master/img/20201028124155.png “ALT =” usestate process “/ >
</details>

Usestate supplement

  • useStateFunctionalArguments are functions that can be passedof
const [counter, setCounter] = useState(() => 10)
  • setStateFunctionalParameters can also pass a functionof
//Accumulation takes effect
setCounter(prevState => prevState + 10)

Effect Hook

Meet effect hook

useEffectIt is an effect hook, which adds the ability of operating side effects to function components.

It is similar to thecomponentDidMountcomponentDidUpdateandcomponentWillUnmountHas the same purpose, butWas merged into one API

  • Effect HookYou can do something similar toclassinLife cycle functions
  • In fact, it is similar to network request and manual updateDOMMonitoring of some eventsReactto updateDOMSome side effects of(Side Effects)
  • So for completing these functionsHookCalledEffect Hook
import React, { useEffect } from 'react'
useEffect(() => {
  Console.log ('useeffect executed ')
})

Analysis of useeffect

  • effect:adoptuseEffectHook, you can tellReactYou need to do something after rendering
  • Parameters: useEffectAsk us to pass in a callback function, inReactUpdate completedDOMAfter the operation, this function will be called back
  • Execution time:This callback function is executed after the first rendering or after each update of the state

Effect needs to be cleared

stayclassIn the process of writing components, some side effects of code, we need tocomponentWillUnmountIn progresseliminate

  • For example, our previous event bus orReduxManual call insubscribe
  • All need to be incomponentWillUnmountThere is a corresponding unsubscribe
  • Effect HookBy what meanscomponentWillUnmountAnd?
  • useEffectThe passed < font color =’Red ‘> callback function a < / font > itself can have a return value, which is another < font color =’Red’ > callback function B < / font >
type EffectCallback = () => (void | (() => void | undefined))

import React, { useEffect, useState } from 'react'
useEffect(() => {
  Console.log ('subscribe to some events')
  //Unsubscribe
  return () => {
    Console.log ('unsubscribe ')
  }
})
  • Why are you hereEffectReturn a function in?

    • This isEffectOptionalClearing mechanism。 eacheffectCan return a clear function
    • This brings together the logic for adding and removing subscriptions
    • They all belong toeffectPart of
  • ReactWhen to clearEffect
  • React will perform cleanup when updating and uninstalling components
  • As I learned before,effectIt is executed at each rendering

Use multiple effects

useHookOne of the purposes of issolveclassinThe life cycle often puts a lot of logic together

For example, network requests, event monitoring, and manual DOM modification are often placed in thecomponentDidMountin

  • useEffect Hook, we can separate them into differentuseEffectMedium:
useEffect(() => {
  Console.log ('modify DOM ')
})

useEffect(() => {
  Console.log ('subscribe to events')
}, [])

useEffect(() => {
  Console.log ('network request ')
}, [])
  • Hook allows us to separate the code according to its purposeInstead of a lifecycle function:

    • ReactWill followeffectThe in the component is called in order of declarationevery last effect

Effect performance optimization

By default,useEffectThe callback function of will be re executed at each rendering, but this will cause two problems:

  • Some code we just want to execute once, similar tocomponentDidMountandcomponentWillUnmountWhat has been done in the; (such as network request, subscription and unsubscribe)
  • In addition, multiple executions can also lead to some performance problems
  • How do we decideuseEffectWhen should it be implemented and when should it not be implemented?

    • useEffectThere are actually two parameters
    • Parameter 1: callback function executed
    • Parameter 2:ShoulduseEffectIn dependencestateThe callback is re executed only when the change occurs (affected by WHO)
  • But if a function weDon’t want to rely on anythingYou can also pass in an empty array[]

    • The two callback functions here correspond tocomponentDidMountandcomponentWillUnmountLife cycle function

useContext

Why usecontext?

  • In previous development: we want to use shared in componentsContextThere are two ways

    • Class components can be:Class name. Contexttype = mycontextMethod, get in classcontext
    • MultipleContextOr through functional componentsMyContext.consumerMode sharingcontext
  • But multipleContextShare make existMassive nesting

    • Context HookAllow us to passHookTo get a directContextvalue

Use of usecontext

import React, { useContext } from 'react'
import { Theme, User } from '../App'
export default function UseContextDemo() {
//Usecontext usage
  const user = useContext(User)
  const theme = useContext(Theme)
  console.log(user, theme)
  return (
    <div>
      <h3>UseContextDemo</h3>
    </div>
  )
}
  • matters needing attention:

    • When the upper layer of the component is the nearest<MyContext.Provider>When updating, theHookA re rendering is triggered and the latest data is passed to theMyContext providerofcontext valueValue.

useReducer

Userducer introduction

Many people seeuseReducerYour first reaction should bereduxA substitute for, in fact, is not.

  • useReducerMerelyuseStateAn alternative to:
  • Usage scenario: in some scenarios, ifstateThe processing logic of is complex. We canuseReducercomeSplit it
  • Or modified this timestateNeed to rely on previousstateYou can also use

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() {
  //Parameter 1: 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>
  )
}
  • Data is not sharedYes, they are just usedidenticalofcounterReducerofIt’s just a function
  • So,useReduceryesuseStateIt is a substitute for and cannot be replacedRedux

useCallback

Usecallback introduction

  • Just imagine: when you update the name attribute, all event handling functions are redefined after calling render again, which is a waste of performance
  • Solution: whenWhen the dependent properties are not changed, when you do not want to update render,Redefine event functions

Usecallback use

  • useCallBackThe timing goal is to optimize performance
  • How to optimize performance?

    • useCallBackA function is returnedmemoized(memorized) value
    • When the dependency remains unchanged, the return value is the same when multiple definitions are used
const increment2 = useCallback(() => {
  Console.log ('increment2 called ')
  setCount(count + 1)
}, [count])

Usecallback usage scenario

  • Scenario: the function in a component,Callback function passed to child elementWhen using, useuseCallbackProcessing functions
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 will not be rendered by render
  //1. Wrap the subcomponent with memo for performance optimization. The subcomponent has no dependent props or state, and will not render
  //2. A question: why is BTN1 still rendered?
  //(1) because the increment1 function that the child component depends on is not cached in the parent component (increment1 is redefined when the function is re rendered)
  //(2) the increment2 function is cached in the parent component, so the increment2 that the memo function relies on for shallow comparison is the same, so it is not re rendered
  //3. When is usecallback used?
  //Scenario: when a function in a component is passed to a child element for callback, usecallback is used to process the function

  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 (! Show)} > show switch < / button >
    </div>
  )
}

useMemo

Usememo introduction

  • useMemoThe actual purpose is also to optimize the performance.
  • How to optimize the performance?
  • useMemoReturn is also amemoized(memorized) value;
  • stayDependency invariantIn the case of multiple definitions, the returned values are the same;
//If the dependency does not change, the local variable will not be redefined
const info = useMemo(() => {
  return { name: 'kobe', age: 18 }
}, [])

Usememo usage scenario

  • Scenario: the function in a component,Local variables passed to child elementsWhen using, useuseMemoProcessing functions
import React, { useState, memo, useMemo } from 'react'

const User = memo(props => {
  Console.log ('user rendered ')
  return (
    <h3>
      Name: {props. Info. Name} age: {props. Info. Age}
    </h3>
  )
})

export default function MemoHookDemo2() {
  Console.log ('memohookdemo2 rendered ')
  //Requirement: when updating the local variables of the parent component, the props or state that the child component depends on has not changed and does not want to be rendered by render
  //1. Memo package subassembly
  //2. Why is the sub component user still rendered?
  //(1) because: when the parent component is re rendered, the info object will be re created. During comparison, memo will find that the info objects created twice are different, so it will be re rendered
  // 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 (! Show)} > toggle < / button >
    </div>
  )
}

useRef

Useref introduction

  • Introduction:useRefReturn arefObject, returnedrefObjects remain unchanged throughout the life cycle of the component
  • Most commonly usedrefThere are two scenarios:

    • Scenario 1: ImportDOM(or components, need to be)classComponent) element
    • Scenario 2: save a data object that can be saved throughout its life cycle
const refContainer = useRef (initialvalue);

Reference 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 DOM
    titleRef.current.innerHTML = 'hello world
    console.log(cpnRef.current)
  }
  return (
    <div>
      {/ * 1. Modify DOM element * /}
------<h2 ref={titleRef}>RefHookDemo01</h2>------
      {/ * 2. Get class component ✔ */}
      <ChildCpn ref={cpnRef} />
      < button onclick = {changedom} > Modify DOM < / button >
    </div>
  )
}

Use ref to save a previous value

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

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

  //Save the last count. When the count changes, save the count again
  //Why: when you click button and increase count, the useeffect function will be called. After rendering DOM, the last value will be saved again. Saving a last value with ref will not trigger render
  const numRef = useRef(count)
  useEffect(() => {
    numRef.current = count
  }, [count])

  return (
    <div>
      <h3>Count last value: {numref. Current}</h3>
      <h3>Count this time's value {count}</h3>
      <button onClick={e => setCount(count + 10)}>+10</button>
    </div>
  )
}

useImperativeHandle

Useimperativehandle introduction

  • Let’s review it firstrefandforwardRefCombined use:

    • adoptforwardRefCanrefForward to child components
    • The child component is created by the parent componentref, bound to one of its own elements
import React, { useRef, forwardRef } from 'react'

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

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

  return (
    <div>
      < button onclick = {getfocus} > focus < / button >
      <JMInput ref={inputRef} />
    </div>
  )
}
  • forwardRefThere is no problem with the approach itself, but we are sub componentsDOMDirectly exposed to parent component:

    • The problem of direct exposure to parent components is that some situations are uncontrollable
    • Parent component can getDOMPerform any operation after
    • We just want the parent component to workfocus, you don’t want it to manipulate other methods at will

Useimperativehandle introduction

useImperativeHandle(ref, createHandle, [deps])
  • adoptuseImperativeHandlesureOnly specific operations are exposed

    • adoptuseImperativeHandleThe hook passed in by the parent componentrefanduseImperativeHandleThe objects returned by the second parameter are bound together
    • So in the parent component, callinginputRef.currentWhen, in fact, it is backObject returned
  • useImperativeHandleUse simple summary:

    • Role: reduce exposure to parent component acquisitionDOMElement attributes, which are only exposed to those needed by the parent componentDOMmethod
    • Parameter 1: ref attribute passed by parent component
    • Parameter 2: returns an object to be supplied to the parent componentref.currentCall the method in the object
import React, { useRef, forwardRef, useImperativeHandle } from 'react'

const JMInput = forwardRef((props, ref) => {
  const inputRef = useRef()
  //Function: reduce the DOM element attributes obtained by the parent component, and only expose the DOM methods required by the parent component
  //Parameter 1: ref attribute passed by parent component
  //Parameter 2: return an object, and the parent component calls the method in the object through ref.current
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus()
    },
  }))
  return <input type="text" ref={inputRef} />
})

export default function ImperativeHandleDemo() {
  //Useimperativehandle is mainly used to reduce too many attributes exposed by Dom elements of child components obtained through forward + useref in parent components
  //Why: because when you use forward + useref to obtain the sub functional component DOM, the DOM properties obtained are exposed too much
  //Solution: use uesimportativehandle to solve the problem. Defining a parent component in a child functional component requires DOM operations to reduce the number of attributes exposed by the dom
  const inputRef = useRef()

  return (
    <div>
      < button onclick = {() = > inputref. Current. Focus ()} > focus < / button >
      <JMInput ref={inputRef} />
    </div>
  )
}

useLayoutEffect

Uselayouteffect introduction

  • useLayoutEffectLooks anduseEffectThey are very similar. In fact, they have only one difference:

    • useEffectThe rendered content is updated toDOMupperafterExecute without blockingDOMto update
    • useLayoutEffectThe rendered content is updated toDOMupperbeforeExecution will blockDOMto update
  • Usage scenario: if we want toDo something before youDOM, then this operation should be put intouseLayoutEffectNote that page rendering is blocked

React with reudx hooks

Uselayouteffect using

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

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

  return (
    <div>
      <h2>Number: {count}</h2>
      <button onClick={e => setCount(0)}>change 0 state for count</button>
    </div>
  )
}

Customize hook

Introduction to custom hook

customHookIt’s essentially just aExtraction of function code logic, strictly speaking, it is not a feature of react itself.

Usage scenario: duplicate logic of components can be extracted into reusable functions

Custom hook usage

  • How to customize:Custom hook is a function whose name is“use”At the beginning, other hooks can be called inside the function.
  • Defined belowHookThe function is: when the component isestablishanduninstallWhen, it will print to the console “current component lifecycle information”
import React, { useEffect } from 'react'

//Add use in front of the function to become a custom hook. You can use the hook feature
function useLifeFlow(name) {
  useEffect(() => {
    Console.log (` ${name} 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>
  )
}

Customize hook scene

Requirement 1: context sharing

//Add use before customizing hook function (share multiple contexts and encapsulate multiple contexts)
export default function useUserContext() {
  //Gets the contentx provide value provided by the ancestor component or parent component
  const user = useContext(UserContext)
  const token = useContext(TokenContext)

  //Return contentx provide value of the same type
  return [user, token]
}

Requirement 2: get the mouse scroll position

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

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

  return scrollY
}

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

Redux Hook

useDispatch

  • useuseDispatchIt allows you to no longer define dependencies in componentsdispatchDistributedactionfunction
  • It can be used directly in componentsdispatchDistributeaction
function JMRecommend(props) {
  //Redux hook component and Redux Association: data acquisition and operation
  const dispatch = useDispatch()
  useEffect(() => {
    dispatch(getTopBannersAction())
  }, [dispatch])

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

useSelector

  • useuseSelectorThere is no need to define the dependent in the componentstate, used directly in the componentuseSelectorThe parameters of the transfer function arestate
  • Function returns an object in which you define the dependencystate
function JMRecommend(props) {
  //Redux hook component and Redux Association: data acquisition and operation
  const { topBanners } = useSelector(state => ({
    topBanners: state.recommend.topBanners,
  }))

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

Useselector performance optimization

  • Dependency in both componentsAnd usedState in ReduxA component has changedstateAnother component will be re rendered, which is normal
  • useSelectorQuestion of:

    • as long asreducerinstateChanges occur regardless of whether the component is dependent or notstate, will be re rendered
  • <details>
    < summary > useselector problem (Figure) < / summary >

    <img src="https://mingcloudpic.oss-cn-beijing.aliyuncs.com/img/20201028123347.gif" alt="render" style="zoom:80%;" />

    </details>

Useselector problem?

Why does useselector have this problem?

  • Because of the use ofuseSelectorstayBefore deciding whether to re render the component
  • A reference comparison will be made: it will be compared with that returned by the previous functionstateObject for one reference comparison (third-class operator)

    • Because every time the function is called, the created object is a new object
    • So every timeState in storeHas changed, regardless of whether the current component depends on thisstateComponents are re rendered

Useselector optimization

  • useSelectorOptimization:useSelectorThe second parameter of passes aShallowEqual
  • ShallowEqualFunction: compare one shallow layer with the previous oneuseSelectorReturned objects and compare
  • <details>
    < summary > useselector performance optimization (Figure) < / summary >

    <img src="https://mingcloudpic.oss-cn-beijing.aliyuncs.com/img/20201028123443.gif" alt="render" style="zoom:80%;" />

    </details>

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>
      The < H4 > component does not depend on count: but it is re rendered < / H4 >
      < H4 > solve useselector rendering problems with shallowequal < / H4 >
    </div>
  )
}
  • In the future, as long as the current component does not depend on changesstateDo not want to re render, useuseSelectorcombinationShallowEqual
  • be careful:connectFunction does not have this problem