关于hooks:React-With-Reudx-Hooks详解

Hooks全面详解

意识Hooks

1.Hook介绍

Hook是 React 16.8 的新增个性,它能够让咱们在不编写class的状况下应用state以及其余的React个性 (比方生命周期)

2.class与function组件比照

  • 咱们先来思考一下class组件绝对于函数式组件有什么劣势?比拟常见的是上面的劣势:
比照 class组件 function组件
state class组件能够定义本人的state, 用来保留组件本人外部的状态 函数式组件不能够,因为函数每次调用都会产生新的长期变量
生命周期 class组件有本人的生命周期, 咱们能够在对应的生命周期中实现本人的逻辑 ( 比方在componentDidMount中发送网络申请,并且该生命周期函数只会执行一次 ) 函数式组件在学习hooks之前,如果在函数中发送网络申请,意味着每次从新渲染都会从新发送一次网络申请
render渲染 class组件能够在状态扭转时只会从新执行render函数以及咱们心愿从新调用的生命周期函数componentDidUpdate 函数式组件在从新渲染时,整个函数都会被执行,仿佛没有什么中央能够只让它们调用一次
  • 所以,在Hook呈现之前,对于下面这些状况咱们通常都会编写class组件。

3.Class组件存在的问题

  • 简单组件变得难以了解:

    • 咱们在最后编写一个class组件时,往往逻辑比较简单,并不会非常复杂, 然而随着业务的增多,咱们的class组件会变得越来越简单
    • 比方componentDidMount中,可能就会蕴含大量的逻辑代码:包含网络申请、一些事件的监听(还须要在 componentWillUnmount中移除)
    • 而对于这样的class实际上十分难以拆分:因为它们的逻辑往往混在一起,强行拆分反而会造成适度设计,减少代码的复杂度
  • 难以了解的class:

    • 很多人发现学习ES6class是学习React的一个阻碍。
    • 比方在class中,咱们必须搞清楚this的指向到底是谁,所以须要花很多的精力去学习this
    • 尽管前端开发人员必须把握this,然而仍然解决起来十分麻烦
  • 组件复用状态很难:

    • 在后面为了一些状态的复用咱们须要通过高阶组件或render props
    • 像咱们之前学习的reduxconnect或者react-router中的withRouter,这些高阶组件设计的目标就是为了状态的复用
    • 或者相似于Provider、Consumer来共享一些状态,然而屡次应用Consumer时,咱们的代码就会存在很多嵌套
    • 这些代码让咱们不论是编写和设计上来说,都变得十分艰难

4.Hook的呈现

  • Hook的呈现,能够解决下面提到的这些问题
  • 简略总结Hooks

    • 它能够让咱们在不编写class的状况下应用state以及其余的React个性
    • 然而咱们能够由此延长出十分多的用法,来让咱们后面所提到的问题失去解决
  • Hook的应用场景:

    • Hook的呈现根本能够代替咱们之前所有应用class组件的中央 (除了一些十分不罕用的场景)
    • 然而如果是一个旧的我的项目,你并不需要间接将所有的代码重构为Hooks,因为它齐全向下兼容,你能够渐进式的来应用它
    • Hook只能在函数组件中应用,不能在类组件,或者函数组件之外的中央应用

Hooks的体验

计数器案例比照

通过一个计数器案例,来比照一下class组件和函数式组件联合hooks的比照

State Hook

意识useState

  • useState来自react, 须要从react中导入, 它是一个Hook

    • 参数: 初始化值, 如果不设置为undefined
    • 返回值: 数组, 蕴含两个元素

      • 元素一: 以后状态的值(第一调用为初始化值)
      • 元素二: 设置状态值的函数
import React, {  useState } from 'react'
const [counter, setCounter] = useState(0)

Hook 补充

  • Hook 就是 JavaScript 函数, 这个函数能够帮忙你钩入(hook into) React State以及生命周期等个性
  • 应用Hook的两个额定规定:

    • 只能在函数最外层调用Hook。不要在循环、条件判断或者子函数中调用
    • 只能在 React 的函数组件中调用Hook。不要在其余 JavaScript 函数中调用

