Gathering detailed insights and metrics for @bphu/zustand-react
Gathering detailed insights and metrics for @bphu/zustand-react
Gathering detailed insights and metrics for @bphu/zustand-react
Gathering detailed insights and metrics for @bphu/zustand-react
npm install @bphu/zustand-react
Typescript
Module System
Node Version
NPM Version
64.4
Supply Chain
91.8
Quality
78.7
Maintenance
50
Vulnerability
99.3
License
TypeScript (98.73%)
JavaScript (1.27%)
Total Downloads
428
Last Day
1
Last Week
6
Last Month
18
Last Year
428
1 Stars
9 Commits
1 Forks
1 Watchers
1 Branches
1 Contributors
Updated on Jan 08, 2025
Minified
Minified + Gzipped
Latest Version
0.1.4
Package Id
@bphu/zustand-react@0.1.4
Unpacked Size
27.48 kB
Size
7.01 kB
File Count
14
NPM Version
10.5.0
Node Version
18.20.2
Published on
Jan 08, 2025
Cumulative downloads
Total Downloads
Last Day
0%
1
Compared to previous day
Last Week
0%
6
Compared to previous week
Last Month
0%
18
Compared to previous month
Last Year
0%
428
Compared to previous year
1
This package provides zustand tools for React. It is lightweight and easy to use.
1npm install @bphu/zustand-react
@bphu/zustand-react
can be compatible with both zustand v5 and v4.
You can create a typed store with create
function.
1import { create } from '@bphu/zustand-react'; 2 3const useStore = create({ 4 count: 0, 5}, (set) => ({ 6 inc: () => set((state) => ({ count: state.count + 1 })), 7 dec: () => set((state) => ({ count: state.count - 1 })), 8}));
You can create a typed vanilla store with createStore
function.
1import { createStore, createStoreHook } from '@bphu/zustand-react'; 2 3const store = create({ 4 count: 0, 5}, (set) => ({ 6 inc: () => set((state) => ({ count: state.count + 1 })), 7 dec: () => set((state) => ({ count: state.count - 1 })), 8})); 9 10const useStore = createStoreHook(store);
Zustand does not provide convenient derivative state syntax like Mobx. You can use create
and createStore
function to create a store with computed state.
1import { create } from '@bphu/zustand-react'; 2 3const useStore = create({ 4 count: 0, 5}, (set) => ({ 6 inc: () => set((state) => ({ count: state.count + 1 })), 7 dec: () => set((state) => ({ count: state.count - 1 })), 8}), 9(state) => ({ 10 get double() { 11 return state.count * 2; 12 }, 13}));
The third parameter receives a function. The function parameter is the previously defined state, and the return value is the derivative state.
It is recommended to declare derivative states using
getter
syntax so that derivative calculation logic can be executed only when retrieving values.
Zustand-react provides a more concise syntax than zustand selector. You can conveniently obtain the internal state and methods of the store through the get
method.
1import { create } from '@bphu/zustand-react'; 2 3const useStore = create({ 4 count: 0, 5}, (set) => ({ 6 inc: () => set((state) => ({ count: state.count + 1 })), 7 dec: () => set((state) => ({ count: state.count - 1 })), 8}), 9(state) => ({ 10 get double() { 11 return state.count * 2; 12 }, 13})); 14 15function App() { 16 const { count, double, inc. dec } = useStore.get('count', 'double', 'inc', 'dec'); 17 18 return ... 19}
This get
syntax is provided by the selector middleware. You can also use it alone in zustand to achieve the same effect.
1import { create } from 'zustand'; 2import { selector } from '@bphu/zustand-react'; 3 4const useStore = create(selector(() => ({ 5 count: 0, 6 inc: () => set((state) => ({ count: state.count + 1 })), 7 dec: () => set((state) => ({ count: state.count - 1 })), 8}), (state) => ({ 9 get double() { 10 return state.count * 2; 11 } 12}))); 13 14function App() { 15 const { count, double, inc. dec } = useStore.get('count', 'double', 'inc', 'dec'); 16 17 return ... 18}
When we need to use props to initialize the zustand store, we need to use React.Context.
Zustand-react provides an out-of-the-box solution that allows you to implement corresponding functions with less code.
1import { createContextStore, useCreateStore } from '@bphu/zustand-react'; 2 3const { withStore, useStore } = createContextStore(function useModel({ defaultCount }: { defaultCount: number }) { 4 return useCreateStore({ 5 count: defaultCount ?? 0, 6 }, 7 (set) => ({}), 8 (state) => ({ 9 get double() { 10 return state.count * 2; 11 } 12 }) 13}); 14 15const App = withStore(function BaseApp(props: { count: number }) { 16 const { count } = useStore.get('count'); 17 18 return ... 19}, (props) => ({ 20 defaultCount: props.count, 21}));
Zustand state and React State are independent of each other. But sometimes we hope to synchronize React state to the zustand store. We may write code like this.
1import { createContextStore, useCreateStore } from '@bphu/zustand-react'; 2 3const { withStore, useStore } = createContextStore(function useModel({ unit }: { unit: string }) { 4 const store = useCreateStore({ 5 count: 0, 6 unit 7 }); 8 9 useEffect(() => { 10 const prevUnit = store.getState().unit; 11 if (prevUnit === unit) { 12 store.setState({ 13 unit 14 })); 15 } 16 }, [unit]); 17 18 return store; 19});
The code above is tedious. You can use the extend
method provided by createContextStore to quickly implement the above function.
1import { createContextStore, useCreateStore } from '@bphu/zustand-react'; 2 3const { withStore, useStore } = createContextStore(function useModel({ unit }: { unit: string }, extend) { 4 const store = useCreateStore({ 5 count: 0 6 }); 7 8 return extend(store, { 9 unit 10 }, (state) => ({ 11 get countWithUnit() { 12 return `${state.count} ${state.unit}`; 13 } 14 })); 15});
The extend method receives three parameters. The first parameter is the zustand store. The second parameter is the state that needs to be passed through. The third parameter is optional and is used for the definition of derived state.
Please ensure that the reference of the second parameter is as expected to avoid falling into an infinite loop. By default, extend internally performs a shallow comparison, but you need to ensure the reference of the properties inside the object.
The
create
/createStore
/useCreateStore
functions are just syntactic sugar for zustand. They have the combine middleware built in.
When you want to customize the middleware, you need to use the createWith
/createStoreWith
/useCreateStoreWith
function.
1import { createWith, createStoreWith } from '@bphu/zustand-react'; 2import { devtool, persist, combine } from 'zustand/middleware'; 3 4const useStore = createWith( 5 devtool(persist(combine({ 6 count: 0 7 }, (set) => ({ 8 inc: () => set((state) => ({ count: state.count + 1 })), 9 dec: () => set((state) => ({ count: state.count - 1 })), 10 })))) 11); 12 13const store = createStoreWith( 14 devtool(persist(combine({ 15 count: 0, 16 }, (set) => ({ 17 inc: () => set((state) => ({ count: state.count + 1 })), 18 dec: () => set((state) => ({ count: state.count - 1 })), 19 })))) 20);
You can reference the built-in utility functions of zustand-react through @bphu/zustand-react/utils
.
This is React.Context syntactic sugar, which can help you quickly create and use Context.
1function createContextHook<PA, RT>(useExternalStore: (props: PA) => RT): { 2 useStore: () => RT; 3 withStore<P>(Component: React.ComponentType<...>, modelProps?: PA | ((props: P) => PA)): (props: P) => React.JSX.Element; 4 withSelector<C extends React.ComponentType<...>, S extends TSelector<...>>(selector: S, Component: C, arePropsEqual?: ((prev: any, next: any) => boolean) | undefined): (props: React.ComponentProps<...> & Partial<...>) => React.JSX.Element; 5 Provider: ({ children, modelProps, }: { 6 children: React.ReactNode; 7 modelProps?: PA | undefined; 8 }) => React.JSX.Element; 9 Context: React.Context<RT>; 10}
A simple usage example:
1import { useState } from 'react'; 2import { createContextHook } from '@bphu/zustand-react/utils'; 3 4// create context 5const { withStore, useStore } = createContextHook(function useModel({ defaultCount }: { defaultCount: number }) { 6 const [count, setCount] = useState(defaultCount ?? 0); 7 8 return { 9 count, 10 setCount 11 }; 12}) 13 14// register context 15const App = withStore(function BaseApp(props: { count: number }) { 16 // use context 17 const { count } = useStore(); 18 return ... 19}, (props) => ({ 20 defaultCount: props.count, 21})); 22 23// use withSelector for performance optimization in child components 24const Child = withSelector(function Child(props: { count: number }) { 25 return <>{count}</>; 26}, (state) => ({ 27 count: state.count, 28});
This function is used to parse JSON strings. It is used to solve the problem of JSON.parse throwing errors when the string is empty.
1import { parseJSON } from '@bphu/zustand-react/utils'; 2 3const str = ''; 4const obj = parseJSON(str, {});
The parseJSON
accepts two parameters. The first parameter is the string to be parsed. The second is an optional parameter used to define the default value when parsing fails.
This hook is used to execute the effect when the state changes.
1import { useUpdateEffect } from '@bphu/zustand-react/utils'; 2 3export default () => { 4 const [count, setCount] = useState(0); 5 6 useUpdateEffect(() => { 7 console.log('count changed'); 8 }, [count]); 9 10 return <button onClick={() => setCount(c => c + 1)}> 11 increase 12 </button>; 13 ); 14};
No vulnerabilities found.
No security vulnerabilities found.