Verified Commit 5c0d6ab7 authored by Florent Chehab's avatar Florent Chehab
Browse files

feat(hook): tweaked useGlobalReducer

* no more state update on unmounted components 🎉
parent ef3efc31
import useGlobalState from "./useGlobalState";
import { useCallback } from "react";
import useGlobalState, { reduceGlobalState } from "./useGlobalState";
/**
* Hook that provides a shared and persistent reducer base state management
* Hook that provides reducer based globalState access / modification
*
* @param {string} key - id for the state
* @param {function} reducer - Reducer to be used
* @param {*} initialState - initial state value (if not already stored)
*/
function useSharedReducer(key, reducer, initialState) {
const [sharedState, setSharedState] = useGlobalState(key, initialState);
function useGlobalReducer(key, reducer, initialState) {
// useGlobalState will automatically handle the subscription
const [sharedState] = useGlobalState(key, initialState);
const [state, dispatch] = useReducer(reducer, sharedState);
useEffect(() => {
setSharedState(state);
}, [state, setSharedState]);
const dispatchNotFunction = useCallback(
action => {
if (process.env.NODE_ENV !== "production") {
const { type } = action;
let backgroundColor;
if (type.includes("STARTED")) backgroundColor = "cyan";
else if (type.includes("SUCCEEDED")) backgroundColor = "green";
else if (type.includes("INVALIDATED")) backgroundColor = "orange";
else if (type.includes("FAILED")) backgroundColor = "red";
else backgroundColor = "black";
// eslint-disable-next-line no-console
console.log(
`%c ACTION %c ${action.type}`,
`background: ${backgroundColor}; color: white; font-weight: bold;`,
""
);
}
dispatch(action);
},
[dispatch]
);
const dispatchNotFunction = useCallback(action => {
// Will update the global state and trigger setUpdate only on the mounted component
reduceGlobalState(key, reducer, action);
}, []);
const dispatchFunction = useCallback(
action => {
......@@ -59,8 +36,7 @@ function useSharedReducer(key, reducer, initialState) {
[dispatchFunction, dispatchNotFunction]
);
// make sure to return the saved state to prevent bugs when directly accessing the saved data
return useMemo(() => [sharedState, dispatchOut], [sharedState, dispatchOut]);
return [sharedState, dispatchOut];
}
export default useSharedReducer;
export default useGlobalReducer;
......@@ -56,6 +56,38 @@ export function updateGlobalState(key, newValue) {
}
}
/**
* Helper to reduce the value in the globalState (identified by a key)
* And trigger an update to all subscribers.
*
* @param key
* @param reducer
* @param action
*/
export function reduceGlobalState(key, reducer, action) {
if (process.env.NODE_ENV !== "production") {
const { type } = action;
let backgroundColor;
if (type.includes("STARTED")) backgroundColor = "cyan";
else if (type.includes("SUCCEEDED")) backgroundColor = "green";
else if (type.includes("INVALIDATED")) backgroundColor = "orange";
else if (type.includes("FAILED")) backgroundColor = "red";
else backgroundColor = "black";
// eslint-disable-next-line no-console
console.log(
`%c ACTION %c ${action.type}`,
`background: ${backgroundColor}; color: white; font-weight: bold;`,
""
);
}
const previousValue = getPersistedValue(key);
const newValue = reducer(previousValue, action);
updateGlobalState(key, newValue);
}
/**
* Gets the latest data retrieved from the api for the key
* @param {string} key - route / sing or plur
......
import { useCallback, useMemo } from "react";
import useSharedReducer from "../useSharedReducer";
import useGlobalReducer from "../useGlobalReducer";
import CrudReducers from "../../utils/api/CrudReducers";
import CrudActions from "../../utils/api/CrudActions";
import RequestParams from "../../utils/api/RequestParams";
......@@ -19,7 +19,7 @@ export function useApi(route, variant) {
return [internal.getForOne(), CrudReducers.defaultOneState];
}, []);
const [state, dispatch] = useSharedReducer(
const [state, dispatch] = useGlobalReducer(
`api-${route}-${variant}`,
reducer,
defaultState
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment