Skip to content

zustand

安装

bash
npm install zustand -S

基本使用

编写状态和动作

src/store/appStore.js

ts
/**
 * @Author: jsopy
 * @Date: 2024-12-25 13:45:52
 * @LastEditTime: 2024-12-25 14:18:23
 * @FilePath: /Zustanddemo1/src/store/appStore.js
 * @Description:切片
 * @
 */

import { create } from "zustand";

const useAppStore = create((set, get) => ({
  count: 0,
  price: 10,
  increment: (value) => {
    set((state) => {
      const result = JSON.parse(JSON.stringify(state));
      result.count += value;
      return result;
    });
  },
  decrement: (value) => {
    set((state) => {
      const result = JSON.parse(JSON.stringify(state));
      result.count -= value;
      return result;
    });
  },
  gettotal: () => {
    console.log(get().count);
    console.log(get().price);
    return get().count * get().price;
  },
}));

export default useAppStore;

使用状态和动作

src/App.tsx

jsx
import useAppStore from "./store/appStore";

function App() {
  const { count, price, increment, decrement, gettotal } = useAppStore();
  return (
    <div>
      <h1>App</h1>
      <p>{count}</p>
      <p>{price}</p>
      <p>{gettotal()}</p>
      <button onClick={() => increment(4)}>增加4个</button>
      <button onClick={() => decrement(4)}>减少4个</button>
    </div>
  );
}

export default App;

补充 ts 类型声明

jsx
import { create } from "react";
type AppleStateType = {
  price: number,
  count: number,
  increment: () => void,
  decrement: () => void,
  gettotal: () => number,
};
const useAppStore = create<AppleStateType>()((set, get) => ({
  count: 0,
  price: 10,
    increment: (value) => {
    set((state) => {
      const result = JSON.parse(JSON.stringify(state));
      result.count += value;
      return result;
    });
  },
  decrement: (value) => {
    set((state) => {
      const result = JSON.parse(JSON.stringify(state));
      result.count -= value;
      return result;
    });
  },
  gettotal: () => {
    console.log(get().count);
    console.log(get().price);
    return get().count * get().price;
  },
}));

export default useAppStore;

高级版中间件

immer

注意

immer 是一个不可变数据管理的库,可以让我们在修改数据的时候,不需要深拷贝,直接修改数据,然后返回一个新的对象

  • 安装依赖
bash
npm install immer -S
  • 使用方式
jsx
/**
 * @Author: jsopy
 * @Date: 2024-12-25 13:45:52
 * @LastEditTime: 2024-12-25 14:38:37
 * @FilePath: /Zustanddemo1/src/store/appStore.js
 * @Description:切片
 * @
 */

import { create } from "zustand";
import { immer } from "zustand/middleware/immer";
const useAppStore = create(
  immer((set, get) => ({
    count: 0,
    price: 10,
    detail: {
      name: "jsopy",
      age: 30,
    },
    increment: (value) => {
      set((state) => {
        state.count += value;
      });
    },
    decrement: (value) => {
      set((state) => {
        state.count -= value;
      });
    },
    gettotal: () => {
      console.log(get().count);
      console.log(get().price);
      return get().count * get().price;
    },
    setName: (value) => {
      set((state) => {
        state.detail.name = value;
      });
    },
    setAge: (value) => {
      set((state) => {
        state.detail.age = value;
      });
    },
  }))
);

export default useAppStore;

异步动作

(该怎么写就怎么写)

jsx
import { create } from "zustand";
import { immer } from "zustand/middleware/immer";
const useAppStore = create(
  immer((set, get) => ({
    count: 0,
    price: 10,
    detail: {
      name: "jsopy",
      age: 30,
    },
    increment: (value) => {
      set((state) => {
        state.count += value;
      });
    },
    decrement: (value) => {
      set((state) => {
        state.count -= value;
      });
    },
    gettotal: () => {
      console.log(get().count);
      console.log(get().price);
      return get().count * get().price;
    },
    setName: (value) => {
      set((state) => {
        state.detail.name = value;
      });
    },
    setAge: (value) => {
      set((state) => {
        state.detail.age = value;
      });
    },
    doubleCount: async () => {
      let result = await new Promise((resolve, reject) => resolve(2));
      set((state) => {
        state.count *= result;
      });
    },
  }))
);
  • 组件里面使用
