zippy-store
zippy-store is a lightweight, flexible, and type-safe global shared state management library for React, React Native, and JavaScript applications. It provides a simple API to manage global shared state, subscribe to state changes, and dispatch actions. With built-in support for selector, it ensures optimal performance by preventing unnecessary re-renders. Additionally, it includes seamless persistence, ensuring state is retained across sessions.
Features
- 🚀 Lightweight: Minimalistic API with zero dependencies.
- 🛠 Type-Safe: Built with TypeScript for better developer experience.
- 🎯 Selector: Extract specific parts of the state to avoid unnecessary re-renders.
- 🔄 Reactive: Automatically re-renders components when the state changes.
- 🧩 Modular: Create multiple stores for different parts of your application.
- 📦 Persistence: Built-in persistence support for JavaScript, React and React Native(AsyncStorage) apps.
- 🔗 Shared State: Share and sync state across multiple components seamlessly.
- 📊 Global Access: Access and update state globally from any component.
- 📤 State Synchronization: Ensure consistent data flow between different UI sections.
- ⚡️ Async Support: Handle asynchronous state updates effortlessly.
Installation
You can install zippy-store via npm:
npm install zippy-store
or
yarn add zippy-store
Usage
Creating a Store in React and React Native
You can create a store using the create
function and use it in components.
import { create } from "zippy-store";
const useCounterTimerStore = create("counterTimerStore", (set, get) => ({ //set for setting the state and get for getting the state
counter: 0,
timer: 60,
user: { name: "John Doe", age: 30 },
incrementCounter: () => set((state) => ({ counter: state.counter + 1 })), // return the new state in set function with callback
decrementTimer: () => {
const { timer } = get(); // get the current value of timer
return set({ timer: timer - 1 }); //can also pass object in set directly
},
}));
export default useCounterTimerStore;
Example 1: Using Full State
import React from "react";
import useCounterTimerStore from "./useCounterTimerStore";
const Example_1 = () => {
const { counter, timer, dispatch } = useCounterTimerStore();
return (
<div>
<h4>Example_1</h4>
<h2>Counter Value : {counter}</h2>
<h2>Timer Value: {timer}</h2>
<div>
<button onClick={dispatch.incrementCounter}>Increment Counter</button>
<button style={{ marginLeft: 10 }} onClick={dispatch.decrementTimer}>
Decrement Timer
</button>
</div>
</div>
);
};
export default Example_1;
Example 2: Using Selector to Optimize Re-renders
import React from "react";
import useCounterTimerStore from "./useCounterTimerStore";
const Example_2 = () => {
const { counter, user_name, dispatch } = useCounterTimerStore((state) => ({ counter: state.counter, user_name: state.user.name })); // using selector
return (
<div>
<h4>Example_2</h4>
<h2>User Name : {user_name}</h2>
<h2>Counter Value : {counter}</h2>
<div>
<button onClick={dispatch.incrementCounter}>Increment Counter</button>
<button style={{ marginLeft: 10 }} onClick={dispatch.decrementTimer}>
Decrement Timer
</button>
</div>
</div>
);
};
export default Example_2;
Example 3: Sharing State Between Components
Multiple components can share the same store and stay in sync with state updates.
import { create, store } from "zippy-store";
const useAuthStore = create("authStore", (set, get) => ({
isAuthenticated: false,
user: null,
login: (user) => set({ isAuthenticated: true, user }),
logout: () => set({ isAuthenticated: false, user: null }),
}));
import React from "react";
import useAuthStore from "./authStore"; // Assuming the store is in authStore.ts
const Header = () => {
const { isAuthenticated, user, dispatch } = useAuthStore();
return (
<header>
{isAuthenticated ? (
<div>
Welcome, {user?.name}!
<button onClick={dispatch.logout}>Logout</button>
</div>
) : (
<button onClick={() => dispatch.login({ name: "Test User", email: "test@example.com" })}>Login</button>
)}
</header>
);
};
const Profile = () => {
const { isAuthenticated, user } = useAuthStore();
return (
<div>
<h2>Profile</h2>
{isAuthenticated ? (
<p>Email: {user?.email}</p>
) : (
<p>Please login to view your profile.</p>
)}
</div>
);
};
Example 4: Async Support
zippy-store supports asynchronous state updates in actions.
import { create } from "zippy-store";
const useMoviesStore = create((set) => ({
data: {},
fetch: async () => {
const response = await someApiCall()
set({ data: respoonse.data })
},
}))
// or
const useMoviesStore = create((set) => ({
data: {},
fetch: () => {
set(async () => {
const response = await someApiCall()
return { data: respoonse.data }
})
},
}))
Example 4: Persistence Support
zippy-store supports persistence for React and JavaScript apps (React Native is not supported yet).
import { create } from "zippy-store";
const usePersistentStore = create("persistentStore", (set, get) => ({
theme: "light",
setTheme: (theme: string) => set(() => ({ theme })),
}), true); // Enable persistence with true as the third parameter
Creating a Store and using it directly in JavaScript, React and React Native apps
You can access the underlying store object for more advanced use cases and in non-React JavaScript environments(non React Components).
import { store } from 'zippy-store';
const { dispatch } = store.createStore('counterTimerStore', (set, get) => ({
counter: 0,
timer: 60,
user: { name: "John Doe", age: 30 },
incrementCounter: () => set((state) => ({ counter: state.counter + 1 })),
decrementTimer: () => set((state) => ({ timer: state.timer - 1 })),
}));
// Get the current state
const counterTimerState = store.getState('counterTimerStore');
// Subscribe to changes
const unsubscribe = store.subscribe('counterTimerStore', (newState) => {
console.log('Store updated:', newState);
});
// Update the state directly
dispatch.incrementCounter();
// or
const actions = store.getActions('counterTimerStore');
actions.incrementCounter();
// or
store.setState('counterTimerStore', (state) => ({ counter: state.counter + 1 }));
// Unsubscribe when done
unsubscribe();
API
create(storeKey: string, stateAndActionsFn: (set, get) => State & Partial<Actions>, persist?: boolean): Hook
Creates a new store for the given storeKey
.
Arguments:
storeKey
: Unique identifier for the store.stateAndActionsFn
: A function with the initial state and actions.set
allows updating the state.get
allows accessing the state.persist
: Boolean flag to enable persistence (default:false
).
Returns:
- A hook that can be used in React components to access the state and actions.
store
The global store object with the following methods:
store.createStore(key: string, stateAndActionsFn: (setState) => State & Actions, persist?: boolean): State
. Initializes the store with the givenkey
andstateAndActionsFn
and returns the dispatch object with actions.store.getActions(key: string): Actions
Returns the current actions of a givenstoreKey
.store.getState(key: string): State
Returns the current state of a givenstoreKey
.store.setState(key: string, newStateCb: (state) => Partial<State>)
Updates the state for a givenstoreKey
.store.subscribe(key: string, callback: (newState) => void): UnsubscribeFn
Subscribes to state changes of a givenstoreKey
and returns an unsubscribe function.
License
MIT
Powered by Harish Ponna @2025