Skip to content

Instantly share code, notes, and snippets.

@malte-wessel
Last active February 2, 2019 09:28
Show Gist options
  • Save malte-wessel/29abe5b6fb07cdd32ed13ed793242a5e to your computer and use it in GitHub Desktop.
Save malte-wessel/29abe5b6fb07cdd32ed13ed793242a5e to your computer and use it in GitHub Desktop.
const initialState = {
resourceByHash: {},
isLoadingByHash: {},
errorByHash: {},
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'START': {
const { hash } = action.payload;
return {
...state,
isLoadingByHash: {
...state.isLoadingByHash,
[hash]: true,
},
};
}
case 'DATA': {
const { hash, data, finished } = action.payload;
return {
...state,
resourceByHash: {
...state.resourceByHash,
[hash]: data,
},
isLoadingByHash: {
...state.isLoadingByHash,
[hash]: !finished,
},
};
}
case 'ERROR': {
const { hash, error } = action.payload;
return {
...state,
errorByHash: {
...state.errorByHash,
[hash]: error,
},
isLoadingByHash: {
...state.isLoadingByHash,
[hash]: false,
},
};
}
default: {
return state;
}
}
};
const loadAction = params => ({
type: 'LOAD',
payload: {
params,
},
});
const startAction = hash => ({
type: 'START',
payload: {
hash,
},
});
const dataAction = (hash, data) => ({
type: 'DATA',
payload: {
hash,
data,
},
});
const errorAction = (hash, error) => ({
type: 'ERROR',
payload: {
hash,
error,
},
});
const defaultCreateHash = params => JSON.stringify(params);
const createUseResource = (epic, createHash = defaultCreateHash) => {
const store = createEpicStore(reducer, initialState, epic);
return params => {
const hash = createHash(params);
const selector = state => ({
resource: state.resourceByHash[hash],
isLoading: state.isLoadingByHash[hash],
error: state.errorByHash[hash],
});
const { resource, isLoading, error } = useStore(store, selector);
const load = store.dispatch(loadAction(hash));
return [load, resource, isLoading, error];
};
};
const useInfoResource = createUseResource((action, state) =>
action.pipe(
ofType('LOAD'),
map(action => [action, createHash(action.payload.params)]),
filter(([action, hash]) => {
const resource = state.value.resourceByHash[hash];
const isLoading = state.value.resourceByHash[hash];
return !resource && !isLoading;
}),
mergeMap(([action, hash]) => {
const { params } = action.payload;
const { itemId, varnish } = params;
const requestParams = {
path: `${itemId}/info.json`,
varnish,
};
return merge(
of(startAction(hash)),
createRequest({ params }).pipe(
map(response => dataAction(hash, response)),
catchError(error => errorAction(hash, error)),
),
);
}),
),
);
const Component = props => {
const { type, itemId, loggedIn } = props;
const [loadInfo, info, isLoadingInfo] = useInfoResource({
itemId,
cache: !loggedIn,
});
if (type === 'info' && !info && !isLoadingInfo) {
loadInfo();
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment