This is a package to easily handling global-state across your react-native-components No-redux... The library now includes @react-native-async-storage/async-storage to persist your state across sessions... if you want to keep using the old version without
npm install react-native-global-state-hooksEffortless 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! β¨
---
- 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 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.
| Track State Changes | Modify the State |
| ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| !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 |
| --------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| !Restore the State | !Custom Actions Granularity |
| Quickly revert your application to a previous state. | Precisely debug specific actions affecting state changes. |
To persist the global state using Async Storage, simply add the asyncStorage option:
``ts`
const useCount = createGlobalState(0, {
asyncStorage: {
key: "count",
},
});
β
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:
`tsx`
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.
You can configure your own storage selection by using asyncStorageWrapper.addAsyncStorageManager. Ensure that the manager is added before any hook is called.
index.ts
`ts`
import { asyncStorageWrapper } from "react-global-state-hooks";
asyncStorageWrapper.addAsyncStorageManager(customAsyncStorageManager);
Define a global state in one line:
`tsx`
import { createGlobalState } from "react-hooks-global-states/createGlobalState";
export const useCount = createGlobalState(0);
Now, use it inside a component:
`tsx`
const [count, setCount] = useCount();
return ;
Works just like useState, but the state is shared globally! π
---
For complex state objects, you can subscribe to specific properties instead of the entire state:
`tsx`
export const useContacts = createGlobalState({ entities: [], selected: new Set
To access only the entities property:
`tsx`
const [contacts] = useContacts((state) => state.entities);
return (
{contacts.map((contact) => (
))}
);
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:
`tsx`
const [contacts] = useContacts(
(state) => state.entities.filter((item) => item.name.includes(filter)),
[filter],
);
Alternatively, you can pass dependencies inside an options object:
`tsx`
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.
---
`tsx`
export const useContactsArray = useContacts.createSelectorHook((state) => state.entities);
export const useContactsCount = useContactsArray.createSelectorHook((entities) => entities.length);
`tsx`
const [contacts] = useContactsArray();
const [count] = useContactsCount();
#### β Selectors support inline selectors and dependencies
You can still use dependencies inside a selector hook:
`tsx`
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.
`tsx
const [actions1] = useContactsArray();
const [actions2] = useContactsCount();
console.log(actions1 === actions2); // true
`
---
Restrict state modifications by defining custom actions:
`tsx`
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:
`tsx`
const [filter, { setFilter }] = useContacts();
---
Use stateControls() to retrieve or update state outside React components:
`tsx`
const [contactsRetriever, contactsApi] = useContacts.stateControls();
console.log(contactsRetriever()); // Retrieves the current state
#### β Subscribe to changes
`tsx`
const unsubscribe = contactsRetriever((state) => {
console.log("State updated:", state);
});
#### β Subscriptions are great when one state depends on another.
`tsx`
const useSelectedContact = createGlobalState(null, {
callbacks: {
onInit: ({ setState, getState }) => {
contactsRetriever(
(state) => state.contacts,
(contacts) => {
if (!contacts.has(getState())) setState(null);
},
);
},
},
});
---
- Scoped State β Context state is isolated inside the provider.
- Same API β Context supports selectors, actions, and state controls.
`tsx`
import { createContext } from "react-global-state-hooks/createContext";
export const [useCounterContext, CounterProvider] = createContext(0);
Wrap your app:
`tsx`
Use the context state:
`tsx`
const [count] = useCounterContext();
Works just like global state, but within the provider.
---
Observables let you react to state changes via subscriptions.
`tsxCounter is at ${count}
export const useCounter = createGlobalState(0);
export const counterLogs = useCounter.createObservable((count) => );`
`tsx`
const unsubscribe = counterLogs((message) => {
console.log(message);
});
`tsxUpdated count: ${count}
export const [useStateControls, useObservableBuilder] = useCounterContext.stateControls();
const createObservable = useObservableBuilder();
useEffect(() => {
const unsubscribe = createObservable((count) => {
console.log();`
});
return unsubscribe;
}, []);
---
vs. createContext| Feature | createGlobalState | createContext |const useCount = createGlobalState(0)
| ---------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| Scope | Available globally across the entire app | Scoped to the Provider where itβs used |
| How to Use | | const [useCountContext, Provider] = createContext(0) |useCount.createSelectorHook
| createSelectorHook | | useCountContext.createSelectorHook |useCount.createObservable
| inline selectors? | β
Supported | β
Supported |
| Custom Actions | β
Supported | β
Supported |
| Observables | | const [, useObservableBuilder] = useCountContext.stateControls() |useCount.stateControls()
| State Controls | | 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 |
Global state hooks support lifecycle callbacks for additional control.
`tsx`
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.
There is a possibility to add non reactive information in the global state:
`tsx`
const useCount = createGlobalState(0, { metadata: { renders: 0 } });
How to use it?
`tsx
const [count, , metadata] = useCount();
metadata.renders += 1;
`
π¦ NPM Package: react-hooks-global-states
π Simplify your global state management in React & React Native today! π
`ts`
const useCount = createGlobalState(0, {
asyncStorage: {
key: "count",
},
});
Inside your components
`tsx`
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;