
โซลูชันการจัดการของรัฐ Bearbones ขนาดเล็กเร็วและปรับขนาดได้โดยใช้หลักการฟลักซ์ที่เรียบง่าย มี API ที่สะดวกสบายบนพื้นฐานของตะขอไม่ใช่หม้อต้มน้ำหรือความเห็น
อย่าเพิกเฉยเพราะมันน่ารัก มันมีกรงเล็บค่อนข้างนานใช้เวลาในการจัดการกับข้อผิดพลาดทั่วไปเช่นปัญหาเด็กซอมบี้ที่น่ากลัวปฏิกิริยาพร้อมกันและการสูญเสียบริบทระหว่างการแสดงผลแบบผสม อาจเป็นผู้จัดการรัฐหนึ่งในพื้นที่ตอบสนองที่ได้รับสิทธิทั้งหมดเหล่านี้
คุณสามารถลองสาธิตสดได้ที่นี่
npm install zustand ร้านค้าของคุณเป็นเบ็ด! คุณสามารถใส่อะไรก็ได้: ดั้งเดิม, วัตถุ, ฟังก์ชั่น รัฐจะต้องได้รับการอัปเดตอย่างไม่เปลี่ยนแปลงและฟังก์ชั่น set รวมสถานะเพื่อช่วยเหลือ
import { create } from 'zustand'
const useBearStore = create ( ( set ) => ( {
bears : 0 ,
increasePopulation : ( ) => set ( ( state ) => ( { bears : state . bears + 1 } ) ) ,
removeAllBears : ( ) => set ( { bears : 0 } ) ,
} ) ) ใช้ตะขอทุกที่ไม่จำเป็นต้องมีผู้ให้บริการ เลือกสถานะของคุณและส่วนประกอบจะแสดงผลอีกครั้งเกี่ยวกับการเปลี่ยนแปลง
function BearCounter ( ) {
const bears = useBearStore ( ( state ) => state . bears )
return < h1 > { bears } around here ... </ h1 >
}
function Controls ( ) {
const increasePopulation = useBearStore ( ( state ) => state . increasePopulation )
return < button onClick = { increasePopulation } > one up </ button >
}คุณสามารถทำได้ แต่โปรดจำไว้ว่ามันจะทำให้ส่วนประกอบอัปเดตทุกการเปลี่ยนแปลงของรัฐ!
const state = useBearStore ( ) มันตรวจพบการเปลี่ยนแปลงด้วยความเท่าเทียมกันอย่างเข้มงวด (เก่า === ใหม่) โดยค่าเริ่มต้นซึ่งมีประสิทธิภาพสำหรับการเลือกสถานะอะตอม
const nuts = useBearStore ( ( state ) => state . nuts )
const honey = useBearStore ( ( state ) => state . honey )หากคุณต้องการสร้างวัตถุเดียวที่มีหลายตัวเลือกสถานะภายในคล้ายกับ MapStatetoprops ของ Redux คุณสามารถใช้ USESHALLOW เพื่อป้องกันการเรียกซ้ำที่ไม่จำเป็นเมื่อเอาต์พุตตัวเลือกไม่เปลี่ยนแปลงตามตื้นเท่ากัน
import { create } from 'zustand'
import { useShallow } from 'zustand/react/shallow'
const useBearStore = create ( ( set ) => ( {
nuts : 0 ,
honey : 0 ,
treats : { } ,
// ...
} ) )
// Object pick, re-renders the component when either state.nuts or state.honey change
const { nuts , honey } = useBearStore (
useShallow ( ( state ) => ( { nuts : state . nuts , honey : state . honey } ) ) ,
)
// Array pick, re-renders the component when either state.nuts or state.honey change
const [ nuts , honey ] = useBearStore (
useShallow ( ( state ) => [ state . nuts , state . honey ] ) ,
)
// Mapped picks, re-renders the component when state.treats changes in order, count or keys
const treats = useBearStore ( useShallow ( ( state ) => Object . keys ( state . treats ) ) ) สำหรับการควบคุมการแสดงผลอีกครั้งคุณอาจให้ฟังก์ชั่นความเท่าเทียมกันที่กำหนดเอง (ตัวอย่างนี้ต้องใช้การใช้งาน createWithEqualityFn )
const treats = useBearStore (
( state ) => state . treats ,
( oldTreats , newTreats ) => compare ( oldTreats , newTreats ) ,
) ฟังก์ชั่น set มีอาร์กิวเมนต์ที่สอง false โดยค่าเริ่มต้น แทนที่จะรวมเข้าด้วยกันมันจะแทนที่โมเดลสถานะ ระวังอย่าล้างชิ้นส่วนที่คุณพึ่งพาเช่นการกระทำ
import omit from 'lodash-es/omit'
const useFishStore = create ( ( set ) => ( {
salmon : 1 ,
tuna : 2 ,
deleteEverything : ( ) => set ( { } , true ) , // clears the entire store, actions included
deleteTuna : ( ) => set ( ( state ) => omit ( state , [ 'tuna' ] ) , true ) ,
} ) ) เพียงแค่โทร set เมื่อคุณพร้อม Zustand ไม่สนใจว่าการกระทำของคุณจะเป็น Async หรือไม่
const useFishStore = create ( ( set ) => ( {
fishies : { } ,
fetch : async ( pond ) => {
const response = await fetch ( pond )
set ( { fishies : await response . json ( ) } )
} ,
} ) ) set อนุญาตให้ set(state => result) แต่คุณยังสามารถเข้าถึงสถานะภายนอกผ่าน get
const useSoundStore = create ( ( set , get ) => ( {
sound : 'grunt' ,
action : ( ) => {
const sound = get ( ) . sound
. . . บางครั้งคุณต้องเข้าถึงสถานะในลักษณะที่ไม่ทำปฏิกิริยาหรือดำเนินการกับร้านค้า สำหรับกรณีเหล่านี้ตะขอที่เกิดขึ้นมีฟังก์ชั่นยูทิลิตี้ที่แนบมากับต้นแบบ
const useDogStore = create ( ( ) => ( { paw : true , snout : true , fur : true } ) )
// Getting non-reactive fresh state
const paw = useDogStore . getState ( ) . paw
// Listening to all changes, fires synchronously on every change
const unsub1 = useDogStore . subscribe ( console . log )
// Updating state, will trigger listeners
useDogStore . setState ( { paw : false } )
// Unsubscribe listeners
unsub1 ( )
// You can of course use the hook as you always would
function Component ( ) {
const paw = useDogStore ( ( state ) => state . paw )
. . . หากคุณต้องการสมัครสมาชิกกับตัวเลือก Middleware สมาชิก subscribeWithSelector จะช่วย
ด้วย subscribe มิดเดิลแวร์นี้ยอมรับลายเซ็นเพิ่มเติม:
subscribe ( selector , callback , options ?: { equalityFn , fireImmediately } ) : Unsubscribe import { subscribeWithSelector } from 'zustand/middleware'
const useDogStore = create (
subscribeWithSelector ( ( ) => ( { paw : true , snout : true , fur : true } ) ) ,
)
// Listening to selected changes, in this case when "paw" changes
const unsub2 = useDogStore . subscribe ( ( state ) => state . paw , console . log )
// Subscribe also exposes the previous value
const unsub3 = useDogStore . subscribe (
( state ) => state . paw ,
( paw , previousPaw ) => console . log ( paw , previousPaw ) ,
)
// Subscribe also supports an optional equality function
const unsub4 = useDogStore . subscribe (
( state ) => [ state . paw , state . fur ] ,
console . log ,
{ equalityFn : shallow } ,
)
// Subscribe and fire immediately
const unsub5 = useDogStore . subscribe ( ( state ) => state . paw , console . log , {
fireImmediately : true ,
} ) Zustand Core สามารถนำเข้าและใช้งานได้โดยไม่ต้องพึ่งพาปฏิกิริยา ความแตกต่างเพียงอย่างเดียวคือฟังก์ชั่นสร้างไม่ส่งคืนเบ็ด แต่ยูทิลิตี้ API
import { createStore } from 'zustand/vanilla'
const store = createStore ( ( set ) => ... )
const { getState , setState , subscribe , getInitialState } = store
export default store คุณสามารถใช้ร้านขายวานิลลาพร้อม Hook useStore ตั้งแต่ V4
import { useStore } from 'zustand'
import { vanillaStore } from './vanillaStore'
const useBoundStore = ( selector ) => useStore ( vanillaStore , selector )set หรือ get ไม่ถูกนำไปใช้กับ getState และ setState
ฟังก์ชั่นการสมัครสมาชิกช่วยให้ส่วนประกอบสามารถผูกกับสถานะของรัฐโดยไม่บังคับให้ทำการเปลี่ยนแปลงอีกครั้ง ดีที่สุดรวมเข้ากับ USEEFTECT สำหรับการยกเลิกการสมัครอัตโนมัติบน Undount สิ่งนี้สามารถสร้างผลกระทบที่รุนแรงเมื่อคุณได้รับอนุญาตให้กลายพันธุ์มุมมองโดยตรง
const useScratchStore = create ( ( set ) => ( { scratches : 0 , ... } ) )
const Component = ( ) => {
// Fetch initial state
const scratchRef = useRef ( useScratchStore . getState ( ) . scratches )
// Connect to the store on mount, disconnect on unmount, catch state-changes in a reference
useEffect ( ( ) => useScratchStore . subscribe (
state => ( scratchRef . current = state . scratches )
) , [ ] )
. . . การลดโครงสร้างที่ซ้อนกันนั้นน่าเบื่อ คุณเคยลอง Immer แล้วหรือยัง?
import { produce } from 'immer'
const useLushStore = create ( ( set ) => ( {
lush : { forest : { contains : { a : 'bear' } } } ,
clearForest : ( ) =>
set (
produce ( ( state ) => {
state . lush . forest . contains = null
} ) ,
) ,
} ) )
const clearForest = useLushStore ( ( state ) => state . clearForest )
clearForest ( )หรือมีวิธีแก้ปัญหาอื่น ๆ
คุณสามารถยืนยันข้อมูลร้านค้าของคุณโดยใช้ที่เก็บข้อมูลทุกประเภท
import { create } from 'zustand'
import { persist , createJSONStorage } from 'zustand/middleware'
const useFishStore = create (
persist (
( set , get ) => ( {
fishes : 0 ,
addAFish : ( ) => set ( { fishes : get ( ) . fishes + 1 } ) ,
} ) ,
{
name : 'food-storage' , // name of the item in the storage (must be unique)
storage : createJSONStorage ( ( ) => sessionStorage ) , // (optional) by default, 'localStorage' is used
} ,
) ,
)ดูเอกสารฉบับเต็มสำหรับมิดเดิลแวร์นี้
Immer มีให้บริการเป็นมิดเดิลแวร์ด้วย
import { create } from 'zustand'
import { immer } from 'zustand/middleware/immer'
const useBeeStore = create (
immer ( ( set ) => ( {
bees : 0 ,
addBees : ( by ) =>
set ( ( state ) => {
state . bees += by
} ) ,
} ) ) ,
) const types = { increase : 'INCREASE' , decrease : 'DECREASE' }
const reducer = ( state , { type , by = 1 } ) => {
switch ( type ) {
case types . increase :
return { grumpiness : state . grumpiness + by }
case types . decrease :
return { grumpiness : state . grumpiness - by }
}
}
const useGrumpyStore = create ( ( set ) => ( {
grumpiness : 0 ,
dispatch : ( args ) => set ( ( state ) => reducer ( state , args ) ) ,
} ) )
const dispatch = useGrumpyStore ( ( state ) => state . dispatch )
dispatch ( { type : types . increase , by : 2 } )หรือเพียงแค่ใช้ redux-middleware ของเรา มันทำให้การลดระดับหลักของคุณตั้งค่าสถานะเริ่มต้นและเพิ่มฟังก์ชั่นการจัดส่งไปยังสถานะของตัวเองและวานิลลา API
import { redux } from 'zustand/middleware'
const useGrumpyStore = create ( redux ( reducer , initialState ) ) ติดตั้งส่วนขยายของ Redux Devtools Chrome เพื่อใช้มิดเดิลแวร์ Devtools
import { devtools } from 'zustand/middleware'
// Usage with a plain action store, it will log actions as "setState"
const usePlainStore = create ( devtools ( ( set ) => ... ) )
// Usage with a redux store, it will log full action types
const useReduxStore = create ( devtools ( redux ( reducer , initialState ) ) )การเชื่อมต่อ Redux devtools หนึ่งรายการสำหรับหลาย ๆ ร้านค้า
import { devtools } from 'zustand/middleware'
// Usage with a plain action store, it will log actions as "setState"
const usePlainStore1 = create ( devtools ( ( set ) => ... , { name , store : storeName1 } ) )
const usePlainStore2 = create ( devtools ( ( set ) => ... , { name , store : storeName2 } ) )
// Usage with a redux store, it will log full action types
const useReduxStore = create ( devtools ( redux ( reducer , initialState ) ) , , { name , store : storeName3 } )
const useReduxStore = create ( devtools ( redux ( reducer , initialState ) ) , , { name , store : storeName4 } )การกำหนดชื่อการเชื่อมต่อที่แตกต่างกันจะแยกร้านค้าใน Redux Devtools นอกจากนี้ยังช่วยจัดกลุ่มร้านค้าที่แตกต่างกันในการเชื่อมต่อ Redux Devtools แยกต่างหาก
Devtools ใช้ฟังก์ชั่นร้านค้าเป็นอาร์กิวเมนต์แรกโดยทางเลือกคุณสามารถตั้งชื่อร้านค้าหรือกำหนดค่าตัวเลือกอนุกรมด้วยอาร์กิวเมนต์ที่สอง
ร้านค้าชื่อ: devtools(..., {name: "MyStore"}) ซึ่งจะสร้างอินสแตนซ์แยกต่างหากชื่อ "Mystore" ใน Devtools
ตัวเลือกที่เป็นอนุกรม: devtools(..., { serialize: { options: true } })
Devtools จะบันทึกการดำเนินการจากร้านค้าที่แยกจากกันแต่ละแห่งซึ่งแตกต่างจากร้านค้า Redux Redux แบบรวม ทั่วไป ดูวิธีการรวมร้านค้า #163
คุณสามารถบันทึกประเภทการกระทำที่เฉพาะเจาะจงสำหรับแต่ละฟังก์ชั่น set โดยผ่านพารามิเตอร์ที่สาม:
const useBearStore = create ( devtools ( ( set ) => ( {
...
eatFish : ( ) = > set (
( prev ) => ( { fishes : prev . fishes > 1 ? prev . fishes - 1 : 0 } ) ,
undefined ,
'bear/eatFish'
) ,
...นอกจากนี้คุณยังสามารถบันทึกประเภทการกระทำพร้อมกับน้ำหนักบรรทุก:
...
addFishes : ( count ) => set (
( prev ) => ( { fishes : prev . fishes + count } ) ,
undefined ,
{ type : 'bear/addFishes' , count , }
) ,
... หากไม่ได้ให้ประเภทการกระทำมันจะผิดนัดเป็น "ไม่ระบุชื่อ" คุณสามารถปรับแต่งค่าเริ่มต้นนี้ได้โดยให้พารามิเตอร์ anonymousActionType :
devtools ( ... , { anonymousActionType : 'unknown' , ... } ) หากคุณต้องการปิดการใช้งาน Devtools (เช่นการผลิต) คุณสามารถปรับแต่งการตั้งค่านี้โดยการให้พารามิเตอร์ enabled :
devtools ( ... , { enabled : false , ... } ) ร้านค้าที่สร้างขึ้นด้วย create ไม่จำเป็นต้องมีผู้ให้บริการบริบท ในบางกรณีคุณอาจต้องการใช้บริบทสำหรับการฉีดพึ่งพาหรือหากคุณต้องการเริ่มต้นร้านค้าของคุณด้วยอุปกรณ์ประกอบฉากจากส่วนประกอบ เนื่องจากร้านค้าปกติเป็นตะขอการส่งผ่านเป็นค่าบริบทปกติอาจละเมิดกฎของตะขอ
วิธีที่แนะนำเนื่องจาก V4 คือการใช้ร้านค้าวานิลลา
import { createContext , useContext } from 'react'
import { createStore , useStore } from 'zustand'
const store = createStore ( ... ) // vanilla store without hooks
const StoreContext = createContext ( )
const App = ( ) => (
< StoreContext . Provider value = { store } >
...
</ StoreContext . Provider >
)
const Component = ( ) => {
const store = useContext ( StoreContext )
const slice = useStore ( store , selector )
. . . การใช้งาน typescript พื้นฐานไม่จำเป็นต้องมีอะไรพิเศษยกเว้นการเขียน create<State>()(...) แทนที่จะ create(...) ...
import { create } from 'zustand'
import { devtools , persist } from 'zustand/middleware'
import type { } from '@redux-devtools/extension' // required for devtools typing
interface BearState {
bears : number
increase : ( by : number ) => void
}
const useBearStore = create < BearState > ( ) (
devtools (
persist (
( set ) => ( {
bears : 0 ,
increase : ( by ) => set ( ( state ) => ( { bears : state . bears + by } ) ) ,
} ) ,
{
name : 'bear-storage' ,
} ,
) ,
) ,
)คู่มือ TypeScript ที่สมบูรณ์ยิ่งขึ้นอยู่ที่นี่
ผู้ใช้บางคนอาจต้องการขยายชุดคุณสมบัติของ Zustand ซึ่งสามารถทำได้โดยใช้ไลบรารีของบุคคลที่สามที่ชุมชนทำ สำหรับข้อมูลเกี่ยวกับห้องสมุดบุคคลที่สามกับ Zustand เยี่ยมชมเอกสาร