
단순화 된 플럭스 원리를 사용하여 작고 빠르고 확장 가능한 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 ( ) 기본적으로 엄격한 평등 (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 두 번째 인수가 있습니다. 병합 대신 상태 모델을 대체합니다. 행동과 같이 의존하는 부품을 닦지 않도록주의하십시오.
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 Hook와 함께 바닐라 상점을 사용할 수 있습니다.
import { useStore } from 'zustand'
import { vanillaStore } from './vanillaStore'
const useBoundStore = ( selector ) => useStore ( vanillaStore , selector )set 또는 get 수정하는 중간 전야는 getState 및 setState 에 적용되지 않습니다.
구독 기능을 통해 구성 요소는 변경 사항에 대한 재 렌더를 강요하지 않고 상태 중심에 결합 할 수 있습니다. 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 ) ) DevTools Middleware를 사용하려면 Redux DevTools Chrome Extension을 설치하십시오.
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"}) 는 DevTools에서 "Mystore"라는 별도의 인스턴스를 만듭니다.
직렬화 옵션 : devtools(..., { serialize: { options: true } }) .
DevTools는 일반적인 결합 된 Redux Store와 달리 각 분리 된 매장에서 동작 만 기록합니다. 상점 #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가있는 타사 라이브러리에 대한 자세한 내용은 문서를 방문하십시오.