Skip to main content

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 install --save @data-client/redux redux

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.

Note

You should only use ONE provider; nested another provider will override the previous.

Note

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.

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,
);

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
),
),
);