Redux integration
Using redux is completely optional. However, for many it means easy integration or migration with existing projects, or just a nice centralized state management abstraction.
Integration is fairly straightforward as Reactive Data Client already uses the same paradigms as redux under the hood. However, care should be taken to integrate the reducer and middlewares properly or it won't work as expected.
First make sure you have redux installed:
- NPM
- Yarn
- esm.sh
yarn add @data-client/redux redux
npm install --save @data-client/redux redux
<script type="module">
import * from 'https://esm.sh/@data-client/redux';
import * from 'https://esm.sh/redux';
</script>
Note: react-redux is not needed for this integration (though you will need it if you want to use redux directly as well).
Then you'll want to use the <ExternalCacheProvider /> instead of <CacheProvider /> and pass in the store and a selector function to grab the Reactive Data Client specific part of the state.
You should only use ONE provider; nested another provider will override the previous.
Reactive Data Client manager middlewares return promises, which is different from how redux middlewares work.
Because of this, if you want to integrate both, you'll need to place all redux middlewares
after the PromiseifyMiddleware
adapter, and place all Reactive Data Client manager middlewares before.
- just Reactive Data Client
- with React-Redux
index.tsx
import {
SubscriptionManager,
PollingSubscription,
ExternalCacheProvider,
PromiseifyMiddleware,
applyManager,
initialState,
createReducer,
NetworkManager,
Controller,
} from '@data-client/redux';
import { createStore, applyMiddleware } from 'redux';
import ReactDOM from 'react-dom';
const networkManager = new NetworkManager();
const subscriptionManager = new SubscriptionManager(PollingSubscription);
const controller = new Controller();
const store = createStore(
createReducer(controller),
initialState,
applyMiddleware(
...applyManager([networkManager, subscriptionManager], controller),
// place Reactive Data Client built middlewares before PromiseifyMiddleware
PromiseifyMiddleware,
// place redux middlewares after PromiseifyMiddleware
),
);
const selector = state => state;
// managers optionally provide initialization subroutine
for (const manager of [networkManager, subscriptionManager]) {
manager.init?.(selector(store.getState()));
}
ReactDOM.render(
<ExternalCacheProvider
store={store}
selector={selector}
controller={controller}
>
<App />
</ExternalCacheProvider>,
document.body,
);
index.tsx
import {
SubscriptionManager,
PollingSubscription,
ExternalCacheProvider,
PromiseifyMiddleware,
applyManager,
initialState,
createReducer,
NetworkManager,
Controller,
} from '@data-client/redux';
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import ReactDOM from 'react-dom';
const manager = new NetworkManager();
const subscriptionManager = new SubscriptionManager(PollingSubscription);
const controller = new Controller();
const store = createStore(
createReducer(controller),
initialState,
applyMiddleware(
...applyManager([networkManager, subscriptionManager], controller),
// place Reactive Data Client built middlewares before PromiseifyMiddleware
PromiseifyMiddleware,
// place redux middlewares after PromiseifyMiddleware
),
);
const selector = state => state;
// managers optionally provide initialization subroutine
for (const manager of [networkManager, subscriptionManager]) {
manager.init?.(selector(store.getState()));
}
ReactDOM.render(
<ExternalCacheProvider
store={store}
selector={selector}
controller={controller}
>
<Provider store={store}>
<App />
</Provider>
</ExternalCacheProvider>,
document.body,
);
Above we have the simplest case where the entire redux store is used for Reactive Data Client.
However, more commonly you will be integrating with other state. In this case, you
will need to use the selector
prop of <ExternalCacheProvider/>
to specify
where in the state tree the Reactive Data Client information is.
// ...
const selector = state => state.dataClient;
const store = createStore(
// Now we have other reducers
combineReducers({
dataClient: dataClientReducer,
myOtherState: otherReducer,
}),
applyMiddleware(
...mapMiddleware(selector)(
...applyManager([networkManager, subscriptionManager], controller),
),
PromiseifyMiddleware,
),
);
// ...
Here we store Reactive Data Client state information in the 'dataClient' part of the tree.
Redux devtools
Redux DevTools allows easy inspection of current state and transitions in the Reactive Data Client store.
Simply wrap the return value of applyMiddleware()
with composeWithDevTools()
import { composeWithDevTools } from 'redux-devtools-extension';
const store = createStore(
createReducer(controller),
initialState,
composeWithDevTools({
trace: true,
})(
applyMiddleware(
...applyManager([networkManager, subscriptionManager], controller),
// place Reactive Data Client built middlewares before PromiseifyMiddleware
PromiseifyMiddleware,
// place redux middlewares after PromiseifyMiddleware
),
),
);