-
-
Notifications
You must be signed in to change notification settings - Fork 0
Closed
Description
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'
type BearStore = {
bears: number
addABear: () => void
}
export const useBearStore = 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)
interface StoreState {
bears: number;
increasePopulation: () => void;
removeAllBears: () => void;
}
// Use `temporal` middleware to create a store with undo/redo capabilities
const useStoreWithUndo = create<StoreState>()(
temporal((set) => ({
bears: 0,
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
removeAllBears: () => set({ bears: 0 }),
})),
);import { useStoreWithEqualityFn } from 'zustand/traditional';
import type { TemporalState } from 'zundo';
function useTemporalStore(): TemporalState<MyState>;
function useTemporalStore<T>(selector: (state: TemporalState<MyState>) => T): T;
function useTemporalStore<T>(
selector: (state: TemporalState<MyState>) => T,
equality: (a: T, b: T) => boolean,
): T;
function useTemporalStore<T>(
selector?: (state: TemporalState<MyState>) => T,
equality?: (a: T, b: T) => boolean,
) {
return useStoreWithEqualityFn(useStoreWithUndo.temporal, selector!, equality);
}
const App = () => {
const { bears, increasePopulation, removeAllBears } = useStoreWithUndo();
// changes to pastStates and futureStates will now trigger a reactive component rerender
const { 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>
<button onClick={() => increasePopulation}>increase</button>
<button onClick={() => removeAllBears}>remove</button>
<button onClick={() => undo()}>undo</button>
<button onClick={() => redo()}>redo</button>
<button onClick={() => 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.
const useStore = 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 changes
return { ...node, data: { ...node.data, color } };
}
return node;
}),
});
},
}));
export default useStore;Copilot
Metadata
Metadata
Assignees
Labels
No labels