Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

Zustand store factory for a best-in-class developer experience.

License

NotificationsYou must be signed in to change notification settings

udecode/zustand-x

Repository files navigation

An extension forZustand that auto-generates type-safe actions, selectors, and hooks for your state. Built with TypeScript and React in mind.

Features

  • Auto-generated type-safe hooks for each state field
  • Simple patterns:store.get('name') andstore.set('name', value)
  • Extend your store with computed values usingextendSelectors
  • Add reusable actions withextendActions
  • Built-in support for devtools, persist, immer, and mutative

Why

Built on top ofzustand,zustand-x offers a better developer experience with less boilerplate. Create and interact with stores faster using a more intuitive API.

Looking for React Context-based state management instead of global state? Check outJotai X - same API, different state model.

Installation

pnpm add zustand-x

You'll also needreact andzustand installed.

Quick Start

Here's how to create a simple store:

import{createStore,useStoreState,useStoreValue}from'zustand-x';// Create a store with an initial stateconstrepoStore=createStore({name:'ZustandX',stars:0,});// Use it in your componentsfunctionRepoInfo(){constname=useStoreValue(repoStore,'name');conststars=useStoreValue(repoStore,'stars');return(<div><h1>{name}</h1><p>{stars} stars</p></div>);}functionAddStarButton(){const[,setStars]=useStoreState(repoStore,'stars');return<buttononClick={()=>setStars((s)=>s+1)}>Add star</button>;}

Core Concepts

Store Configuration

The store is where everything begins. Configure it with type-safe middleware:

import{createStore}from'zustand-x';// Types are inferred, including middleware optionsconstuserStore=createStore({name:'Alice',loggedIn:false,},{name:'user',devtools:true,// Enable Redux DevToolspersist:true,// Persist to localStoragemutative:true,// Enable immer-style mutations});

Available middleware options:

{  name:string;  devtools?:boolean|DevToolsOptions;  persist?:boolean|PersistOptions;  immer?:boolean|ImmerOptions;  mutative?:boolean|MutativeOptions;}

Reading and Writing State

The API is designed to be intuitive. Here's how you work with state:

Reading State

// Get a single valuestore.get('name');// => 'Alice'// Get the entire statestore.get('state');// Call a selector with argumentsstore.get('someSelector',1,2);

Writing State

// Set a single valuestore.set('name','Bob');// Call an actionstore.set('someAction',10);// Update multiple values at oncestore.set('state',(draft)=>{draft.name='Bob';draft.loggedIn=true;});

Subscribing State

// Subscribe to changesconstunsubscribe=store.subscribe('name',(name,previousName)=>{console.log('Name changed from',previousName,'to',name);});// Subscribe to the entire stateconstunsubscribe=store.subscribe('state',(state)=>{console.log('State changed:',state);});// Subscribe to a selector with argumentsconstunsubscribe=store.subscribe('someSelector',1,2,(result)=>{console.log('Selector result changed:',result);});// Subscribe with an additional selector and optionsconstunsubscribe=store.subscribe('name',name=>name.length,length=>console.log('Name length changed:',length),{fireImmediately:true}// Fire the callback immediately when subscribing);

React Hooks

useStoreValue(store, key, ...args)

Subscribe to a single value or selector. Optionally pass an equality function for custom comparison:

constname=useStoreValue(store,'name');// With selector argumentsconstgreeting=useStoreValue(store,'greeting','Hello');// With custom equality function for arrays/objectsconstitems=useStoreValue(store,'items',(a,b)=>a.length===b.length&&a.every((item,i)=>item.id===b[i].id));

useStoreState(store, key, [equalityFn])

Get a value and its setter, just likeuseState. Perfect for form inputs:

functionUserForm(){const[name,setName]=useStoreState(store,'name');const[email,setEmail]=useStoreState(store,'email');return(<form><inputvalue={name}onChange={(e)=>setName(e.target.value)}/><inputvalue={email}onChange={(e)=>setEmail(e.target.value)}/></form>);}

useTracked(store, key)

Subscribe to a value with minimal re-renders. Perfect for large objects where you only use a few fields:

