@corndev/store-react
v1.0.0-beta.4
Published
React bindings for @corndev/store — and for any store with getState/subscribe, including Redux
Maintainers
Readme
@corndev/store-react
React bindings for @corndev/store
— and for any store with getState and subscribe, including a real Redux
store. ~0.6 KB min+gzip.
npm install @corndev/store @corndev/store-reactreact >= 18 is a peer dependency (the hooks are built on useSyncExternalStore).
Usage
import { createStore, createSlice } from '@corndev/store';
import { StoreProvider, useSelector, useDispatch } from '@corndev/store-react';
const todos = createSlice({
name: 'todos',
initialState: { items: [] },
reducers: {
added: (state, action) => ({ items: [...state.items, action.payload] }),
cleared: () => ({ items: [] }),
},
});
const store = createStore(todos.reducer);
export default function App() {
return (
<StoreProvider store={store}>
<TodoCount />
</StoreProvider>
);
}
function TodoCount() {
const count = useSelector(state => state.items.length);
const dispatch = useDispatch();
return <button onClick={() => dispatch(todos.actions.cleared())}>Clear {count}</button>;
}API
<StoreProvider store={store}>
Provides a store to the tree. store needs getState and subscribe;
dispatch is required only if something below calls useDispatch.
useSelector(select, options?)
Subscribes to a slice of state and re-renders when it changes.
const count = useSelector(state => state.items.length);
const slice = useSelector(state => ({ n: state.n }), { equals: shallowEqual });
const data = useSelector(state => state.data, { store: someOtherSource });| Option | Default | What it does |
|---|---|---|
| equals | Object.is | Change test. Pass a shallow comparison for object-returning selectors. |
| store | the provider's | Read this source instead — how you read a query observer, or a second store, without another context. |
Selectors that build an object re-render on every change unless you supply
equals, or memoise the selector withcreateSelector. That is the same trade react-redux makes, for the same reason.
useDispatch()
The store's dispatch. Stable for the life of the store, so it is safe in a
dependency array.
useStore()
The store itself, for imperative work.
It binds more than this store
The hooks are written against { getState, subscribe } rather than against
createStore. Three things follow, and all three are covered by tests:
1. They bind a real Redux store. Adopt the hooks first and swap the store later, or never:
import { createStore } from 'redux'; // still Redux
import { StoreProvider, useSelector } from '@corndev/store-react';
<StoreProvider store={createStore(rootReducer)}>…</StoreProvider>2. They read a @corndev/query observer with no adapter, and with no
dependency between the two packages:
import { createQueryClient } from '@corndev/query';
import { useSelector } from '@corndev/store-react';
const todos = createQueryClient().watchQuery({ queryKey: ['todos'], queryFn });
function Todos() {
const status = useSelector(state => state.status, { store: todos });
return <p>{status}</p>;
}3. They read anything else you write that exposes those two methods.
Licence
MIT © Corndog Development LLC
