当前位置: 首页 > news >正文

简单又强大的Zustand,为啥不自己手写一个呢

谈起react的状态管理库,大家肯定会想到redux,用过redux的人肯定会发现,它使用起来是非常的繁琐,受人诟病。

后起之秀的react的管理库就有zustand jotai等,其中最为简单且流行的就是 zustand,它可以说是操作简单,功能强大,redux的任何功能他都可以实现,并且可以更简单的实现。

我们先来简单看一下 zustand使用方法。

  • 安装 npm install zustand
import { create } from 'zustand'

type BearState = {
  bears: number
  increasePopulation: () => void
}

const useBearStore = create<BearState>((set, get, api) => ({
    bears: 0,
    increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
}))


export default useBearStore;

我们可以看到三个参数,set get api他们分别是后去设置state,获取state,api是有关state方法的数据。

在这里插入图片描述

其中的getState和setState就是上述的中的get set

在App.tsx中使用

import useBearStore from "./zustand/baseStore";

function App() {

  const bears = useBearStore(state => state.bears)
  const increasePopulation = useBearStore(state => state.increasePopulation)

  return (
    <div>
    {bears}  
    <button onClick={increasePopulation}>+ 1</button>
    </div>
  );
}

export default App;

使用方式很简单,只需要创建一个store,使用的时候用 useBearStore传入一个回调,返回需要的数据即可。

在zustand中另一个实用的凡是subscribe,添加一个监听器,在state变化的时候会执行传入的回调函数。

    import useBearStore from "./zustand/baseStore";
    import { useEffect } from "react";
    function App() {

      const bears = useBearStore(state => state.bears)
      const increasePopulation = useBearStore(state => state.increasePopulation)

      useEffect(() => {
        //监听state的变化
        useBearStore.subscribe((state, prevState) => {
          console.log(state, prevState);
        })
      }, []);


      return (
        <div>
        {bears}  
        <button onClick={increasePopulation}> + 1</button>
        </div>
      );
    }

    export default App;

使用起来是不是很简单,redux中需要自己定义action,reducer ,然后在把action导出,要繁杂的不少。

那我们如何写一个zustand,其实zustand的原理很简单,包括源码都没有多少。

那我们如何自己写一个呢!

在这里插入图片描述

首先就是create方法接受一个回调函数,回调函数中接口set get api这么几个参数,并返回初始数据

所以写一个create方法


function create(initialState) {
  let state;

  //replace 是否替换
  function setState(partial, replace) {
    if(replace) {
      //直接替换  
      state = partial;
    } else {
      //合并老数据
      state = { ...state, ...partial };
    }
  }

  function getState() {
    return state;
  }

  const api = { setState, getState };

  state = initialState(setState, getState);
    }

之后返回的值要能通过回调返回用户想要的数据。
在这里插入图片描述

继续补全代码

function create(initialState) {
  let state;

  //replace 是否替换
  function setState(partial, replace) {
    if(replace) {
      //直接替换  
      state = partial;
    } else {
      //合并老数据
      state = { ...state, ...partial };
    }
  }

  function getState() {
    return state;
  }

  const api = { setState, getState };

  state = initialState(setState, getState);


  return (func) => {
    //执行函数将state当作参数传入
    const result = func(api.getState());
    return result;
  }
}

现在我们在继续完善subscribe订阅操作

订阅操作就是在state变化的时候,调用相应的回调

在这里插入图片描述

只要又setState的时候state会变化,所以我们只需要在封装setState方法即可,在setState的时候去对比一下前后数据,如果数据变化了则执行回调函数。

在这里插入图片描述

代码如下

function create(initialState) {
  let state;

  //监听器(存放回调函数)
  const listeners = new Set();

  //replace 是否替换
  function setState(partial, replace) {
    //获取到最新的state
    const nextState = typeof partial === 'function' ? partial(state) : partial;

    // 数据有变化
    if (!Object.is(nextState, state)) {
      //记录旧的state
      const previousState = state;
      //更新state
      if (replace) {
        state = nextState;
      } else {
        state = { ...state, ...nextState };
      }
      //执行记录的回调函数
      listeners.forEach((listener: any) => listener(state, previousState));
    }
  }

  //增加订阅方法
  function subscribe(listener) {
    listeners.add(listener);
  }
  //增加移除调用的方法,如果组件卸载了,那么他的相关订阅也许删除
  function unsubscribe(listener) {
    listeners.delete(listener);
  }

  function getState() {
    return state;
  }

  const api = { setState, getState , subscribe};

  state = initialState(setState, getState);

  const useBoundStore = (func) => {
    const result = func(state);
    return result;
  }

  //我们知道返回值是一个函数,但是它又可以调用subscribe这种api所以我们需要将api挂载到返回的函数中
  //函数是一个对象,使用Object.assign将api挂载到返回的函数中
  return Object.assign(useBoundStore, api);
}

