[REF] web: replace use of other hooks with useEffect

closes odoo-dev/odoo#883

Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
Samuel Degueldre
2021-06-18 21:31:29 +02:00
committed by Aaron Bohy
parent 328190f735
commit ff870d8d15
19 changed files with 129 additions and 143 deletions
+9 -10
View File
@@ -1,6 +1,8 @@
/** @odoo-module **/
const { onMounted, onPatched, useComponent } = owl.hooks;
import { useEffect } from "./effect_hook";
const { useComponent } = owl.hooks;
// -----------------------------------------------------------------------------
// Hook functions
@@ -23,21 +25,18 @@ export function useAutofocus(params = {}) {
return () => {};
}
const selector = params.selector || "[autofocus]";
let target = null;
function autofocus() {
const prevTarget = target;
target = comp.el.querySelector(selector);
if (target && target !== prevTarget) {
let forceFocusCount = 0;
useEffect(function autofocus(target) {
if (target) {
target.focus();
if (["INPUT", "TEXTAREA"].includes(target.tagName)) {
const inputEl = target;
inputEl.selectionStart = inputEl.selectionEnd = inputEl.value.length;
}
}
}
onMounted(autofocus);
onPatched(autofocus);
}, () => [comp.el.querySelector(selector), forceFocusCount]);
return function focusOnUpdate() {
target = null;
forceFocusCount++; // force the effect to rerun on next patch
};
}
+6 -4
View File
@@ -1,6 +1,6 @@
/** @odoo-module **/
const { onMounted, onWillUnmount, useComponent } = owl.hooks;
import { useEffect } from "./effect_hook";
const { useComponent } = owl.hooks;
/**
* This file contains various custom hooks.
@@ -30,6 +30,8 @@ const { onMounted, onWillUnmount, useComponent } = owl.hooks;
*/
export function useBus(bus, eventName, callback) {
const component = useComponent();
onMounted(() => bus.on(eventName, component, callback));
onWillUnmount(() => bus.off(eventName, component));
useEffect(() => {
bus.on(eventName, component, callback);
return () => bus.off(eventName, component);
}, () => []);
}
@@ -1,11 +1,12 @@
/** @odoo-module **/
import { useService } from "../service_hook";
import { useEffect } from "../effect_hook";
import { registry } from "../registry";
const debugRegistry = registry.category("debug");
const { Component, hooks } = owl;
const { Component } = owl;
export class DebugManager extends Component {
setup() {
@@ -79,10 +80,8 @@ export function useDebugManager(elementsFactory) {
elementsFactory,
inDialog: env.inDialog,
};
hooks.onMounted(() => {
useEffect(() => {
env.bus.trigger("DEBUG-MANAGER:NEW-ITEMS", payload);
});
hooks.onWillUnmount(() => {
env.bus.trigger("DEBUG-MANAGER:REMOVE-ITEMS", payload);
});
return () => env.bus.trigger("DEBUG-MANAGER:REMOVE-ITEMS", payload);
}, () => []);
}
@@ -2,12 +2,13 @@
import { useBus } from "../bus_hook";
import { useService } from "../service_hook";
import { useEffect } from "../effect_hook";
import { scrollTo } from "../utils/scrolling";
import { ParentClosingMode } from "./dropdown_item";
const { Component, core, hooks, useState, QWeb } = owl;
const { EventBus } = core;
const { useExternalListener, onMounted, onPatched, onWillStart } = hooks;
const { useExternalListener, onWillStart } = hooks;
/**
* @typedef DropdownState
@@ -52,11 +53,11 @@ export class Dropdown extends Component {
this.close();
}
});
onMounted(() => {
useEffect(() => {
Promise.resolve().then(() => {
this.myActiveEl = this.ui.activeElement;
});
});
}, () => []);
// Set up key navigation -----------------------------------------------
this.hotkeyService = useService("hotkey");
@@ -124,8 +125,7 @@ export class Dropdown extends Component {
}
}
onMounted(autoSubscribeKeynav.bind(this));
onPatched(autoSubscribeKeynav.bind(this));
useEffect(autoSubscribeKeynav.bind(this));
}
// -------------------------------------------------------------------------
+5 -10
View File
@@ -1,9 +1,7 @@
/** @odoo-module */
import { useService } from "./service_hook";
const { hooks } = owl;
const { onMounted, onWillUnmount } = hooks;
import { useEffect } from "./effect_hook";
/**
* This hook will register/unregister the given registration
@@ -21,11 +19,8 @@ const { onMounted, onWillUnmount } = hooks;
*/
export function useHotkey(hotkey, callback, options = {}) {
const hotkeyService = useService("hotkey");
let token;
onMounted(() => {
token = hotkeyService.registerHotkey(hotkey, callback, options);
});
onWillUnmount(() => {
hotkeyService.unregisterHotkey(token);
});
useEffect(() => {
const token = hotkeyService.registerHotkey(hotkey, callback, options);
return () => hotkeyService.unregisterHotkey(token);
}, () => []);
}
+5 -6
View File
@@ -3,10 +3,11 @@
import { useService } from "./service_hook";
import { registry } from "./registry";
import { debounce } from "./utils/timing";
import { useEffect } from "./effect_hook";
const { Component, core, hooks } = owl;
const { EventBus } = core;
const { onMounted, onWillUnmount, useRef } = hooks;
const { useRef } = hooks;
export const SIZES = { XS: 0, VSM: 1, SM: 2, MD: 3, LG: 4, XL: 5, XXL: 6 };
@@ -24,12 +25,10 @@ export const SIZES = { XS: 0, VSM: 1, SM: 2, MD: 3, LG: 4, XL: 5, XXL: 6 };
export function useActiveElement(refName = null) {
const uiService = useService("ui");
const owner = refName ? useRef(refName) : Component.current;
onMounted(() => {
useEffect(() => {
uiService.activateElement(owner.el);
});
onWillUnmount(() => {
uiService.deactivateElement(owner.el);
});
return () => uiService.deactivateElement(owner.el);
}, () => []);
}
export const uiService = {
+25 -26
View File
@@ -3,6 +3,8 @@
import core from "web.core";
import Context from "web.Context";
import { useService } from "../core/service_hook";
import { useEffect } from "../core/effect_hook";
import { useBus } from "../core/bus_hook";
import { ViewNotFoundError } from "../webclient/actions/action_service";
import { useDebugManager } from "../core/debug/debug_menu";
import { objectToQuery } from "../core/browser/router_service";
@@ -11,7 +13,7 @@ import { mapDoActionOptionAPI } from "./utils";
import { setupDebugAction, setupDebugViewForm, setupDebugView } from "./debug_manager";
import { cleanDomFromBootstrap } from "./utils";
const { Component, hooks, tags } = owl;
const { Component, tags } = owl;
function wrapSuccessOrFail(promise, { on_success, on_fail } = {}) {
return promise.then(on_success || (() => {})).catch((reason) => {
@@ -38,7 +40,7 @@ class ActionAdapter extends ComponentAdapter {
// This is protected in legacy (backward compatibility) but should not e supported in Wowl
this.tempQuery = {};
let originalUpdateControlPanel;
hooks.onMounted(async () => {
useEffect(() => {
this.title.setParts({ action: this.widget.getTitle() });
const query = objectToQuery(this.widget.getState());
Object.assign(query, this.tempQuery);
@@ -58,15 +60,14 @@ class ActionAdapter extends ComponentAdapter {
this.trigger("controller-title-updated", this.__widget.getTitle());
return originalUpdateControlPanel(newProps);
};
await Promise.resolve(); // see https://github.com/odoo/owl/issues/809
this.trigger("controller-title-updated", this.__widget.getTitle());
core.bus.trigger("DOM_updated");
});
hooks.onWillUnmount(() => {
this.__widget.updateControlPanel = originalUpdateControlPanel;
this.wowlEnv.bus.off("ACTION_MANAGER:UPDATE", this);
});
return () => {
this.__widget.updateControlPanel = originalUpdateControlPanel;
this.wowlEnv.bus.off("ACTION_MANAGER:UPDATE", this);
};
}, () => []);
}
_trigger_up(ev) {
@@ -189,7 +190,7 @@ function useMagicLegacyReload() {
let legacyReloadProm = null;
const getReloadProm = () => legacyReloadProm;
let manualReload;
hooks.onMounted(() => {
useEffect(() => {
const widget = comp.widget;
const controllerReload = widget.reload;
widget.reload = function (...args) {
@@ -216,7 +217,7 @@ function useMagicLegacyReload() {
});
};
widget[magicReloadSymbol] = getReloadProm;
});
}, () => []);
return getReloadProm;
}
@@ -240,22 +241,20 @@ export class ViewAdapter extends ActionAdapter {
);
}
if (!envWowl.inDialog) {
hooks.onMounted(() => {
envWowl.bus.on("ACTION_MANAGER:UPDATE", this, (info) => {
switch (info.type) {
case "OPEN_DIALOG": {
// we are a main action, and a dialog is going to open:
// we should not reload
this.shouldUpdateWidget = false;
break;
}
case "CLOSE_DIALOG": {
this.shouldUpdateWidget = false;
info.closingProms.push(() => this.magicReload());
break;
}
useBus(envWowl.bus, "ACTION_MANAGER:UPDATE", (info) => {
switch (info.type) {
case "OPEN_DIALOG": {
// we are a main action, and a dialog is going to open:
// we should not reload
this.shouldUpdateWidget = false;
break;
}
});
case "CLOSE_DIALOG": {
this.shouldUpdateWidget = false;
info.closingProms.push(() => this.magicReload());
break;
}
}
});
}
this.env = Component.env;
@@ -1,8 +1,8 @@
odoo.define('web.custom_hooks', function () {
odoo.define('web.custom_hooks', function (require) {
"use strict";
const { Component, hooks } = owl;
const { onMounted, onPatched, onWillUnmount } = hooks;
const { Component } = owl;
const { useEffect } = require("@web/core/effect_hook");
/**
* Focus a given selector as soon as it appears in the DOM and if it was not
@@ -20,22 +20,19 @@ odoo.define('web.custom_hooks', function () {
return () => {};
}
const selector = params.selector || '[autofocus]';
let target = null;
function autofocus() {
const prevTarget = target;
target = comp.el.querySelector(selector);
if (target && target !== prevTarget) {
let forceFocusCount = 0;
useEffect(function autofocus(target) {
if (target) {
target.focus();
if (['INPUT', 'TEXTAREA'].includes(target.tagName)) {
target.selectionStart = target.selectionEnd = target.value.length;
if (["INPUT", "TEXTAREA"].includes(target.tagName)) {
const inputEl = target;
inputEl.selectionStart = inputEl.selectionEnd = inputEl.value.length;
}
}
}
onMounted(autofocus);
onPatched(autofocus);
}, () => [comp.el.querySelector(selector), forceFocusCount]);
return function focusOnUpdate() {
target = null;
forceFocusCount++; // force the effect to rerun on next patch
};
}
@@ -103,12 +100,12 @@ odoo.define('web.custom_hooks', function () {
} else {
boundHandler = handler.bind(comp);
}
onMounted(function () {
useEffect(() => {
comp.el.addEventListener(eventName, boundHandler, addEventListenerOptions);
});
onWillUnmount(function () {
comp.el.removeEventListener(eventName, boundHandler, addEventListenerOptions);
});
return () => {
comp.el.removeEventListener(eventName, boundHandler, addEventListenerOptions);
};
}, () => []);
}
return {
@@ -3,8 +3,7 @@ odoo.define('web.AbstractFieldOwl', function (require) {
const field_utils = require('web.field_utils');
const { useListener } = require('web.custom_hooks');
const { onMounted, onPatched } = owl.hooks;
const { useEffect } = require('@web/core/effect_hook');
/**
* This file defines the Owl version of the AbstractField. Specific fields
@@ -71,8 +70,7 @@ odoo.define('web.AbstractFieldOwl', function (require) {
useListener('click', this._onClick);
useListener('keydown', this._onKeydown);
useListener('navigation-move', this._onNavigationMove);
onMounted(() => this._applyDecorations());
onPatched(() => this._applyDecorations());
useEffect(() => this._applyDecorations());
}
/**
* Hack: studio tries to find the field with a selector base on its
@@ -1,12 +1,13 @@
/** @odoo-module alias=web.PivotRenderer **/
import { useEffect } from "@web/core/effect_hook";
import DropdownMenu from 'web.DropdownMenu';
import DropdownMenuItem from 'web.DropdownMenuItem';
import OwlAbstractRenderer from '../abstract_renderer_owl';
import field_utils from 'web.field_utils';
import { DEFAULT_INTERVAL, INTERVAL_OPTIONS } from 'web.searchUtils';
const { useExternalListener, useState, onMounted, onPatched } = owl.hooks;
const { useExternalListener, useState } = owl.hooks;
class PivotCustomGroupByItem extends DropdownMenuItem {
constructor() {
@@ -146,10 +147,7 @@
this.hasSearchArchGroupBys = Boolean(searchArchGroupBys && searchArchGroupBys.length);
this.customGroupBys = new Map();
onMounted(() => this._updateTooltip());
onPatched(() => this._updateTooltip());
useEffect(() => this._updateTooltip());
useExternalListener(window, 'click', this._resetState);
}
@@ -2,10 +2,10 @@
import { Dialog } from "../core/dialog/dialog";
import { patch } from "@web/core/utils/patch";
import { useEffect } from "@web/core/effect_hook";
import OwlDialog from "web.OwlDialog";
const { hooks } = owl;
const { onMounted, onWillUnmount } = hooks;
/**
* This is a patch of the new Dialog class.
@@ -14,11 +14,9 @@ const { onMounted, onWillUnmount } = hooks;
patch(Dialog.prototype, "Legacy Adapted Dialog", {
setup() {
this._super();
onMounted(() => {
useEffect(() => {
OwlDialog.display(this);
});
onWillUnmount(() => {
OwlDialog.hide(this);
});
return () => OwlDialog.hide(this);
}, () => []);
},
});
@@ -2,6 +2,7 @@
import { Dialog } from "../../core/dialog/dialog";
import { DebugManager } from "../../core/debug/debug_menu";
import { useEffect } from "../../core/effect_hook";
const { hooks } = owl;
@@ -46,7 +47,7 @@ class LegacyAdaptedActionDialog extends ActionDialog {
const ControllerComponent = this.props && this.props.ActionComponent;
const Controller = ControllerComponent && ControllerComponent.Component;
this.isLegacy = Controller && Controller.isLegacy;
hooks.onMounted(() => {
useEffect(() => {
if (this.isLegacy) {
// Retrieve the widget climbing the wrappers
const componentController = this.actionRef.comp;
@@ -57,7 +58,7 @@ class LegacyAdaptedActionDialog extends ActionDialog {
const footer = this.modalRef.el.querySelector("footer");
widget.renderButtons($(footer));
}
});
}, () => []); // TODO: should this depend on actionRef.comp?
}
}
LegacyAdaptedActionDialog.template = "web.LegacyAdaptedActionDialog";
@@ -1,8 +1,9 @@
/** @odoo-module **/
import { getScrollPosition, setScrollPosition } from "../../core/utils/scrolling";
import { useEffect } from "../../core/effect_hook";
const { useComponent, onMounted } = owl.hooks;
const { useComponent } = owl.hooks;
// -----------------------------------------------------------------------------
// Action hook
@@ -17,7 +18,7 @@ const scrollSymbol = Symbol("scroll");
export function useSetupAction(params) {
const component = useComponent();
onMounted(() => {
useEffect(() => {
if (component.props.registerCallback) {
if (params.beforeLeave) {
component.props.registerCallback("beforeLeave", params.beforeLeave);
@@ -34,5 +35,5 @@ export function useSetupAction(params) {
if (component.props.state) {
setScrollPosition(component, component.props.state[scrollSymbol]);
}
});
}, () => []);
}
@@ -1,9 +1,7 @@
/** @odoo-module **/
import { useService } from "../../core/service_hook";
const { hooks } = owl;
const { onMounted, onWillUnmount } = hooks;
import { useEffect } from "../../core/effect_hook";
/**
* @typedef {import("./command_service").Command} Command
@@ -17,11 +15,8 @@ const { onMounted, onWillUnmount } = hooks;
*/
export function useCommand(command) {
const commandService = useService("command");
let token;
onMounted(() => {
token = commandService.registerCommand(command);
});
onWillUnmount(() => {
commandService.unregisterCommand(token);
});
useEffect(() => {
const token = commandService.registerCommand(command);
return () => commandService.unregisterCommand(token);
}, () => []);
}
@@ -2,11 +2,12 @@
import { registry } from "@web/core/registry";
import { useAutofocus } from "../../core/autofocus_hook";
import { useEffect } from "../../core/effect_hook";
import { useHotkey } from "../../core/hotkey_hook";
import { scrollTo } from "../../core/utils/scrolling";
const { Component, hooks } = owl;
const { onPatched, useState } = hooks;
const { useState } = hooks;
const commandCategoryRegistry = registry.category("command_categories");
/**
@@ -51,7 +52,7 @@ export class CommandPalette extends Component {
useAutofocus();
this.mouseSelectionActive = false;
onPatched(() => {
useEffect(() => {
const index = this.state.commands.indexOf(this.state.selectedCommand);
const listbox = this.el.querySelector(".o_command_palette_listbox");
const command = listbox.querySelector(`#o_command_${index}`);
@@ -1,6 +1,7 @@
/** @odoo-module **/
import { browser } from "../../core/browser/browser";
import { useEffect } from "../../core/effect_hook";
const { Component, hooks } = owl;
@@ -28,15 +29,14 @@ export class RainbowMan extends Component {
const fadeout = "fadeout" in this.props ? this.props.fadeout : "medium";
const delay = fadeout ? RainbowMan.rainbowFadeouts[fadeout] : false;
this.delay = typeof delay === "number" ? delay : false;
hooks.onMounted(() => {
if (this.delay !== false) {
browser.setTimeout(() => {
if (this.__owl__.status !== 5 /* DESTROYED */) {
this.el.classList.add("o_reward_fading");
}
if (this.delay !== false) {
useEffect(() => {
const timeout = browser.setTimeout(() => {
this.el.classList.add("o_reward_fading");
}, this.delay);
}
});
return () => browser.clearTimeout(timeout);
}, () => []);
}
}
onAnimationEnd(ev) {
@@ -3,22 +3,23 @@
import { Dropdown } from "../../core/dropdown/dropdown";
import { DropdownItem } from "../../core/dropdown/dropdown_item";
import { useService } from "../../core/service_hook";
import { useEffect } from "../../core/effect_hook";
import { registry } from "../../core/registry";
import { debounce } from "../../core/utils/timing";
const { Component, hooks } = owl;
const { onMounted, useExternalListener } = hooks;
const { useExternalListener } = hooks;
const systrayRegistry = registry.category("systray");
export class MenuDropdown extends Dropdown {
setup() {
super.setup();
onMounted(() => {
useEffect(() => {
if (this.props.xmlid) {
const toggler = this.el.querySelector("button.o_dropdown_toggler");
toggler.dataset.menuXmlid = this.props.xmlid;
}
});
}, () => []);
}
}
MenuDropdown.props.xmlid = {
@@ -29,11 +30,11 @@ MenuDropdown.props.xmlid = {
export class MenuItem extends DropdownItem {
setup() {
super.setup();
onMounted(() => {
useEffect(() => {
if (this.props.payload.xmlid) {
this.el.dataset.menuXmlid = this.props.payload.xmlid;
}
});
}, () => []);
}
}
@@ -4,19 +4,20 @@ import { browser } from "../../core/browser/browser";
import { DropdownItem } from "../../core/dropdown/dropdown_item";
import { useService } from "../../core/service_hook";
import { registry } from "../../core/registry";
import { useEffect } from "../../core/effect_hook";
const { Component, hooks } = owl;
const { Component } = owl;
const userMenuRegistry = registry.category("user_menuitems");
class UserMenuItem extends DropdownItem {
setup() {
super.setup();
hooks.onMounted(() => {
useEffect(() => {
if (this.props.payload.id) {
this.el.dataset.menu = this.props.payload.id;
}
});
}, () => []);
}
}
+11 -9
View File
@@ -2,10 +2,12 @@
import { registry } from "../core/registry";
import { useService } from "../core/service_hook";
import { useBus } from "../core/bus_hook";
import { ActionContainer } from "./actions/action_container";
import { NavBar } from "./navbar/navbar";
import { useEffect } from "@web/core/effect_hook";
const { Component, hooks } = owl;
const { Component } = owl;
const mainComponentRegistry = registry.category("main_components");
export class WebClient extends Component {
@@ -18,15 +20,15 @@ export class WebClient extends Component {
useService("legacy_service_provider");
this.Components = mainComponentRegistry.getEntries();
this.title.setParts({ zopenerp: "Odoo" }); // zopenerp is easy to grep
hooks.onMounted(() => {
this.env.bus.on("ROUTE_CHANGE", this, this.loadRouterState);
this.env.bus.on("ACTION_MANAGER:UI-UPDATED", this, (mode) => {
if (mode !== "new") {
this.el.classList.toggle("o_fullscreen", mode === "fullscreen");
}
});
this.loadRouterState();
useBus(this.env.bus, "ROUTE_CHANGE", this.loadRouterState);
useBus(this.env.bus, "ACTION_MANAGER:UI-UPDATED", (mode) => {
if (mode !== "new") {
this.el.classList.toggle("o_fullscreen", mode === "fullscreen");
}
});
useEffect(() => {
this.loadRouterState();
}, () => []);
}
mounted() {