jsx
import useAppStore from "./store/appStore";

function App() {
  const {
    count,
    price,
    increment,
    decrement,
    setName,
    setAge,
    gettotal,
    detail,
    doubleCount,
  } = useAppStore();
  return (
    <div>
      <h1>App</h1>
      <p>{count}</p>
      <p>{price}</p>

      <p>{gettotal()}</p>
      <p>{detail.name}</p>
      <p>{detail.age}</p>
      <button onClick={() => doubleCount()}>数量翻倍</button>
      <button onClick={() => increment(4)}>增加4个</button>
      <button onClick={() => decrement(4)}>减少4个</button>
      <button onClick={() => setName("李四")}>改变名字</button>
      <button onClick={() => setAge(88)}>改变年龄</button>
    </div>
  );
}

export default App;

useShallow

注意

A,B 组件 A 组件改变 stor 状态,B 组件哪怕没用 A 组件里面的状态,B 也会重新渲染.这是不对的

这个时候就需要用 useShallow

  • 使用
jsx
import { useShallow } from "zustand/shallow";
import { useAppStore } from "../store/appStore";
import { increment, decrement, doubleCount, getTotal } from "../store/appStore";
function Child1() {
  const { price, count } = useAppStore(
    useShallow((state) => {
      return {
        price: state.price,
        count: state.count,
      };
    })
  );
  return (
    <div>
      <h1>价格是{price}</h1>
      <h2>数量是{count}</h2>
      <h1
        onClick={() => {
          increment(3);
        }}
      >
        增加+3
      </h1>
      <h1
        onClick={() => {
          decrement(3);
        }}
      >
        减去-3
      </h1>
      <h1
        onClick={() => {
          doubleCount();
        }}
      >
        数量翻倍
      </h1>
      <h1>获取到总价{getTotal()}</h1>
    </div>
  );
}

export default Child1;

Redux DevTools

  • 去谷歌商店里面安装 Redux Tolls

  • 使用的时候就是 store 里面加入一层包裹

bash

devtools(回调,{enabled:true,name:"appstore"})
  • 举例
jsx

import { create } from "zustand";
import { createJSONStorage, devtools, persist } from "zustand/middleware";
import { immer } from "zustand/middleware/immer";

interface AppStore {
  price: number;
  count: number;
}
export const useAppStore = create<AppStore>()(
  immer(
    devtools(
      persist(
        () => ({
          price: 7.0,
          count: 1,
        }),
        {
          name: "appStore",
          partialize: (state) => ({ count: state.count }),
          storage: createJSONStorage(() => sessionStorage),
        }
      ),
      { enabled: true, name: "appStore2" }
    )
  )
);

持久化

就是把数据保存到缓存中,在加一层包裹层

  • 使用
jsx
immer(
  devtools(persist(回调, { name: "appstore" }), {
    enabled: true,
    name: "appStore2",
  })
);
  • 举例
jsx
import { create } from "zustand";
import { createJSONStorage, devtools, persist } from "zustand/middleware";
import { immer } from "zustand/middleware/immer";

interface AppStore {
  price: number;
  count: number;
}
export const useAppStore = create<AppStore>()(
  immer(
    devtools(
      persist(
        () => ({
          price: 7.0,
          count: 1,
        }),
        {
          name: "appStore",
          partialize: (state) => ({ count: state.count }),
          storage: createJSONStorage(() => sessionStorage),
        }
      ),
      { enabled: true, name: "appStore2" }
    )
  )
);
  • 仅存指定的属性
js
// 仅仅保存count属性
partialize: (state) => ({ count: state.count }),
  • 指定某些属性不存储

这里[price] 就是黑名单

js
partialize: (state) => {
  Object.fromEntries(
    Object.entries(state).filter(([key]) => !["price"].includes(key))
  )
},

