Skip to content

Move statemangement to zustand #17

Description

@mikebarkmin

We better performance and overall better handling, we should move to zustand.

Here is the documentation on how to integrate it: https://reactflow.dev/learn/advanced-use/state-management

This would enable us to update nodes individually and not to pass function around.

We can also use the integrated persisting store middleware (https://zustand.docs.pmnd.rs/integrations/persisting-store-data).

Example:

import{create}from'zustand'import{persist,createJSONStorage}from'zustand/middleware'typeBearStore={bears: numberaddABear: ()=>void}exportconstuseBearStore=create<BearStore>()(persist((set,get)=>({bears: 0,addABear: ()=>set({bears: get().bears+1}),}),{name: 'food-storage',// name of the item in the storage (must be unique)},),)

There is also zustand undo (https://github.com/charkour/zundo). So we do not need to rely on a custom implementation.

Example

import{create}from'zustand';import{temporal}from'zundo';// Define the type of your store state (typescript)interfaceStoreState{bears: number;increasePopulation: ()=>void;removeAllBears: ()=>void;}// Use `temporal` middleware to create a store with undo/redo capabilitiesconstuseStoreWithUndo=create<StoreState>()(temporal((set)=>({bears: 0,increasePopulation: ()=>set((state)=>({bears: state.bears+1})),removeAllBears: ()=>set({bears: 0}),})),);
import{useStoreWithEqualityFn}from'zustand/traditional';importtype{TemporalState}from'zundo';functionuseTemporalStore(): TemporalState<MyState>;functionuseTemporalStore<T>(selector: (state: TemporalState<MyState>)=>T): T;functionuseTemporalStore<T>(selector: (state: TemporalState<MyState>)=>T,equality: (a: T,b: T)=>boolean,): T;functionuseTemporalStore<T>(selector?: (state: TemporalState<MyState>)=>T,equality?: (a: T,b: T)=>boolean,){returnuseStoreWithEqualityFn(useStoreWithUndo.temporal,selector!,equality);}constApp=()=>{const{ bears, increasePopulation, removeAllBears }=useStoreWithUndo();// changes to pastStates and futureStates will now trigger a reactive component rerenderconst{ undo, redo, clear, pastStates, futureStates }=useTemporalStore((state)=>state,);return(<><p> bears: {bears}</p><p> pastStates: {JSON.stringify(pastStates)}</p><p> futureStates: {JSON.stringify(futureStates)}</p><buttononClick={()=>increasePopulation}>increase</button><buttononClick={()=>removeAllBears}>remove</button><buttononClick={()=>undo()}>undo</button><buttononClick={()=>redo()}>redo</button><buttononClick={()=>clear()}>clear</button></>);};

For this we would probably create a store.ts file, which should contain all actions.

Here is an example. We want to manage the RoadmapData state instead of nodes and edges individually.

constuseStore=create<AppState>((set,get)=>({nodes: initialNodes,edges: initialEdges,onNodesChange: (changes)=>{set({nodes: applyNodeChanges(changes,get().nodes),});},onEdgesChange: (changes)=>{set({edges: applyEdgeChanges(changes,get().edges),});},onConnect: (connection)=>{set({edges: addEdge(connection,get().edges),});},setNodes: (nodes)=>{set({ nodes });},setEdges: (edges)=>{set({ edges });},updateNodeColor: (nodeId,color)=>{set({nodes: get().nodes.map((node)=>{if(node.id===nodeId&&isColorChooserNode(node)){// it's important to create a new object here, to inform React Flow about the changesreturn{ ...node,data: { ...node.data, color }};}returnnode;}),});},}));exportdefaultuseStore;

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions