
簡素化されたフラックス原理を使用した、小さく、高速でスケーラブルなベアボーンの状態管理ソリューション。フックに基づいた快適なAPIを持っていますが、VoilerPlateyでも意見もありません。
かわいいので無視しないでください。かなりの爪があり、恐ろしいゾンビの子供の問題、反応の同時性、混合レンダラー間のコンテキストの損失など、一般的な落とし穴に対処するのに多くの時間が費やされました。これらすべてを正しく行うのは、反応空間の1つの国家管理者かもしれません。
ここでライブデモを試すことができます。
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 ( ) デフォルトでは、厳格な平等(old === new)で変化を検出します。これは、原子状態のピックに効率的です。
const nuts = useBearStore ( ( state ) => state . nuts )
const honey = useBearStore ( ( state ) => state . honey )ReduxのMapStateTopropsと同様に、複数の状態ピックを内部に置いて単一のオブジェクトを構築する場合は、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 2番目の引数があります。マージする代わりに、状態モデルを置き換えます。アクションのように、あなたが頼っている部品を一掃しないように注意してください。
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を使用すると、fn-updates 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ミドルウェアが役立ちます。
このミドルウェアの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は、React依存性なしでインポートおよび使用できます。唯一の違いは、作成機能がフックを返すのではなく、APIユーティリティを返すことです。
import { createStore } from 'zustand/vanilla'
const store = createStore ( ( set ) => ... )
const { getState , setState , subscribe , getInitialState } = store
export default store V4以降、 useStoreフックを利用できるバニラストアを使用できます。
import { useStore } from 'zustand'
import { vanillaStore } from './vanillaStore'
const useBoundStore = ( selector ) => useStore ( vanillaStore , selector )set変更またはget MiddleWaresは、 getStateおよびsetStateに適用されないことに注意してください。
サブスクライブ関数により、コンポーネントは変更を強制することなく、状態領域にバインドできます。 Unmountの自動登録解除のための使用Effectと最適に組み合わせます。これにより、ビューを直接変異させることが許可されている場合、これにより劇的なパフォーマンスに影響を与える可能性があります。
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 )
) , [ ] )
. . . ネストされた構造を減らすことは面倒です。浸したことはありますか?
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 Extensionを取り付けて、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 ) ) )複数の店舗の1つの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はストア機能を最初の引数として受け取ります。オプションでは、ストアに名前を付けたり、2番目の引数でSerializeオプションを構成できます。
名前ストア: devtools(..., {name: "MyStore"}) 。
シリアル化オプション: devtools(..., { serialize: { options: true } }) 。
DevToolsは、典型的な複合還元剤Reduxストアとは異なり、分離した各ストアからのアクションのみを記録します。ストア#163を組み合わせるためのアプローチを参照してください
3番目のパラメーターを渡すことにより、各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はVanillaストアを使用するため、推奨される方法です。
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 )
. . . 基本的なタイプスクリプトの使用は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' ,
} ,
) ,
) ,
)より完全なタイプスクリプトガイドはこちらです。
一部のユーザーは、コミュニティが作成したサードパーティライブラリを使用して実行できるZustandの機能セットを拡張したい場合があります。 Zustandのサードパーティライブラリに関する情報については、ドキュメントをご覧ください。