alveron
Version:
Elm-inspired state management for React
39 lines (38 loc) • 2.24 kB
JavaScript
import { useEffect } from 'react';
export default function useStoreFactory(useState) {
return function useStoreWithMiddleware(middleware = []) {
return function useStore(actions, initialState, context) {
const [state, setState] = useState(initialState);
useEffect(() => middleware.forEach(({ effect }) => effect && effect(setState)), []);
const actionNames = Object.keys(actions);
const resolvedActions = actionNames.reduce((resolved, name) => {
const fn = (...payload) => {
const action = actions[name];
setState((prevState) => {
const result = action(prevState, ...payload);
// safety checks for a more convenient DX
if (!Array.isArray(result)) {
throw Error(`Wrong format returned from action ("${name.toString()}"). Expected a tuple of [newState, effect], but got ${typeof result}. Make sure to wrap your state with an additional array. See https://alveron.js.org/concepts/action`);
}
const [newState, effect, ...rest] = result;
// safety checks for a more convenient DX
if (rest.length > 0) {
throw Error(`Too many values return from an action ("${name.toString()}"). Expected a tuple of [newState, effect]. If your state is an array, make sure to wrap it with an additional array when you return it. See https://alveron.js.org/concepts/action`);
}
if (effect && typeof effect === 'function') {
effect(resolvedActions, context);
}
return middleware.reduce((newState, { middleware }) => middleware(newState, {
action: name.toString(),
payload,
prevState,
}), newState);
});
};
resolved[name] = fn;
return resolved;
}, {});
return [state, resolvedActions];
};
};
}