Skip to content

Commit

Permalink
[breaking] CacheProvider takes array of managers
Browse files Browse the repository at this point in the history
  • Loading branch information
ntucker committed May 29, 2019
1 parent e01f1b1 commit ddb814f
Show file tree
Hide file tree
Showing 8 changed files with 41 additions and 39 deletions.
5 changes: 5 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import {
State,
FetchAction,
ReceiveAction,
Middleware,
Manager,
} from './types';
import { StateContext, DispatchContext } from './react-integration/context';

Expand Down Expand Up @@ -81,6 +83,9 @@ export type Request = RequestType;
export type FetchAction = FetchAction;
export type ReceiveAction = ReceiveAction;

export type Middleware = Middleware;
export type Manager = Manager;

export {
Resource,
CacheProvider,
Expand Down
26 changes: 12 additions & 14 deletions src/react-integration/provider/CacheProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,37 @@ import NetworkManager from '../../state/NetworkManager';
import SubscriptionManager from '../../state/SubscriptionManager';
import PollingSubscription from '../../state/PollingSubscription';
import createEnhancedReducerHook from './middleware';
import { State } from '../../types';
import { State, Manager } from '../../types';

interface ProviderProps {
children: ReactNode;
manager?: NetworkManager;
subscriptionManager?: SubscriptionManager<any>;
managers?: Manager[];
initialState?: State<unknown>;
}

/** Controller managing state of the REST cache and coordinating network requests. */
export default function CacheProvider({
children,
manager = new NetworkManager(),
subscriptionManager = new SubscriptionManager(PollingSubscription),
managers = [
new NetworkManager(),
new SubscriptionManager(PollingSubscription),
],
initialState = defaultState,
}: ProviderProps) {
// TODO: option to use redux
const useEnhancedReducer = createEnhancedReducerHook(
manager.getMiddleware(),
subscriptionManager.getMiddleware(),
...managers.map(manager => manager.getMiddleware()),
);
const [state, dispatch] = useEnhancedReducer(masterReducer, initialState);

// if we change out the manager we need to make sure it has no hanging async
useEffect(() => {
return () => {
manager.cleanup();
};
}, [manager]);
useEffect(() => {
return () => {
subscriptionManager.cleanup();
for (let i = 0; i < managers.length; ++i) {
managers[i].cleanup();
}
};
}, [subscriptionManager]);
}, managers);

return (
<DispatchContext.Provider value={dispatch}>
Expand Down
2 changes: 1 addition & 1 deletion src/react-integration/provider/__tests__/provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ describe('<CacheProvider />', () => {
expect(curDisp).toBe(dispatch);
expect(count).toBe(1);
const manager = new NetworkManager();
rerender(<CacheProvider manager={manager}>{chil}</CacheProvider>);
rerender(<CacheProvider managers={[manager]}>{chil}</CacheProvider>);
expect(curDisp).toBe(dispatch);
expect(count).toBe(1);
rerender(
Expand Down
4 changes: 2 additions & 2 deletions src/state/NetworkManager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { memoize } from 'lodash';
import { FetchAction, ReceiveAction, MiddlewareAPI } from '../types';
import { FetchAction, ReceiveAction, MiddlewareAPI, Manager } from '../types';

export const RIC: (cb: (...args: any[]) => void, options: any) => void =
typeof (global as any).requestIdleCallback === 'function'
Expand All @@ -13,7 +13,7 @@ export const RIC: (cb: (...args: any[]) => void, options: any) => void =
*
* Interfaces with store via a redux-compatible middleware.
*/
export default class NetworkManager {
export default class NetworkManager implements Manager {
protected fetched: { [k: string]: Promise<any> } = {};
protected resolvers: { [k: string]: (value?: any) => void } = {};
protected rejectors: { [k: string]: (value?: any) => void } = {};
Expand Down
10 changes: 8 additions & 2 deletions src/state/SubscriptionManager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { memoize } from 'lodash';
import { MiddlewareAPI, SubscribeAction, UnsubscribeAction } from '../types';
import {
MiddlewareAPI,
SubscribeAction,
UnsubscribeAction,
Manager,
} from '../types';
import { Schema } from '../resource';

type Actions = UnsubscribeAction | SubscribeAction;
Expand Down Expand Up @@ -29,7 +34,8 @@ export interface SubscriptionConstructable {
* Constructor takes a SubscriptionConstructable class to control how
* subscriptions are handled. (e.g., polling, websockets)
*/
export default class SubscriptionManager<S extends SubscriptionConstructable> {
export default class SubscriptionManager<S extends SubscriptionConstructable>
implements Manager {
protected subscriptions: {
[url: string]: InstanceType<S>;
} = {};
Expand Down
8 changes: 3 additions & 5 deletions src/test/makeRenderRestHook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@ import { MockNetworkManager } from './managers';
import mockInitialState, { Fixture } from './mockState';
import {
State,
NetworkManager,
SubscriptionManager,
PollingSubscription,
Manager,
} from '../index';

export default function makeRenderRestHook(
makeProvider: (
manager: NetworkManager,
subManager: SubscriptionManager<any>,
managers: Manager[],
initialState?: State<unknown>,
) => React.ComponentType<{ children: React.ReactChild }>,
) {
Expand All @@ -30,8 +29,7 @@ export default function makeRenderRestHook(
const initialState =
options && options.results && mockInitialState(options.results);
const Provider: React.ComponentType<any> = makeProvider(
manager,
subManager,
[manager, subManager],
initialState,
);
const Wrapper = options && options.wrapper;
Expand Down
20 changes: 5 additions & 15 deletions src/test/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ import { ReactNode } from 'react';
import {
State,
reducer,
NetworkManager,
SubscriptionManager,
ExternalCacheProvider,
CacheProvider,
Manager,
} from '../index';

// Extension of the DeepPartial type defined by Redux which handles unknown
Expand All @@ -18,17 +17,13 @@ type DeepPartialWithUnknown<T> = {
};

const makeExternalCacheProvider = (
manager: NetworkManager,
subscriptionManager: SubscriptionManager<any>,
managers: Manager[],
initialState?: DeepPartialWithUnknown<State<any>>,
) => {
const store = createStore(
reducer,
initialState,
applyMiddleware(
manager.getMiddleware(),
subscriptionManager.getMiddleware(),
),
applyMiddleware(...managers.map(manager => manager.getMiddleware())),
);

return ({ children }: { children: ReactNode }) => (
Expand All @@ -39,16 +34,11 @@ const makeExternalCacheProvider = (
};

const makeCacheProvider = (
manager: NetworkManager,
subscriptionManager: SubscriptionManager<any>,
managers: Manager[],
initialState?: State<unknown>,
) => {
return ({ children }: { children: ReactNode }) => (
<CacheProvider
manager={manager}
subscriptionManager={subscriptionManager}
initialState={initialState}
>
<CacheProvider managers={managers} initialState={initialState}>
{children}
</CacheProvider>
);
Expand Down
5 changes: 5 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,8 @@ export interface MiddlewareAPI<
getState: () => React.ReducerState<R>;
dispatch: React.Dispatch<React.ReducerAction<R>>;
}

export interface Manager {
getMiddleware(): Middleware;
cleanup(): void;
}

0 comments on commit ddb814f

Please sign in to comment.