Package Exports
- use-effect-reducer
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (use-effect-reducer) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
useEffectReducer
A React hook for managing side-effects in your reducers.
If you know how to useReducer, you already know how to useEffectReducer.
Installation
- Install it:
npm install use-effect-reducer- Import it:
import { useEffectReducer } from 'use-effect-reducer';// ...
const [state, dispatch] = useEffectReducer(someEffectReducer, initialState, {
// effects implementation
});
// Just like useReducer:
dispatch({ type: 'FETCH', user: 'David' });Quick Start
An "effect reducer" takes 3 arguments:
state- the current stateevent- the event that was dispatched to the reducerexec- a function that captures effects to be executed.
import { useEffectReducer } from 'use-effect-reducer';
// I know, I know, yet another counter example
const countReducer = (state, event, exec) => {
switch (event.type) {
case 'ADD':
exec(() => {
// "Execute" a side-effect here
console.log('Going up!');
});
return {
...state,
count: state.count + 1,
};
default:
return state;
}
};
const App = () => {
const [state, dispatch] = useEffectReducer(countReducer, { count: 0 });
return <div>Count: {state.count}</div>;
};Named Effects
A better way to make reusable effect reducers is to have effects that are named and parameterized. This is done by running exec(...) an effect object (instead of a function) and specifying that named effect's implementation as the 3rd argument to useEffectReducer(reducer, initial, effectMap).
const fetchEffectReducer = (state, event, exec) => {
switch (event.type) {
case 'FETCH':
// Capture a named effect to be executed
exec({ type: 'fetchFromAPI', user: event.user });
return {
...state,
status: 'fetching',
};
case 'RESOLVE':
return {
status: 'fulfilled',
user: event.data,
};
default:
return state;
}
};
const Fetcher = () => {
const [state, dispatch] = useEffectReducer(
fetchEffectReducer,
{ status: 'idle', user: undefined },
{
// Specify how effects are implemented
fetchFromAPI(_, effect) {
fetch(`/api/users/${effect.user}`)
.then(res => res.json())
.then(data => {
dispatch({
type: 'RESOLVE',
data,
});
});
},
}
);
return (
<button
onClick={() => {
dispatch({ type: 'FETCH', user: 42 });
}}
>
Fetch user
</div>
);
};API
useEffectReducer hook
The useEffectReducer hook takes the same first 2 arguments as the built-in useReducer hook, and returns the current state returned from the effect reducer, as well as a dispatch function for sending events to the reducer.
const SomeComponent = () => {
const [state, dispatch] = useEffectReducer(someEffectReducer, initialState);
// ...
};Additionally, the useEffectReducer hook takes a 3rd argument, which is the implementation details for named effects:
const SomeComponent = () => {
const [state, dispatch] = useEffectReducer(someEffectReducer, initialState, {
log: (state, effect) => {
console.log(state);
},
});
// ...
};TypeScript
The effect reducer can be specified as an EffectReducer<TState, TEvent, TEffect>, where the generic types are:
- The
statetype returned from the reducer - The
eventobject type that can be dispatched to the reducer - The
effectobject type that can be executed
import { useEffectReducer, EffectReducer } from 'use-effect-reducer';
interface User {
name: string;
}
type FetchState =
| {
status: 'idle';
user: undefined;
}
| {
status: 'fetching';
user: User | undefined;
}
| {
status: 'fulfilled';
user: User;
};
type FetchEvent =
| {
type: 'FETCH';
user: string;
}
| {
type: 'RESOLVE';
data: User;
};
type FetchEffect = {
type: 'fetchFromAPI';
user: string;
};
const fetchEffectReducer: EffectReducer<FetchState, FetchEvent, FetchEffect> = (
state,
event,
exec
) => {
switch (event.type) {
case 'FETCH':
// State, event, and effect types will be inferred!
// Also you should probably switch on
// `state.status` first ;-)
// ...
default:
return state;
}
};