useState 解析

  • useState

    • useState会帮忙咱们定义一个 state变量,useState是一种新办法,它与 class 外面的 this.state 提供的性能完全相同。
    • 一般来说,在函数执行结束后变量就会”隐没”,而 state 中的变量会被 React 保留。
    • useState接管一个惟一参数, 在第一次组件被调用时应用来作为初始化值
    • useState是一个数组, 能够通过[数组的解构赋值](https://developer.mozilla.org…
      )来应用
  • FAQ:为什么叫 useState 而不叫 createState?

    • “Create” 可能不是很精确,因为 state 只在组件首次渲染的时候被创立
    • 下一次从新渲染时, useState 返回给咱们以后的 state
  • 如果每次都创立新的变量,它就不是”state”了

    • 这也是 Hook 的名字总是use 结尾的一个起因

<details>
<summary>useState流程解析</summary>
<img src=”https://gitee.com/xmkm/cloudPic/raw/master/img/20201028124155.png” alt=”useState流程” />
</details>

useState 补充

  • useState函数的参数是能够传递一个函数
const [counter, setCounter] = useState(() => 10)
  • setState函数的参数也是能够传递一个函数
// 进行累加都会失效
setCounter(prevState => prevState + 10)

Effect Hook

意识Effect Hook

useEffect 就是一个 Effect Hook,给函数组件减少了操作副作用的能力。

它跟 class 组件中的 componentDidMountcomponentDidUpdatecomponentWillUnmount 具备雷同的用处,只不过被合并成了一个 API

  • Effect Hook能够让你来实现一些相似于class生命周期的性能
  • 事实上,相似于网络申请、手动更新DOM、一些事件的监听,都是React更新DOM的一些副作用(Side Effects)
  • 所以对于实现这些性能的Hook被称之为 Effect Hook
import React, { useEffect } from 'react'
useEffect(() => {
  console.log('useEffect被执行了')
})

useEffect的解析

  • 作用: 通过useEffect的Hook, 能够通知React须要在渲染后执行某些操作
  • 参数: useEffect要求咱们传入一个回调函数,在React执行完更新DOM操作之后,就会回调这个函数
  • 执行机会: 首次渲染之后,或者每次更新状态之后,都会执行这个回调函数

须要革除Effect

class组件的编写过程中,某些副作用的代码,咱们须要在componentWillUnmount中进行革除

  • 比方咱们之前的事件总线或Redux中手动调用subscribe
  • 都须要在componentWillUnmount有对应的勾销订阅
  • Effect Hook通过什么形式来模仿componentWillUnmount呢?
  • useEffect传递的<font color=’red’>回调函数A</font>自身能够有一个返回值, 这个返回值是另外一个<font color=’red’>回调函数B</font>
type EffectCallback = () => (void | (() => void | undefined))

import React, { useEffect, useState } from 'react'
useEffect(() => {
  console.log('订阅一些事件')
  // 勾销订阅
  return () => {
    console.log('勾销订阅')
  }
})
  • 为什么要在 Effect 中返回一个函数?

    • 这是Effect可选的革除机制。每个effect都能够返回一个革除函数
    • 如此能够将增加和移除订阅的逻辑放在一起
    • 它们都属于effect的一部分
  • React何时革除Effect
  • React 会在组件更新和卸载的时候执行革除操作
  • 正如之前学到的,effect在每次渲染的时候都会执行

应用多个Effect

应用Hook的其中一个目标就是解决class生命周期常常将很多的逻辑放在一起的问题

比方网络申请、事件监听、手动批改DOM,这些往往都会放在componentDidMount

  • 应用Effect Hook,咱们能够将它们拆散到不同的useEffect中:
useEffect(() => {
  console.log('批改DOM')
})

useEffect(() => {
  console.log('订阅事件')
}, [])