我们都知道如果组件中使用的state的数据变化肯定是要刷新的,谁又能监听到state的变化呢,这不正好是我们刚写的subscribe吗
在这里插入图片描述

代码如下

import { useEffect } from "react";

import { useState } from "react";

function create(initialState) {
  let state;

  //监听器(存放回调函数)
  const listeners = new Set();

  //replace 是否替换
  function setState(partial, replace) {
    //获取到最新的state
    const nextState = typeof partial === 'function' ? partial(state) : partial;

    // 数据有变化
    if (!Object.is(nextState, state)) {
      //记录旧的state
      const previousState = state;
      //更新state
      if (replace) {
        state = nextState;
      } else {
        state = { ...state, ...nextState };
      }
      //执行记录的回调函数
      listeners.forEach((listener: any) => listener(state, previousState));
    }
  }

  //增加订阅方法
  function subscribe(listener) {
    listeners.add(listener);
  }

  //取消订阅
  function unsubscribe(listener) {
    listeners.delete(listener);
  }

  function getState() {
    return state;
  }

  const api = { setState, getState , subscribe, unsubscribe};

  state = initialState(setState, getState);

  const useBoundStore = (func) => {

    //增加一个useState,这样调用forceUpdate就可以自己控制组件是否刷新
    const [_, forceUpdate] = useState(0);

    useEffect(() => {
      //只要增加一个订阅,在state变化的时候看一下func返回值是否变化即可
      const listener = subscribe((state, previousState) => {
        //变化了则强制刷新
        if (func(state) !== func(previousState)) forceUpdate(Math.random());
      });
      return () => {
        //组件卸载的时候取消订阅
        unsubscribe(listener);
      }
    }, []);

    const result = func(state);
    return result;
  }

  //我们知道返回值是一个函数,但是它又可以调用subscribe这种api所以我们需要将api挂载到返回的函数中
  //函数是一个对象,使用Object.assign将api挂载到返回的函数中
  return Object.assign(useBoundStore, api);
}

我们只需要在useBearStore的时候增加一个监听,在state变化的时候调用回调函数,回调函数中判断属性是否变化即可。

在这里插入图片描述

这样就能实习一个zustand了,是不是很简单。

然后替换成自己的手写的zustand试试

import useBearStore from "./zustand/baseStore";
import { useEffect } from "react";
function App() {

  const bears = useBearStore(state => state.bears)
  const increasePopulation = useBearStore(state => state.increasePopulation)

  useEffect(() => {
    useBearStore.subscribe((state, prevState) => {
        console.log(state, prevState);
    })
}, []);


  return (
    <div>
      {bears}  
      <button onClick={increasePopulation}> + 1</button>
    </div>
  );
}

export default App;

在这里插入图片描述

完全没问题,源码也是这个思路。

看完这篇你文章是不是发现原来高高在上的库也不过如此。

相关文章:

  • LeetCode 热题 100 560. 和为 K 的子数组
  • 侯捷 C++ 课程学习笔记:内存管理的每一层面
  • Linux提权之passwd提权(七)
  • AI助力小微企业技术开发规范化管理 | 杂谈
  • Python爬虫处理网页中的动态内容
  • rust之Tokio学习1
  • 【GDB】 断点的相关设置
  • python turtle模块有哪几种命令
  • Web自动化之Selenium控制已经打开的浏览器(Chrome,Edge)
  • DPVS-4: dpvs.conf配置文件解读
  • 开源机器学习框架
  • 搭建Docker Harbor仓库
  • 【MySQL篇】持久化和非持久化统计信息的深度剖析(含analyze命令和mysqlcheck工具两种收集方式)
  • Leetcode350:两个数组的交集 II
  • 高通Camera点亮3——Camera Module
  • 记录此刻:历时两月,初步实现基于FPGA的NVMe SSD固态硬盘存储控制器设计!
  • SpringBoot+Mybatis-Plus实现动态数据源
  • Deepseek-强化学习算法(通俗易懂版)
  • 【带你 langchain 双排系列教程】9.LangChain基于RAG 实现文档问答:从入门到实战
  • inet_pton()函数的概念和使用案例
  • 做网站大量视频怎么存储/怎么去做推广
  • 宜春做网站的联系电话/好用吗
  • 网站权重最高是多少/网站建设开发简介
  • 飞浪网站建设/上百度推广的网站要多少钱
  • 网站流量统计实现/微信上如何投放广告
  • 安徽建工招采平台/英文外链seo兼职