9.2.4 β€’ Published 3 months ago

react-global-state-hooks v9.2.4

Weekly downloads
10
License
MIT
Repository
github
Last release
3 months ago

react-global-state-hooks 🌟

Image John Avatar

Effortless global state management for React & React Native and Preact! πŸš€ 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


πŸš€ 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 ChangesModify the State
Track State ChangesModify the State
Effortlessly monitor state updates and history.Instantly edit global states directly from the extension.

Restore the StateCustom Actions Granularity
Restore the StateCustom Actions Granularity
Quickly revert your application to a previous state.Precisely debug specific actions affecting state changes.

πŸ—‚οΈ Persisting State with LocalStorage

To persist the global state using LocalStorage, simply add the localStorage option:

const useContacts = createGlobalState(new Map(), {
  localStorage: {
    key: 'contacts',
  },
});

πŸ›  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

FeaturecreateGlobalStatecreateContext
ScopeAvailable globally across the entire appScoped to the Provider where it’s used
How to Useconst useCount = createGlobalState(0)const [useCountContext, Provider] = createContext(0)
createSelectorHookuseCount.createSelectorHookuseCountContext.createSelectorHook
inline selectors?βœ… Supportedβœ… Supported
Custom Actionsβœ… Supportedβœ… Supported
ObservablesuseCount.createObservableconst [, useObservableBuilder] = useCountContext.stateControls()
State ControlsuseCount.stateControls()const [useStateControls] = useCountContext.stateControls()
Best ForGlobal 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! πŸš€

9.0.4

3 months ago

9.0.3

3 months ago

7.0.6

4 months ago

7.0.5

4 months ago

8.0.10

3 months ago

8.0.12

3 months ago

8.0.11

3 months ago

8.0.14

3 months ago

8.0.16

3 months ago

8.0.15

3 months ago

8.0.18

3 months ago

8.0.17

3 months ago

7.0.0

4 months ago

7.0.4

4 months ago

7.0.3

4 months ago

7.0.2

4 months ago

7.0.1

4 months ago

9.0.2

3 months ago

9.0.1

3 months ago

9.0.0

3 months ago

9.2.4

3 months ago

9.2.3

3 months ago

9.2.2

3 months ago

9.2.1

3 months ago

8.0.9

3 months ago

8.0.8

3 months ago

8.0.5

3 months ago

8.0.4

3 months ago

8.0.7

3 months ago

8.0.6

3 months ago

6.0.8

4 months ago

9.1.0

3 months ago

8.0.1

4 months ago

8.0.0

4 months ago

8.0.3

3 months ago

8.0.2

3 months ago

9.2.0

3 months ago

6.0.7

9 months ago

4.2.3

9 months ago

6.0.6

9 months ago

4.2.2

9 months ago

4.0.1

10 months ago

4.0.0

10 months ago

4.2.1

9 months ago

4.2.0

9 months ago

4.0.2

10 months ago

5.0.3

9 months ago

5.0.2

9 months ago

5.0.1

9 months ago

5.0.0

9 months ago

6.0.1

9 months ago

6.0.0

9 months ago

6.0.3

9 months ago

6.0.2

9 months ago

6.0.5

9 months ago

6.0.4

9 months ago

4.3.2

9 months ago

4.3.1

9 months ago

4.1.0

10 months ago

4.3.0

9 months ago

3.2.0

10 months ago

3.1.2

10 months ago

3.1.1

10 months ago

3.1.0

10 months ago

3.0.8

1 year ago

3.0.7

1 year ago

3.0.6

1 year ago

3.0.5

1 year ago

3.0.4

1 year ago

3.0.3

1 year ago

3.0.2

1 year ago

3.0.1

1 year ago

3.0.0

1 year ago

1.1.1

2 years ago

1.0.19

2 years ago

1.0.18

2 years ago

1.0.16

2 years ago

1.1.2

2 years ago

2.0.15

2 years ago

2.0.3

2 years ago

2.0.2

2 years ago

2.0.13

2 years ago

2.0.5

2 years ago

2.0.14

2 years ago

2.0.4

2 years ago

2.0.11

2 years ago

2.0.12

2 years ago

2.0.6

2 years ago

2.0.10

2 years ago

2.0.8

2 years ago

1.0.22

2 years ago

1.0.21

2 years ago

1.0.20

2 years ago

1.0.25

2 years ago

2.0.1

2 years ago

1.0.24

2 years ago

2.0.0

2 years ago

1.0.23

2 years ago

1.0.28

2 years ago

1.0.27

2 years ago

1.0.15

2 years ago

1.0.14

2 years ago

1.0.9

3 years ago

1.0.11

3 years ago

1.0.10

3 years ago

1.0.13

3 years ago

1.0.12

3 years ago

1.0.8

4 years ago

1.0.7

4 years ago

1.0.6

4 years ago

1.0.5

4 years ago

1.0.4

4 years ago

1.0.3

4 years ago

1.0.2

5 years ago

1.0.1

5 years ago

1.0.0

5 years ago