
حل حكومي صغير وسريع وقابل للتطوير باستخدام مبادئ تدفق مبسطة. يحتوي على واجهة برمجة تطبيقات مريحة على أساس السنانير ، ليست Boilerplatey أو الرأي.
لا تتجاهلها لأنها لطيفة. إنه يحتوي على المخالب تمامًا ، وقد قضيت الكثير من الوقت في التعامل مع المزالق الشائعة ، مثل مشكلة طفل الزومبي المخيف ، وتفاعل التزامن ، وفقدان السياق بين العارضين المختلطين. قد يكون مديرة الدولة الواحدة في الفضاء الذي يحصل على كل هذه الحق.
يمكنك تجربة عرض مباشر هنا.
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 لمنع Rerenders غير الضروري عندما لا يتغير ناتج المحدد وفقًا للضحلة المساواة.
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 إذا كانت أفعالك غير متزامنة أم لا.
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 )
. . . إذا كنت بحاجة إلى الاشتراك مع محدد ، فسيساعد subscribeWithSelector Middleware.
مع هذا الوسيطة ، يقبل 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 يمكنك استخدام متجر الفانيليا مع خطاف useStore المتاح منذ V4.
import { useStore } from 'zustand'
import { vanillaStore } from './vanillaStore'
const useBoundStore = ( selector ) => useStore ( vanillaStore , selector )set أو get على getState و setState .
تسمح وظيفة الاشتراك للمكونات بالربط بملفات الدولة دون إجبار إعادة تقديم التغييرات على التغييرات. أفضل دمجها مع useffect لإلغاء الاشتراك التلقائي على Unmount. يمكن أن يؤدي ذلك إلى تأثير أداء صارم عندما يُسمح لك بتطوير العرض مباشرة.
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 وظيفة المتجر كوسيطة أولى ، يمكنك اختياريًا تسمية المتجر أو تكوين خيارات التسلسل مع وسيطة ثانية.
Name Store: devtools(..., {name: "MyStore"}) ، والذي سيقوم بإنشاء مثيل منفصل يسمى "Mystore" في DevTools.
خيارات التسلسل: devtools(..., { serialize: { options: true } }) .
ستقوم DevTools بتسجيل الإجراءات فقط من كل متجر منفصل على عكس متجر 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 ، تفضل بزيارة Doc.