// const obj ={aaa:"xxx",ccc:2000}
// Object.entries(obj) // 返回值 [['aaa','xxx'],['ccc',2000]]

//const arr = [['aaa','xxx'],['ccc',2000]]
//Object.fromEntries(arr) // 返回值 {aaa:'xxx',ccc:2000}
  • 指定存储位置
js
import { createJSONStorage, devtools, persist } from "zustand/middleware";
{
  name: "appStore",
  partialize: (state) => ({ count: state.count }),
  storage: createJSONStorage(() => sessionStorage),
}
  • 清除 storage
js
/* 清除本地存储,但是状态本身(内存)并没有清除
 如果此时对状态操作 会重新保存到本地存储
 所以一般直接自己写个方法 还原成初始值
*/
useAppStore.persist.clearStorage;

订阅模式(少)

警告

  1. 假设在模板中判断状态是否满足条件,满足条件就一种渲染,不满足就另外一种渲染

  2. 直接使用状态 状态只要改变 我就重新渲染,不管它在判断中是否成立

  • 改用订阅模式

状态改变,组件不刷新,只有当状态改变后并且条件判断成立的时候,再刷新

ts
// 放在useEffect中,目的是仅仅订阅一次

useEffect(() => {
  const unsub = useAppStore.subscribe((state, prevState) => {});
  return unsub;
}, []);
  • 监听具体属性
bash

immer(
  devtools(
    subscribeWithSelector(
      persist(回调,{配置})
    )
  )
)
  • 对具体属性订阅
js
useEffect(() => {
  const unsub = useAppStore.subscribe(
    state=>state.count,
    (count,prevCount)=>{...},
    {
      equalityFn:shallow, // 判断两个对象是否相等
      fireImmediately:true // 是否立即执行回调函数
    }
  );
  return unsub;
}, []);

getState/setState

把方法可以拿出来,,代码逻辑清晰了

jsx
import { create } from "zustand";
import { createJSONStorage, devtools, persist } from "zustand/middleware";
import { immer } from "zustand/middleware/immer";

export const useAppStore = create()(
  immer(
    devtools(
      persist(
        () => ({
          price: 7.0,
          count: 1,
          detail: {
            name: "jsopy",
            age: 18,
          },
        }),
        {
          name: "appStore",
          partialize: (state) => ({ count: state.count }),
          storage: createJSONStorage(() => sessionStorage),
        }
      ),
      { enabled: true, name: "appStore2" }
    )
  )
);

export const decrement = (value) => {
  useAppStore.setState((state) => {
    state.count -= value;
  });
};

export const increment = (value) => {
  useAppStore.setState((state) => {
    state.count += value;
  });
};

export const getTotal = () => {
  return useAppStore.getState().price * useAppStore.getState().count;
};

export const doubleCount = async () => {
  await new Promise((resolve, reject) => {
    resolve(2);
  });
  useAppStore.setState((state) => {
    state.count *= 2;
  });
};

export const setName = (value) => {
  useAppStore.setState((state) => {
    state.detail.name = value;
  });
};

export const setAge = (value) => {
  useAppStore.setState((state) => {
    state.detail.age = value;
  });
};
  • 组件里面使用
jsx
import { useAppStore } from "./store/appStore";
import { useShallow } from "zustand/shallow";
import {
  doubleCount,
  getTotal,
  increment,
  decrement,
  setName,
  setAge,
} from "./store/appStore";
function App() {
  const { count, price, detail } = useAppStore(
    useShallow((state) => {
      return {
        price: state.price,
        count: state.count,
        detail: state.detail,
      };
    })
  );
  return (
    <div>
      <h1>App</h1>
      <p>{count}</p>
      <p>{price}</p>
      <p>{getTotal()}</p>
      <p>{detail.name}</p>
      <p>{detail.age}</p>
      <button onClick={() => doubleCount()}>数量翻倍</button>
      <button onClick={() => increment(4)}>增加4个</button>
      <button onClick={() => decrement(4)}>减少4个</button>
      <button onClick={() => setName("李四")}>改变名字</button>
      <button onClick={() => setAge(88)}>改变年龄</button>
    </div>
  );
}

export default App;