useEffect(() => {
  console.log('网络申请')
}, [])
  • Hook容许咱们依照代码的用处拆散它们, 而不是像生命周期函数那样:

    • React 将依照 effect 申明的程序顺次调用组件中的每一个 effect

Effect性能优化

默认状况下,useEffect的回调函数会在每次渲染时都从新执行,然而这会导致两个问题:

  • 某些代码咱们只是心愿执行一次即可,相似于componentDidMountcomponentWillUnmount中实现的事件;(比方网络申请、订阅和勾销订阅)
  • 另外,屡次执行也会导致肯定的性能问题
  • 咱们如何决定useEffect在什么时候应该执行和什么时候不应该执行呢?

    • useEffect实际上有两个参数
    • 参数一: 执行的回调函数
    • 参数二:useEffect在依赖state产生扭转时, 才从新执行该回调(受谁的影响)
  • 然而,如果一个函数咱们不心愿依赖任何的内容时,也能够传入一个空的数组[]

    • 那么这里的两个回调函数别离对应的就是componentDidMountcomponentWillUnmount生命周期函数了

useContext

为什么应用useContext?

  • 在之前的开发中: 咱们要在组件中应用共享的Context有两种形式

    • 类组件能够通过: 类名.contextType = myContext形式,在类中获取context
    • 多个Context或者在函数式组件通过MyContext.consumer形式共享context
  • 然而多个Context共享使存在大量嵌套

    • Context Hook容许咱们通过Hook来间接获取某个Context

useContext的应用

import React, { useContext } from 'react'
import { Theme, User } from '../App'
export default function UseContextDemo() {
// useContext应用
  const user = useContext(User)
  const theme = useContext(Theme)
  console.log(user, theme)
  return (
    <div>
      <h3>UseContextDemo</h3>
    </div>
  )
}
  • 注意事项:

    • 当组件下层最近的 <MyContext.Provider> 更新时,该 Hook 会触发从新渲染,并应用最新传递给 MyContext providercontext value 值。

useReducer

useRducer介绍

很多人看到useReducer的第一反馈应该是redux的某个替代品,其实并不是。

  • useReducer仅仅是useState的一种代替计划:
  • 应用场景: 在某些场景下,如果state的解决逻辑比较复杂,咱们能够通过useReducer对其进行拆分
  • 或者这次批改的state须要依赖之前的state时,也能够应用

useReducer应用

// 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() {
  // 参数1: reducer   参数2: 初始state
  const [state, setState] = useReducer(reducer, { count: 0 })
  return (
    <div>
      <h2>以后计数: {state.count}</h2>
      <button onClick={e => setState({ type: 'increment' })}>+</button>
      <button onClick={e => setState({ type: 'decrement' })}>-</button>
    </div>
  )
}
  • 数据是不会共享的,它们只是应用了雷同counterReducer函数而已
  • 所以, useReduceruseState的一种替代品, 并不能代替Redux

useCallback

useCallback介绍

  • 试想一下: 当你更新 name 属性时, 从新调用 render 之后所有的事件处理函数从新全副定义, 十分节约性能
  • 解决: 当依赖的属性没有扭转时, 不心愿更新 render 时, 从新定义事件函数

useCallback应用

  • useCallBack机会指标是为了进行性能的优化
  • 如何性能性能的优化呢?

    • useCallBack会返回一个函数 memoized(记忆的) 值
    • 在依赖不变的状况下, 多定义的时候, 返回值是雷同的
const increment2 = useCallback(() => {
  console.log('increment2被调用了')
  setCount(count + 1)
}, [count])

useCallback应用场景

  • 场景: 在将一个组件中的函数, 传递给子元素回调函数应用时, 应用useCallback对函数进行解决
import React, { useState, useCallback, memo } from 'react'

const JMButton = memo(props => {
  console.log('HYButton从新渲染: ', props.title)
  return <button onClick={props.increment}>JMButton+1</button>
})

