[IMP] hooks: introduce useEffect

Co-Authored-By: Samuel Degueldre <sad@odoo.com>
This commit is contained in:
Bruno Boi
2021-11-15 14:53:40 +01:00
committed by Géry Debongnie
parent fa426b7fc0
commit 3ceb118ace
4 changed files with 277 additions and 16 deletions
+51
View File
@@ -1,5 +1,6 @@
import type { Env } from "./app/app";
import { getCurrent } from "./component/component_node";
import { onMounted, onPatched, onWillPatch, onWillUnmount } from "./component/lifecycle_hooks";
// -----------------------------------------------------------------------------
// useRef
@@ -39,3 +40,53 @@ export function useSubEnv(envExtension: Env) {
const node = getCurrent()!;
node.childEnv = Object.freeze(Object.assign({}, node.childEnv, envExtension));
}
// -----------------------------------------------------------------------------
// useEffect
// -----------------------------------------------------------------------------
const NO_OP = () => {};
/**
* @param {...any} dependencies the dependencies computed by computeDependencies
* @returns {void|(()=>void)} a cleanup function that reverses the side
* effects of the effect callback.
*/
type Effect = (...dependencies: any[]) => void | (() => void);
/**
* This hook will run a callback when a component is mounted and patched, and
* will run a cleanup function before patching and before unmounting the
* the component.
*
* @param {Effect} effect the effect to run on component mount and/or patch
* @param {()=>any[]} [computeDependencies=()=>[NaN]] a callback to compute
* dependencies that will decide if the effect needs to be cleaned up and
* run again. If the dependencies did not change, the effect will not run
* again. The default value returns an array containing only NaN because
* NaN !== NaN, which will cause the effect to rerun on every patch.
*/
export function useEffect(effect: Effect, computeDependencies: () => any[] = () => [NaN]) {
let cleanup: () => void;
let dependencies: any[];
onMounted(() => {
dependencies = computeDependencies();
cleanup = effect(...dependencies) || NO_OP;
});
let shouldReapplyOnPatch = false;
onWillPatch(() => {
const newDeps = computeDependencies();
shouldReapplyOnPatch = newDeps.some((val, i) => val !== dependencies[i]);
if (shouldReapplyOnPatch) {
cleanup();
dependencies = newDeps;
}
});
onPatched(() => {
if (shouldReapplyOnPatch) {
cleanup = effect(...dependencies) || NO_OP;
}
});
onWillUnmount(() => cleanup());
}