Package Exports
- react-native-global-state-hooks
- react-native-global-state-hooks/GlobalStore
- react-native-global-state-hooks/GlobalStoreAbstract
- react-native-global-state-hooks/asyncStorageWrapper
- react-native-global-state-hooks/createContext
- react-native-global-state-hooks/createCustomGlobalState
- react-native-global-state-hooks/createGlobalState
- react-native-global-state-hooks/generateStackHash
- react-native-global-state-hooks/getAsyncStorageItem
- react-native-global-state-hooks/isRecord
- react-native-global-state-hooks/setAsyncStorageItem
- react-native-global-state-hooks/shallowCompare
- react-native-global-state-hooks/throwWrongKeyOnActionCollectionConfig
Readme
react-native-global-state-hooks π
Effortless global state management for React & React Native! π Define a global state in just one line of code and enjoy lightweight, flexible, and scalable state management. Try it now on CodePen and see it in action! β¨
π Explore More
- Live Example π
- Video Overview π₯
- react-hooks-global-states compatible with both
React & React Native
- react-global-state-hooks specific for web applications (local-storage integration).
- react-native-global-state-hooks specific for React Native projects (async-storage integration).
π React Hooks Global States - DevTools Extension
React Hooks Global States includes a dedicated, devTools extension
to streamline your development workflow! Easily visualize, inspect, debug, and modify your application's global state in real-time right within your browser.
π Install the DevTools Extension for Chrome
πΈ DevTools Highlights
Track State Changes | Modify the State |
---|---|
![]() |
![]() |
Effortlessly monitor state updates and history. | Instantly edit global states directly from the extension. |
Restore the State | Custom Actions Granularity |
---|---|
![]() |
![]() |
Quickly revert your application to a previous state. | Precisely debug specific actions affecting state changes. |
ποΈ Async Persist Storage
To persist the global state using Async Storage, simply add the asyncStorage
option:
const useCount = createGlobalState(0, {
asyncStorage: {
key: "count",
},
});
πΉ How It Works
β
Automatically syncs the state with Async Storage if the value is serializable.
β
Provides an isAsyncStorageReady
flag to indicate when the async storage has been reviewed and committed.
β
Uses @react-native-async-storage/async-storage
by default (make sure to install this package if needed).
β
Allows custom async storage managers with asyncStorageWrapper.addAsyncStorageManager(customAsyncStorageManager)
;
Inside your components:
const [count, setCount, { isAsyncStorageReady }] = useCount();
If you specify a key in asyncStorage
, the state value persists automatically when serializable. When connecting to async storage, expect a second render that updates isAsyncStorageReady
, indicating that the storage has been reviewed and the state is committed.
π§ Custom Async Storage Manager
You can configure your own storage selection by using asyncStorageWrapper.addAsyncStorageManager
. Ensure that the manager is added before any hook is called.
index.ts
import { asyncStorageWrapper } from "react-global-state-hooks";
asyncStorageWrapper.addAsyncStorageManager(customAsyncStorageManager);
π Creating a Global State
Define a global state in one line:
import { createGlobalState } from "react-hooks-global-states/createGlobalState";
export const useCount = createGlobalState(0);
Now, use it inside a component:
const [count, setCount] = useCount();
return <Button onClick={() => setCount((count) => count + 1)}>{count}</Button>;
Works just like useState, but the state is shared globally! π
π― Selectors: Subscribing to Specific State Changes
For complex state objects, you can subscribe to specific properties instead of the entire state:
export const useContacts = createGlobalState({ entities: [], selected: new Set<number>() });
To access only the entities
property:
const [contacts] = useContacts((state) => state.entities);
return (
<ul>
{contacts.map((contact) => (
<li key={contact.id}>{contact.name}</li>
))}
</ul>
);
π Using Dependencies in Selectors
You can also add dependencies to a selector. This is useful when you want to derive state based on another piece of state (e.g., a filtered list). For example, if you're filtering contacts based on a filter
value:
const [contacts] = useContacts(
(state) => state.entities.filter((item) => item.name.includes(filter)),
[filter]
);
Alternatively, you can pass dependencies inside an options object:
const [contacts] = useContacts((state) => state.entities.filter((item) => item.name.includes(filter)), {
dependencies: [filter],
isEqualRoot: (a, b) => a.entities === b.entities,
});
Unlike Redux, where only root state changes trigger re-selection, this approach ensures that derived values recompute when dependencies change while maintaining performance.
π Reusing Selectors
π Creating a Selector
export const useContactsArray = useContacts.createSelectorHook((state) => state.entities);
export const useContactsCount = useContactsArray.createSelectorHook((entities) => entities.length);
π Using Selectors in Components
const [contacts] = useContactsArray();
const [count] = useContactsCount();
β Selectors support inline selectors and dependencies
You can still use dependencies inside a selector hook:
const [filteredContacts] = useContactsArray(
(contacts) => contacts.filter((c) => c.name.includes(filter)),
[filter]
);
β Selector hooks share the same state mutator
The stateMutator remains the same across all derived selectors, meaning actions and setState functions stay consistent.
const [actions1] = useContactsArray();
const [actions2] = useContactsCount();
console.log(actions1 === actions2); // true
π State Actions: Controlling State Modifications
Restrict state modifications by defining custom actions:
export const useContacts = createGlobalState(
{ filter: "", items: [] },
{
actions: {
async fetch() {
return async ({ setState }) => {
const items = await fetchItems();
setState({ items });
};
},
setFilter(filter: string) {
return ({ setState }) => {
setState((state) => ({ ...state, filter }));
};
},
},
}
);
Now, instead of setState
, the hook returns actions:
const [filter, { setFilter }] = useContacts();
π Accessing Global State Outside Components
Use stateControls()
to retrieve or update state outside React components:
const [contactsRetriever, contactsApi] = useContacts.stateControls();
console.log(contactsRetriever()); // Retrieves the current state
β Subscribe to changes
const unsubscribe = contactsRetriever((state) => {
console.log("State updated:", state);
});
β Subscriptions are great when one state depends on another.
const useSelectedContact = createGlobalState(null, {
callbacks: {
onInit: ({ setState, getState }) => {
contactsRetriever(
(state) => state.contacts,
(contacts) => {
if (!contacts.has(getState())) setState(null);
}
);
},
},
});
π Using Context for Scoped State
- Scoped State β Context state is isolated inside the provider.
- Same API β Context supports selectors, actions, and state controls.
π Creating a Context
import { createContext } from "react-global-state-hooks/createContext";
export const [useCounterContext, CounterProvider] = createContext(0);
Wrap your app:
<CounterProvider>
<MyComponent />
</CounterProvider>
Use the context state:
const [count] = useCounterContext();
π Context Selectors
Works just like global state, but within the provider.
π₯ Observables: Watching State Changes
Observables let you react to state changes via subscriptions.
π Creating an Observable
export const useCounter = createGlobalState(0);
export const counterLogs = useCounter.createObservable((count) => `Counter is at ${count}`);
π Subscribing to an Observable
const unsubscribe = counterLogs((message) => {
console.log(message);
});
π Using Observables Inside Context
export const [useStateControls, useObservableBuilder] = useCounterContext.stateControls();
const createObservable = useObservableBuilder();
useEffect(() => {
const unsubscribe = createObservable((count) => {
console.log(`Updated count: ${count}`);
});
return unsubscribe;
}, []);
βοΈ createGlobalState
vs. createContext
Feature | createGlobalState |
createContext |
---|---|---|
Scope | Available globally across the entire app | Scoped to the Provider where itβs used |
How to Use | const useCount = createGlobalState(0) |
const [useCountContext, Provider] = createContext(0) |
createSelectorHook | useCount.createSelectorHook |
useCountContext.createSelectorHook |
inline selectors? | β Supported | β Supported |
Custom Actions | β Supported | β Supported |
Observables | useCount.createObservable |
const [, useObservableBuilder] = useCountContext.stateControls() |
State Controls | useCount.stateControls() |
const [useStateControls] = useCountContext.stateControls() |
Best For | Global app state (auth, settings, cache) | Scoped module state, reusable component state, or state shared between child components without being fully global |
π Lifecycle Methods
Global state hooks support lifecycle callbacks for additional control.
const useData = createGlobalState(
{ value: 1 },
{
callbacks: {
onInit: ({ setState }) => {
console.log("Store initialized");
},
onStateChanged: ({ state, previousState }) => {
console.log("State changed:", previousState, "β", state);
},
computePreventStateChange: ({ state, previousState }) => {
return state.value === previousState.value;
},
},
}
);
Use onInit
for setup, onStateChanged
to listen to updates, and computePreventStateChange
to prevent unnecessary updates.
Metadata
There is a possibility to add non reactive information in the global state:
const useCount = createGlobalState(0, { metadata: { renders: 0 } });
How to use it?
const [count, , metadata] = useCount();
metadata.renders += 1;
π― Ready to Try It?
π¦ NPM Package: react-hooks-global-states
π Simplify your global state management in React & React Native today! π
Using async persist storage
const useCount = createGlobalState(0, {
asyncStorage: {
key: "count",
},
});
Inside your components
const [count, setCount, { isAsyncStorageReady }] = useCount();
- if you specify a key into the
asyncStorage
this will persist the state value if the same is serializable - when connecting to the async storage you can expect a second render which will update isAsyncStorageReady indicating that the async storage was already reviewed and the state value is committed.
The async storage default functionality depends on @react-native-async-storage/async-storage but this dependency is optional, install the package as a dependency if you want to enable persisted state.
optionally you can configure your own selection for persisting storage by using asyncStorageWrapper.addAsyncStorageManager
, notice that the manager should be added before any hook gets call;