export default function CallBackHomeDemo2() {
  // useCallback: 心愿更新父组件的state时,子组件不被render渲染
  // 1.应用memo包裹子组件进行性能优化,子组件没有依赖的props或state没有批改,不会进行render
  // 2.一个疑难: 为什么 btn1 还是被渲染了?
  //  (1)因为子组件依赖的 increment1 函数,在父组件没有进行缓存(在函数从新render时,increment1被从新定义了)
  //  (2)而 increment2 函数在父组件中被缓存了,所以memo函数进行性浅层比拟时依赖的increment2是一样的所以没有被从新render渲染
  // 3.useCallback在什么时候应用?
  //  场景: 在将一个组件中的函数, 传递给子元素进行回调应用时, 应用useCallback对函数进行解决.

  console.log('CallBackHomeDemo2从新渲染')
  const [count, setCount] = useState(0)
  const [show, setShow] = useState(true)

  const increment1 = () => {
    console.log('increment1被调用了')
    setCount(count + 1)
  }

  const increment2 = useCallback(() => {
    console.log('increment2被调用了')
    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切换</button>
    </div>
  )
}

useMemo

useMemo介绍

  • useMemo理论的目标也是为了进行性能的优化。
  • 如何进行性能的优化呢?
  • useMemo返回的也是一个 memoized(记忆的) 值;
  • 依赖不变的状况下,屡次定义的时候,返回的值是雷同的;
// 依赖没有扭转的话,是不会进行从新定义局部变量的
const info = useMemo(() => {
  return { name: 'kobe', age: 18 }
}, [])

useMemo应用场景

  • 场景: 在将一个组件中的函数, 传递给子元素局部变量应用时, 应用useMemo对函数进行解决
import React, { useState, memo, useMemo } from 'react'

const User = memo(props => {
  console.log('User被渲染了')
  return (
    <h3>
      姓名: {props.info.name} 年龄:{props.info.age}
    </h3>
  )
})

export default function MemoHookDemo2() {
  console.log('MemoHookDemo2被渲染了')
  // 需要: 在更新父组件的局部变量时,子组件依赖的props或state没有扭转不心愿被render渲染
  // 1.memo包裹子组件
  // 2.为什么子组件User还是被渲染了呢?
  //  (1)因为: 在父组件从新渲染时,会吃会从新创立info对象,memo在比照时会发现两次创立的info对象不同,会从新render渲染
  // const info = { name: 'kobe', age: 18 }
  //  (2)解决: 应用useMemo
  const info = useMemo(() => {
    return { name: 'kobe', age: 18 }
  }, [])
  const [show, setShow] = useState(true)
  return (
    <div>
      <User info={info} />
      <button onClick={e => setShow(!show)}>切换</button>
    </div>
  )
}

useRef

useRef介绍

  • 介绍: useRef返回一个ref对象,返回的ref对象在组件的整个生命周期放弃不变
  • 最罕用的ref是两种场景:

    • 场景一: 引入DOM(或者组件, 须要是class组件) 元素
    • 场景二: 保留一个数据, 这个对象在整个生命周期能够保留不变
const refContainer = useRef (initialvalue);

援用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() {
    // 批改DOM
    titleRef.current.innerHTML = 'hello world
    console.log(cpnRef.current)
  }
  return (
    <div>
      {/* 1.批改DOM元素 */}
------<h2 ref={titleRef}>RefHookDemo01</h2>------
      {/* 2.获取class组件 ✔ */}
      <ChildCpn ref={cpnRef} />
      <button onClick={changeDOM}>批改DOM</button>
    </div>
  )
}

应用ref保留上一次的某一个值

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

export default function RefHookDemo02() {
  // 需要: 应用ref保留上一次的某一个值
  const [count, setCount] = useState(0)

  // 将上一次的count进行保留,在count产生扭转时,从新保留count
  // 为什么: 在点击button时,减少count时,会调用useEffect函数,渲染DOM后,会从新将上一次的值进行保留,应用ref保留上一次的某一个值不会触发render
  const numRef = useRef(count)
  useEffect(() => {
    numRef.current = count
  }, [count])

  return (
    <div>
      <h3>count上一次的值: {numRef.current}</h3>
      <h3>count这一次的值 {count}</h3>
      <button onClick={e => setCount(count + 10)}>+10</button>
    </div>
  )
}

