Zustand5 selector 发生 infinate loops

Zustand selector 发生 infinate loops

做zustand tutorial project的时候,使用选择器方法引入store,出现Maximum update depth exceeded,也就是组件一直重新渲染,改成直接使用store就没有不会出现这个问题。如下:

js 复制代码
  // const [xIsNext, setXIsNext] = useGameStore((state) => [
  //   state.xIsNext,
  //   state.setXIsNext,
  // ]);
  
  const { history, setHistory, xIsNext, setXIsNext } = useGameStore()

目前看到有关这个问题的博客很少,特此记录一下。 感谢一下我的mentor们,如此认真地教我,给我充足的时间学习

问题产生原因

官方文档的解释如下:

文档链接

也就是说,这是因为Zustand v5的改动,当selector返回一个新的引用,可能导致无限循环

细究了一下,问题产生原因如下:

  1. react的默认比较为引用比较,在zustandV5中沿用了这种方式。
  2. 每次重新渲染,useStore都会再执行一次,selector每次都返回的是一个新的引用
  3. zustand使用浅比较判断状态变化,触发重新渲染,以此往复

比如像下面这样使用selector

js 复制代码
  const { history, setHistory} = useGameStore(((state) => ({
    history: state.history,
    setHistory: state.setHistory,
  })));
  const [searchValue, setSearchValue] = useStore((state) 
          => [  state.searchValue,  state.setSearchValue,])

以上两种方法selector分别返回一个对象和一个数组,每次重新渲染,其引用都会发生变化,也就触发了infinate loops

解决方法

  1. 避免在selector中返回对象 这是我认为最直接的办法:
js 复制代码
const history = useGameStore((state) => state.history);
const setHistory = useGameStore((state) => state.setHistory);
//不要像下面这样
//  const { history, setHistory} = useGameStore(((state) => ({
//    history: state.history,
//    setHistory: state.setHistory,
//  })));
  1. 采用文档中介绍的使用useShllow解决: 但是useShllow比较对象只会比较最浅层的值,要注意可能产生的副作用
js 复制代码
  const { history, setHistory, xIsNext, setXIsNext } = useGameStore(useShallow((state) => ({
    history: state.history,
    setHistory: state.setHistory,
    xIsNext: state.xIsNext,
    setXIsNext: state.setXIsNext,
  })));

附: zustand实现原理

zustand源代码中useShallow实现部分如下(也就是比较值而非比较引用):

js 复制代码
import React from 'react'
import { shallow } from '../vanilla/shallow.ts'

export function useShallow<S, U>(selector: (state: S) => U): (state: S) => U {
  const prev = React.useRef<U>(undefined)
  return (state) => {
    const next = selector(state)
    return shallow(prev.current, next)
      ? (prev.current as U)
      : (prev.current = next)
  }
}
相关推荐
持久的棒棒君3 小时前
npm安装electron下载太慢,导致报错
前端·electron·npm
crary,记忆4 小时前
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
前端·webpack·angular·angular.js
漂流瓶jz5 小时前
让数据"流动"起来!Node.js实现流式渲染/流式传输与背后的HTTP原理
前端·javascript·node.js
SamHou05 小时前
手把手 CSS 盒子模型——从零开始的奶奶级 Web 开发教程2
前端·css·web
我不吃饼干6 小时前
从 Vue3 源码中了解你所不知道的 never
前端·typescript
开航母的李大6 小时前
【中间件】Web服务、消息队列、缓存与微服务治理:Nginx、Kafka、Redis、Nacos 详解
前端·redis·nginx·缓存·微服务·kafka
Bruk.Liu6 小时前
《Minio 分片上传实现(基于Spring Boot)》
前端·spring boot·minio
鱼樱前端6 小时前
Vue3+d3-cloud+d3-scale+d3-scale-chromatic实现词云组件
前端·javascript·vue.js
coding随想6 小时前
JavaScript中的原始值包装类型:让基本类型也能“变身”对象
开发语言·javascript·ecmascript
zhangxingchao6 小时前
Flutter入门:Flutter开发必备Dart基础
前端