Skip to content

withsprinkles/store

Repository files navigation

Sprinkles Store

This React store uses a simple EventTarget-backed implementation to prove out the API for React's upcoming concurrent store (types).

For more info on the React Team's plans for this API, check out the React Labs post:

screenshot of the aforementioned concurrent stores section of the React Labs post

API

The store API only differs from React's proposed concurrent store API in that the hook to access the store inside of components is named useStore(), because, well, use() is taken.

export interface Store<out Value, in Action> {
	// private brand because only values from `createStore` are useable,
	// not arbitrary objects matching the shape.
	[STORE]: never;
	update: (action: Action) => void;
}

export function createStore<Value>(initialValue: Value): Store<Value, Value>;
export function createStore<Value>(
	initialValue: Value,
	reducer: (previousValue: Value) => Value,
): Store<Value, void>;
export function createStore<Value, Action>(
	initialValue: Value,
	reducer?: (previousValue: Value, action: Action) => Value,
): Store<Value, Action>;

// for React's built-in concurrent store, this will just be `use()` as it exists today
export function useStore<Value, Action>(store: Store<Value, Action>): Value;

You can see an example usage of this store API in the demo application at /apps/sprinkles-doughnut-shoppe with a live example here.

Usage

Create a store:

import { createStore } from "@withsprinkles/store";

export type ViewState = { category: string };

export const viewStore = createStore<ViewState, Partial<ViewState>>(
	{ category: "All" },
	(state, updates) => ({ ...state, ...updates }),
);

Use your store in a component with the hook and it'll update automatically:

import { useStore } from "@withsprinkles/store";
import { doughnuts } from "~/lib/data";

export function ShoppingApp() {
	const view = useStore(viewStore);

	const categories = [
		"All",
		...Array.from(new Set(doughnuts.map((d) => d.category))),
	];
    
	const filtered =
		view.category === "All"
			? doughnuts
			: doughnuts.filter((d) => d.category === view.category);

    	return (
			<main>
                <div>
                    {categories.map((category) => (
                        <button
                            key={category}
                            type="button"
                            onClick={() => viewStore.update({ category: category })}
                            className={view.category === category
                                    ? "bg-pink-600 text-white"
                                    : "bg-pink-100 text-pink-800"}
                        >
                            {category}
                        </button>
                    ))}
                </div>

				<section>
					{filtered.map((product) => (
						<ProductCard key={product.id} product={product} />
					))}
				</section>
			</main>
	);
}

About

Global React Store API

Resources

Stars

Watchers

Forks

Contributors

Languages