zustand-di

dependency injection for zustand with react context. initialize zustand stores with props.

Build Size Version Downloads

Installation

npm install react zustand zustand-di

Note: zustand-di requires react and zustand as peer dependencies.

For zustand@4.1.0 and higher, you need zustand-di@0.0.7 or higher.

For zustand@~4.0.0, you need zustand-di@0.0.6 or lower.

Usage

import { create } from "zustand";
import { createContext } from "zustand-di";

// 0. (TypeScript Only) Define a store
type CounterState = {
  count: number;
  inc: () => void;
};

// 1. Create the context
const [Provider, useStore] = createContext<CounterState>();

// 2. Create the store
const createStore = (initialState: { count: number }) =>
  create<CounterState>((set) => ({
    count: initialState.count,
    inc: () => set((state) => ({ count: state.count + 1 })),
  }));

// 3. Use the store in a child component
function Counter() {
  const { count, inc } = useStore((state) => state);
  return (
    <>
      <button onClick={inc}>increment</button>
      <div>count: {count}</div>
    <>
  );
}

// 4. Use the store in the app and pass in the store creator
const myInitialState = { count: 5 };

function App() {
  return (
    <Provider createStore={() => createStore(myInitialState)}>
      <Counter />
    </Provider>
  );
}

Motivation

This package was originally inspired by createContext from zustand/context that was deprecated in v4. This package is a simplified version of that implementation:

  • Removes useStoreApi and forces the use of a selector.
  • Uses modern typescript features to simplify the code and reduce bundle size.
  • Returns an array of the Provider and useStore hook to reduce bundle size and improve DX.

For a detailed explanation, check out TkDoDo's blog post.

Why is Dependency Injection useful within React?

  • You can pass in props to the store creator.

API

createContext

This is the only export from the package. It creates a context and returns a Provider and useStore hook.

interface State {
  count: number;
}

const [Provider, useStore] = createContext<State>();

Provider

The Provider component is used to wrap the application and initialize the store.

<Provider createStore={createStore}>
  <App />
</Provider>

If you have default props, you can pass them to the Provider component.

<Provider createStore={() => createStore(myInitialState)}>
  <MyComponent />
</Provider>

useStore

The useStore hook is used to access the store in a child component. Be sure that the child component is wrapped in the Provider component.

function MyComponent = () => {
  const storeState = useStore((state) => state);
  return <div>{storeState.count}</div>;
};