UNPKG

@castore/event-storage-adapter-redux

Version:

DRY Castore EventStorageAdapter implementation using a Redux store

138 lines (101 loc) 3.86 kB
# Redux Event Storage Adapter DRY Castore [`EventStorageAdapter`](https://castore-dev.github.io/castore/docs/event-sourcing/fetching-events/) implementation using a Redux store. ## 📥 Installation ```bash # npm npm install @castore/event-storage-adapter-redux # yarn yarn add @castore/event-storage-adapter-redux ``` This package has `@castore/core`, `@reduxjs/toolkit` (above v1.9) and `react-redux` (above v8) as peer dependencies, so you will have to install them as well: ```bash # npm npm install @castore/core @reduxjs/toolkit react-redux # yarn yarn add @castore/core @reduxjs/toolkit react-redux ``` ## 👩‍💻 Usage ### Direct usage If you do not already use Redux in your app, you can simply use the `configureCastore` util: ```tsx import { Provider } from 'react-redux'; import { configureCastore } from '@castore/event-storage-adapter-redux'; const store = configureCastore({ eventStores: [pokemonsEventStore, trainersEventStore], }); const MyReactApp = () => ( <Provider store={store}> <App /> </Provider> ); ``` And that's it 🙌 `configureCastore` not only configures the Redux store but also connects it to the event stores by replacing their `eventStorageAdapter`. You can use the `pushEvent` method as usual: ```tsx const CatchPokemonButton = ({ pokemonId }) => ( <Button onClick={async () => { await pokemonsEventStore.pushEvent({ aggregateId: pokemonId, type: 'POKEMON_CAUGHT', version: currentPokemonVersion + 1, }); }} /> ); ``` You can also use the other methods, but it's simpler to use the following built-in hooks. ### Hooks You can use the `useAggregateEvents`, `useAggregate`, `useExistingAggregate` and `useAggregateIds` hooks to read data from the store. Their interface is the same as the event store methods, but synchronous. ```tsx import { useAggregateIds } from '@castore/event-storage-adapter-redux'; const AggregateIdsList = () => { // 🙌 Will synchronously return the store data, as well as hook the component to it const { aggregateIds } = useAggregateIds(pokemonsEventStore, { limit: 20 }); return aggregateIds.map(aggregateId => ( <Aggregate key={aggregateId} aggregateId={aggregateId} /> )); }; const Aggregate = ({ aggregateId }) => { const { aggregate } = useExistingAggregate(pokemonsEventStore, aggregateId); // 🙌 aggregate is correctly typed return <p>{aggregate.name}</p>; }; ``` Thanks to the magic of Redux, pushing a new event to an aggregate will only trigger re-renders of components hooked to the said aggregate. The same goes when listing aggregate ids: Only creating a new aggregate will trigger a re-render. ### Configure with another store If you already use Redux, you can merge the Castore Redux store with your own. First, know that event stores events are stored as Redux "slices". Their name is their `eventStoreId`, prefixed by a customizable string (`@castore` by default). You can use the `getCastoreReducers` util to generate the Castore Redux reducers, and merge them with your own: ```tsx import { Provider } from 'react-redux'; import { ReduxEventStorageAdapter, getCastoreReducers, } from '@castore/event-storage-adapter-redux'; const castoreReducers = getCastoreReducers({ eventStores: [pokemonsEventStore, trainersEventStore], // 👇 Optional prefix: 'customPrefix', }); const store = configureStore({ reducer: { ...castoreReducers, customReducer, }, }); // 👇 Connect the event stores to the store eventStores.forEach(eventStore => { eventStore.eventStorageAdapter = new ReduxEventStorageAdapter({ store, eventStoreId: eventStore.eventStoreId, // 👇 Don't forget the prefix if one has been provided prefix: 'customPrefix', }); }); const MyReactApp = () => ( <Provider store={store}> <App /> </Provider> ); ```