functionUserEmail(){// Only re-renders when user.email changesconstuser=useTracked(store,'user');return<div>{user.email}</div>;}functionUserAvatar(){// Only re-renders when user.avatar changesconstuser=useTracked(store,'user');return<imgsrc={user.avatar}/>;}

useTrackedStore(store)

Get the entire store with tracking.

functionUserProfile(){// Only re-renders when accessed fields changeconststate=useTrackedStore(store);return(<div><h1>{state.user.name}</h1><p>{state.user.bio}</p>{state.isAdmin&&<AdminPanel/>}</div>);}

Extending Your Store

Adding Selectors

Selectors help you derive new values from your state. Chain them together to build complex computations:

conststore=createStore({firstName:'Jane',lastName:'Doe'},{mutative:true});constextendedStore=store.extendSelectors(({ get})=>({fullName:()=>get('firstName')+' '+get('lastName'),})).extendSelectors(({ get})=>({fancyTitle:(prefix:string)=>prefix+get('fullName').toUpperCase(),}));// Using themextendedStore.get('fullName');// => 'Jane Doe'extendedStore.get('fancyTitle','Hello ');// => 'Hello JANE DOE'

Use them in components:

functionTitle(){constfancyTitle=useStoreValue(extendedStore,'fancyTitle','Welcome ')return<h1>{fancyTitle}</h1>}

Adding Actions

Actions are functions that modify state. They can read or write state and even compose with other actions:

conststoreWithActions=store.extendActions(({ get, set,actions:{ someActionToOverride}})=>({updateName:(newName:string)=>set('name',newName),resetState:()=>{set('state',(draft)=>{draft.firstName='Jane';draft.lastName='Doe';});},someActionToOverride:()=>{// You could call the original if you want:// someActionToOverride()// then do more stuff...},}));// Using actionsstoreWithActions.set('updateName','Julia');storeWithActions.set('resetState');

Middleware Configuration

Each middleware can be enabled with a simple boolean or configured with options:

conststore=createStore({name:'ZustandX',stars:10},{name:'repo',devtools:{enabled:true},// Redux DevTools with optionspersist:{enabled:true},// localStorage with optionsmutative:true,// shorthand for { enabled: true }});

Zustand Store

Access the underlying Zustand store when needed:

// Use the original Zustand hookconstname=useStoreSelect(store,(state)=>state.name);// Get the vanilla storeconstvanillaStore=store.store;vanillaStore.getState();vanillaStore.setState({count:1});// Subscribe to changesconstunsubscribe=vanillaStore.subscribe((state)=>console.log('New state:',state));

Comparison with Zustand

// zustandimportcreatefrom'zustand'constuseStore=create((set,get)=>({count:0,increment:()=>set((state)=>({count:state.count+1})),// Computed values need manual memoizationdouble:0,setDouble:()=>set((state)=>({double:state.count*2}))}))// Componentconstcount=useStore((state)=>state.count)constincrement=useStore((state)=>state.increment)constdouble=useStore((state)=>state.double)// zustand-ximport{createStore,useStoreValue,useStoreState}from'zustand-x'conststore=createStore({count:0}).extendSelectors(({ get})=>({// Computed values are auto-memoizeddouble:()=>get('count')*2})).extendActions(({ set})=>({increment:()=>set('count',(count)=>count+1),}))// Componentconstcount=useStoreValue(store,'count')constdouble=useStoreValue(store,'double')constincrement=()=>store.set('increment')

Key differences:

  • No need to create selectors manually - they're auto-generated for each state field
  • Direct access to state fields without selector functions
  • Simpler action definitions withset('key', value) pattern
  • Type-safe by default without extra type annotations
  • Computed values are easier to define and auto-memoized withextendSelectors

Migration to v6

// Beforestore.use.name();store.get.name();store.set.name('Bob');// NowuseStoreValue(store,'name');store.get('name');store.set('name','Bob');// With selectors and actions// Beforestore.use.someSelector(42);store.set.someAction(10);// NowuseStoreValue(store,'someSelector',42);store.set('someAction',10);

License

MIT


[8]ページ先頭

©2009-2025 Movatter.jp