useImperativeHandle

useImperativeHandle引入

  • 咱们先来回顾一下refforwardRef联合应用:

    • 通过forwardRef能够将ref转发给子组件
    • 子组件拿到父组件创立的ref, 绑定到本人的某一个元素中
import React, { useRef, forwardRef } from 'react'

// forwardRef能够将ref转发给子组件
const JMInput = forwardRef((props, ref) => {
  return <input type="text" ref={ref} />
})

export default function ForwardDemo() {
  // forward用于获取函数式组件DOM元素
  const inputRef = useRef()
  const getFocus = () => {
    inputRef.current.focus()
  }

  return (
    <div>
      <button onClick={getFocus}>聚焦</button>
      <JMInput ref={inputRef} />
    </div>
  )
}
  • forwardRef的做法自身没有什么问题, 然而咱们是将子组件的DOM间接裸露给了父组件:

    • 间接裸露给父组件带来的问题是某些状况的不可控
    • 父组件能够拿到DOM后进行任意的操作
    • 咱们只是心愿父组件能够操作的focus,其余并不心愿它随便操作其余办法

useImperativeHandle介绍

useImperativeHandle(ref, createHandle, [deps])
  • 通过useImperativeHandle能够只裸露特定的操作

    • 通过useImperativeHandle的Hook, 将父组件传入的refuseImperativeHandle第二个参数返回的对象绑定到了一起
    • 所以在父组件中, 调用inputRef.current时, 实际上是返回的对象
  • useImperativeHandle应用简略总结:

    • 作用: 缩小裸露给父组件获取的DOM元素属性, 只裸露给父组件须要用到的DOM办法
    • 参数1: 父组件传递的ref属性
    • 参数2: 返回一个对象, 以供应父组件中通过ref.current调用该对象中的办法
import React, { useRef, forwardRef, useImperativeHandle } from 'react'

const JMInput = forwardRef((props, ref) => {
  const inputRef = useRef()
  // 作用: 缩小父组件获取的DOM元素属性,只裸露给父组件须要用到的DOM办法
  // 参数1: 父组件传递的ref属性
  // 参数2: 返回一个对象,父组件通过ref.current调用对象中办法
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus()
    },
  }))
  return <input type="text" ref={inputRef} />
})

export default function ImperativeHandleDemo() {
  // useImperativeHandle 次要作用:用于缩小父组件中通过forward+useRef获取子组件DOM元素裸露的属性过多
  // 为什么应用: 因为应用forward+useRef获取子函数式组件DOM时,获取到的dom属性裸露的太多了
  // 解决: 应用uesImperativeHandle解决,在子函数式组件中定义父组件须要进行DOM操作,缩小获取DOM裸露的属性过多
  const inputRef = useRef()

  return (
    <div>
      <button onClick={() => inputRef.current.focus()}>聚焦</button>
      <JMInput ref={inputRef} />
    </div>
  )
}

useLayoutEffect

useLayoutEffect介绍

  • useLayoutEffect看起来和useEffect十分的类似,事实上他们也只有一点区别而已:

    • useEffect会将渲染的内容更新到DOM执行, 不会阻塞DOM更新
    • useLayoutEffect会将渲染的内容更新到DOM之前执行, 会阻塞DOM更新
  • 应用场景: 如果咱们心愿在执行某些操作之后再DOM, 那么这个操作应该放到useLayoutEffect, 留神会阻塞页面渲染

useLayoutEffect应用

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

