Verified Commit ce477414 authored by Florent Chehab's avatar Florent Chehab
Browse files

fix(hooks): cleaned & optimized

* Created useOnBeforeComponentMount hook
parent c1b57a4f
/** General app JS entry
*/
import React, { useMemo } from "react";
import React from "react";
import { compose } from "recompose";
import { Route, Switch } from "react-router-dom";
import FullScreenDialogServiceComponent from "../services/FullScreenDialogServiceComponent";
......@@ -34,6 +34,7 @@ import CurrencyService from "../../services/data/CurrencyService";
import LanguageService from "../../services/data/LanguageService";
import FilterService from "../../services/FilterService";
import AlertServiceComponent from "../services/AlertServiceComponent";
import useOnBeforeComponentMount from "../../hooks/useOnBeforeComponentMount";
const SERVICES_TO_INITIALIZE = [
UniversityService,
......@@ -43,17 +44,16 @@ const SERVICES_TO_INITIALIZE = [
LanguageService,
FilterService
];
// import PageFiles from "../pages/PageFiles";
/**
* Main entry
*/
function App() {
// Not using useEffect as it might not be called on first render exactly,
// Here we really need to have this stuff initialzed.
useMemo(() => {
useOnBeforeComponentMount(() => {
SERVICES_TO_INITIALIZE.forEach(service => service.initialize());
}, []);
});
return (
<div
......
......@@ -10,6 +10,8 @@ function AlertServiceComponent() {
useEffect(() => {
AlertService.setSetAlertProps(setAlertProps);
return () => AlertService.setSetAlertProps(() => {});
}, []);
const handleClose = useCallback(() => setAlertProps({ open: false }), []);
......
import { useState } from "react";
/**
* Method to use a constant value. Can also be used with a function as initialValue to
* make sure something is ran directly.
* (useMemo might be rettrigered, useEffect with no dependencies seems to have some delay).
*
* @param initialValue
* @returns {unknown}
*/
function useConstantState(initialValue) {
const [state] = useState(initialValue);
return state;
}
export default useConstantState;
......@@ -16,7 +16,7 @@ function useEditor(formInfo, onClose = () => {}) {
return useCallback(modelData => {
const { route, license, Form, formLevelErrors } = formInfo;
const InternalEditor = () => {
const InternalEditor = React.memo(() => {
const { saveData, clearSaveError } = useEditorDispatch(route);
const {
......@@ -40,7 +40,7 @@ function useEditor(formInfo, onClose = () => {}) {
onClose={onClose}
/>
);
};
});
FullScreenDialogService.openDialog(<InternalEditor />);
}, []);
......
import { useCallback, useContext, useMemo, useState } from "react";
import FormContext from "../contexts/FormContext";
import CustomError from "../components/common/CustomError";
import useConstantState from "./useConstantState";
import useOnBeforeComponentMount from "./useOnBeforeComponentMount";
/**
*
......@@ -27,8 +27,7 @@ function useField(
const [allErrors, setAllErrors] = useState(getError(valueInt));
// Hack to make sure it is run NOW
useConstantState(() => {
useOnBeforeComponentMount(() => {
formManager.fieldSubscribe(fieldMapping, allErrors, setAllErrors);
});
......
import { useRef } from "react";
/**
* Hook to make sure something is directly run and only ran during mount.
* (useMemo might be triggered again, useEffect with no dependencies seems to have some delay).
*
* @param {Function} execOnMount
* @returns {boolean} Has the method been executed?
*/
function useOnBeforeComponentMount(execOnMount) {
const ref = useRef(false);
if (ref.current === false) {
execOnMount();
ref.current = true;
}
// Has been ran?
return ref.current;
}
export default useOnBeforeComponentMount;
import { useCallback, useState } from "react";
import { useCallback, useMemo, useState } from "react";
import { getLatestRead } from "../utils/api/utils";
import useConstantState from "./useConstantState";
import useOnBeforeComponentMount from "./useOnBeforeComponentMount";
export const globalState = new Map();
......@@ -35,30 +35,26 @@ export function getPersistedValue(key) {
*/
function usePersistentState(key, initialValue) {
// initializing the global state if needed
// hack with useConstantState to make sure it is run NOW
useConstantState(() => {
useOnBeforeComponentMount(() => {
if (!globalState.has(key)) globalState.set(key, initialValue);
});
const [storedValue, setStoredValue] = useState(() => globalState.get(key));
const [state, setState] = useState(() => getPersistedValue(key));
const setValue = useCallback(
value => {
const currentGlobalValue = globalState.get(key);
const valueToStore =
value instanceof Function ? value(currentGlobalValue) : value;
const setPersistentState = useCallback(value => {
const currentGlobalValue = getPersistedValue(key);
const valueToStore =
value instanceof Function ? value(currentGlobalValue) : value;
if (currentGlobalValue !== valueToStore) {
// Save state
globalState.set(key, valueToStore);
setStoredValue(valueToStore);
}
},
[globalState]
);
// Save global state
globalState.set(key, valueToStore);
// save local state
setState(valueToStore);
}, []);
// Make sure to return the latest data
return [storedValue, setValue];
return useMemo(() => [state, setPersistentState], [state]);
}
export default usePersistentState;
......@@ -9,12 +9,13 @@ import useSharedState from "./useSharedState";
* @param {*} initialState - initial state value (if not already stored)
*/
function useSharedReducer(key, reducer, initialState) {
const [savedState, setSavedState] = useSharedState(key, initialState);
const [sharedState, setSharedState] = useSharedState(key, initialState);
const [state, dispatch] = useReducer(reducer, sharedState);
const [state, dispatch] = useReducer(reducer, savedState);
useEffect(() => {
setSavedState(state);
}, [state, setSavedState]);
setSharedState(state);
}, [state, setSharedState]);
const dispatchOut = useCallback(action => {
// built-in support for action that take dispatch as argument
......@@ -43,7 +44,7 @@ function useSharedReducer(key, reducer, initialState) {
}, []);
// make sure to return the saved state to prevent bugs when directly accessing the saved data
return useMemo(() => [savedState, dispatchOut], [savedState, dispatchOut]);
return useMemo(() => [sharedState, dispatchOut], [sharedState, dispatchOut]);
}
export default useSharedReducer;
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useCallback, useEffect, useMemo, useState } from "react";
import usePersistentState, { getPersistedValue } from "./usePersistentState";
import useConstantState from "./useConstantState";
import useOnBeforeComponentMount from "./useOnBeforeComponentMount";
/**
* @type {Map<string, Set.<function>>}
......@@ -30,10 +30,9 @@ function useSharedState(key, valueIfNoPrevious) {
listenersByKey.get(key).delete(setState);
}
};
}, []);
}, [listenersByKey]);
// hack with useMemo to make sure it is run NOW
useMemo(() => {
useOnBeforeComponentMount(() => {
// Subscribe as a listener
if (!listenersByKey.has(key)) listenersByKey.set(key, new Set());
listenersByKey.get(key).add(setState);
......@@ -41,7 +40,7 @@ function useSharedState(key, valueIfNoPrevious) {
// small check to make sure the value is coherent
const valueInStore = getPersistedValue(key);
if (valueInStore !== state) setState(valueInStore);
}, []);
});
const setStateOut = useCallback(
v => {
......@@ -53,10 +52,10 @@ function useSharedState(key, valueIfNoPrevious) {
f(v);
});
},
[listenersByKey]
[listenersByKey, setPersistedState]
);
return [state, setStateOut];
return useMemo(() => [state, setStateOut], [state, setStateOut]);
}
export default useSharedState;
......@@ -2,7 +2,7 @@ import { useEffect, useMemo, useState } from "react";
import { apiDataIsUsable, getLatestRead } from "../../utils/api/utils";
import RequestParams from "../../utils/api/RequestParams";
import { useApiRead } from "./api";
import useConstantState from "../useConstantState";
import useOnBeforeComponentMount from "../useOnBeforeComponentMount";
/**
* Hook to provide access to the data from the backend stored in the global state.
......@@ -11,7 +11,7 @@ import useConstantState from "../useConstantState";
* @param {"all"|"one"} variant - Is it a "all" or a "one" (GET all or GET one object)
* @param {RequestParams} [initialParams] - RequestParams for the first request.
* @returns {{hasError: Boolean, isLoading: Boolean, latestData: any, data: any, setParams: function}}
* @returns {{isLoading: boolean, data: ({}|Function), latestData: *, hasError: boolean, setParams: React.Dispatch<React.SetStateAction<RequestParams>>}}
*/
function useSingleApiData(
routeName,
......@@ -30,8 +30,7 @@ function useSingleApiData(
*/
// Initial load
// hack to make sure it is run NOW
useConstantState(() => {
useOnBeforeComponentMount(() => {
if (!apiDataIsUsable(data)) performRead(params);
});
......
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