zustand subscribeWithSelector 使用

龙陌發表於2024-03-24

zustand subscribeWithSelector 使用

Zustand 提供了一個名為 subscribeWithSelector 的高階功能,允許你在訂閱狀態時使用一個選擇器函式(selector function),類似於 Redux 的 reselect。選擇器函式能夠更高效地追蹤和計算僅關心的部分狀態,減少不必要的渲染。

下面是如何使用 subscribeWithSelector 的示例:

首先,確保你已經在專案中安裝了 zustand 並建立了一個 store:

import create from 'zustand';

const useCatStore = create(set => ({
  cats: [],
  addCat: (newCat) => set(state => ({ cats: [...state.cats, newCat] })),
  removeCat: (id) => set(state => ({ cats: state.cats.filter(cat => cat.id !== id) })),
}));

然後,你可以建立一個元件並使用 subscribeWithSelector

import { subscribeWithSelector } from 'zustand';

function CatList() {
  const selectedCats = subscribeWithSelector(useCatStore, (state) => {
    // 在這裡編寫你的選擇器函式,它會根據 store 的狀態返回你關心的資料
    return state.cats.filter(cat => cat.type === 'big');
  });

  // 當 `selectedCats` 改變時,元件將重新渲染
  // 此處 `selectedCats` 是篩選出的大貓列表

  return (
    <ul>
      {selectedCats.map(cat => (
        <li key={cat.id}>{cat.name}</li>
      ))}
    </ul>
  );
}

在這個例子中,subscribeWithSelector 接收兩個引數:

  1. store:這是你想要訂閱的 zustand store,通常是一個返回 store 的 hook,如 useCatStore
  2. selector:這是一個函式,它接收整個 store 的狀態作為引數,並返回你關心的那一部分狀態。

每次 store 的狀態發生改變時,selector 會被重新執行,只有當返回值與上一次執行的結果不同時,元件才會重新渲染。這樣可以幫助提高大型應用中的效能。

subscribe 訂閱

useEffect(() => {
	// const unsub = useFoodStore.subscribe((state, prevState) => {
	//   if (prevState.fish <= 5 && state.fish > 5) {
	//     setBgColor("lightgreen");
	//   } else if (prevState.fish > 5 && state.fish <= 5) {
	//     setBgColor("lightpink");
	//   }
	// });

	const unsub = useFoodStore.subscribe(
		(state) => state.fish,
		(fish, prevFish) => {
			// if (fish == prevFish) {
			//   if (fish <= 5) {
			//     setBgColor("lightpink");
			//   } else {
			//     setBgColor("lightgreen");
			//   }
			// }

			if (prevFish <= 5 && fish > 5) {
				setBgColor("lightgreen");
			} else if (prevFish > 5 && fish <= 5) {
				setBgColor("lightpink");
			}
		},
		{
			equalityFn: shallow,
			fireImmediately: true,
		}
	);

	return unsub;
}, []);

這段程式碼是使用 React Hooks(結合 Zustand 庫)監聽狀態變化並據此更新元件樣式背景色的示例。下面是詳細的解釋:

  1. useEffect 是 React 的內建 Hook,用於處理副作用操作,如訂閱事件、定時任務、DOM 更新等。在這個例子中,useEffect
    的第二個引數為空陣列 [],意味著這個副作用僅在元件掛載時執行一次。

  2. useFoodStore.subscribe 是來自 Zustand 庫的方法,用於訂閱狀態 store 的變化。當 store 中的狀態(state)發生變化時,提供的回撥函式會被執行。

      const unsub = useFoodStore.subscribe(
      	(state) => state.fish,
      	(fish, prevFish) => {...}
      	// 其他配置項
      );
      ```
    
    第一個引數是選擇器函式,它返回 store 中我們需要關注的特定部分(這裡是 `state.fish`,魚的數量)。
    
    第二個引數是訂閱的回撥函式,它接收兩個引數:當前的魚的數量 `fish` 和前一個魚的數量 `prevFish`。當 `fish`
    的值發生變化時,這個回撥函式會被呼叫。
    
    回撥函式內的邏輯是根據魚的數量變化來切換背景顏色。當魚的數量從少於等於5變為多於5時,背景色設定為 "lightgreen"
    ;反之,當魚的數量從多於5變為少於等於5時,背景色設定為 "lightpink"。
    
    
  3. equalityFn: shallow 表示在比較新舊狀態時使用淺比較(shallow equality check),即只有當引用地址改變時才認為狀態發生了變化

  4. fireImmediately: true 表示訂閱後立即執行一次回撥函式,無論狀態是否已經改變。

  5. 最後,useEffect 的返回函式 unsub 是一個取消訂閱的方法,當元件解除安裝時會自動執行,以防止記憶體洩漏,確保不再接收無效的狀態更新通知。

總結:這段程式碼在元件掛載時訂閱 useFoodStore 中的 fish 狀態變化,根據魚的數量變化動態更改元件的背景色,並在元件解除安裝時清理訂閱。

相關文章