export default function LayoutEffectCountChange() {
  const [count, setCount] = useState(10)
  // 次要作用: 执行某些操作之后再执行DOM渲染,会阻塞页面渲染
  useLayoutEffect(() => {
    if (count === 0) {
      setCount(Math.random())
    }
  }, [count])

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

自定义Hook

自定义Hook 介绍

自定义Hook实质上只是一种函数代码逻辑的抽取, 严格意义上来说,它自身并不算React的个性。

应用场景: 能够将组件反复的逻辑抽取到可重用的函数中

自定义Hook 应用

  • 如何自定义: 自定义 Hook 是一个函数,其名称以 “use” 结尾,函数外部能够调用其余的 Hook。
  • 上面定义的Hook作用是: 在组件被创立卸载时, 都会打印到控制台”以后组件生命周期信息”
import React, { useEffect } from 'react'

// 函数后面增加use 成为自定义Hook,能够应用Hook个性
function useLifeFlow(name) {
  useEffect(() => {
    console.log(`${name}被创立`)

    return () => {
      console.log(`${name}被卸载`)
    }
  }, [])
}

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>
  )
}

自定义Hook场景

需要一: Context共享

// 自定义Hook 函数前增加 use (共享多个context,将多个context进行封装)
export default function useUserContext() {
  // 获取先人组件或父组件提供 contetx provide value
  const user = useContext(UserContext)
  const token = useContext(TokenContext)

  // 将同一类型的 contetx provide value 返回
  return [user, token]
}

需要二: 获取鼠标滚动地位

export default function useScrollPosition() {
  const [scrollY, setScrollY] = useState(0)
  // 挂载完DOM后,注册scroll事件
  useEffect(() => {
    const handleScroll = () => {
      setScrollY(window.scrollY)
    }
    document.addEventListener('scroll', handleScroll)

    // 组件卸载后移除scroll事件
    return () => {
      document.removeEventListener('scroll', handleScroll)
    }
  }, [])

  return scrollY
}

需要三: localStorage数据存储

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

  • 应用useDispatch能够让你再也不必在组件中定义须要依赖的dispatch派发的action函数
  • 能够间接在组件中间接应用dispatch派发action
function JMRecommend(props) {
  // redux Hook 组件和redux关联: 获取数据和进行操作
  const dispatch = useDispatch()
  useEffect(() => {
    dispatch(getTopBannersAction())
  }, [dispatch])

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

useSelector

  • 应用useSelector后不必在组件中定义依赖的state, 间接在组件中应用useSelector传递函数的参数是state
  • 函数返回一个对象, 在对象中定义须要依赖的state
function JMRecommend(props) {
  // redux Hook 组件和redux关联: 获取数据和进行操作
  const { topBanners } = useSelector(state => ({
    topBanners: state.recommend.topBanners,
  }))

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

useSelector性能优化

  • 两个组件中都依赖并应用了redux中的state一个组件扭转了state另一个组件会被从新渲染, 这个很失常
  • useSelector的问题:

    • 只有reducerstate产生了变动,不论该组件是否依赖state,都会进行从新渲染
  • <details>
    <summary>useSelector问题(图示)</summary>

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

    </details>

useSelector的问题?

useSelector为什么会呈现这个的问题?

  • 因为应用了useSelector决定在组件是否从新渲染的之前
  • 会进行一次援用比照: 会和前一次函数返回的state对象进行一次援用比照(三等运算符)

    • 因为每次调用函数的时候, 创立的对象都是一个全新对象
    • 所以每次只有store中的state产生了扭转, 不论以后组件是否有依赖这个state组件都会进行从新渲染

useSelector优化

  • useSelector优化: useSelector的第二个参数传递一个ShallowEqual
  • ShallowEqual作用: 对一次浅层比拟, 和前一次useSelector返回的对象及进行比拟
  • <details>
    <summary>useSelector性能优化(图示)</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 组件被从新渲染')
  // 应用shallowEqual解决useSelector问题
  const { banners, recommends } = useSelector(state => ({
    banners: state.banners,
    recommends: state.recommends
  }), shallowEqual)

  return (
    <div>
      <h1>About</h1>
      <h4>组件没有依赖count: 但还是被从新渲染了</h4>
      <h4>应用shallowEqual解决useSelector渲染问题</h4>
    </div>
  )
}
  • 当前只有以后组件没有依赖扭转的state不心愿从新渲染, 应用useSelector联合ShallowEqual
  • 留神: connect函数是不存在这个问题的

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理