From 7f9af5747c7b650b6cf4ab894bdd0b113e77a78b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Theys?= Date: Thu, 21 Jan 2021 17:43:49 +0000 Subject: [FIX] mail, *: significantly reduce number of useless renders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * = hr_holidays, im_livechat, website_livechat A huge amount of components where rendered for no reason, notably when typing a new message (at every key press), which significantly slowed down the interface. There were 2 causes to this number of renders: - OWL rendering all children automatically, even if their props didn't change, unless `shouldUpdate` is overridden, which is now done. - `useStore` selectors being way too generous in what they observed task-2399731 closes odoo/odoo#62703 Signed-off-by: Alexandre Kühn (aku) --- addons/mail/static/src/bugfix/bugfix.js | 74 +++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) (limited to 'addons/mail/static/src/bugfix/bugfix.js') diff --git a/addons/mail/static/src/bugfix/bugfix.js b/addons/mail/static/src/bugfix/bugfix.js index 162bf606db5..4a8bb890bff 100644 --- a/addons/mail/static/src/bugfix/bugfix.js +++ b/addons/mail/static/src/bugfix/bugfix.js @@ -115,3 +115,77 @@ function useUpdate({ func, priority }) { return useUpdate; }); + +// Should be moved to its own file in master. +odoo.define('mail/static/src/component_hooks/use_should_update_based_on_props/use_should_update_based_on_props.js', function (require) { +'use strict'; + +const { Component } = owl; + +/** + * Compares `a` and `b` up to the given `compareDepth`. + * + * @param {any} a + * @param {any} b + * @param {Object|integer} compareDepth + * @returns {boolean} + */ +function isEqual(a, b, compareDepth) { + const keys = Object.keys(a); + if (Object.keys(b).length !== keys.length) { + return false; + } + for (const key of keys) { + // the depth can be given either as a number (for all keys) or as + // an object (for each key) + let depth; + if (typeof compareDepth === 'number') { + depth = compareDepth; + } else { + depth = compareDepth[key] || 0; + } + if (depth === 0 && a[key] !== b[key]) { + return false; + } + if (depth !== 0) { + let nextDepth; + if (typeof depth === 'number') { + nextDepth = depth - 1; + } else { + nextDepth = depth; + } + if (!isEqual(a[key], b[key], nextDepth)) { + return false; + } + } + } + return true; +} + +/** + * This hook overrides the `shouldUpdate` method to ensure the component is only + * updated if its props actually changed. This is especially useful to use on + * components for which an extra render costs proportionally a lot more than + * comparing props. + * + * @param {Object} [param0={}] + * @param {Object} [param0.compareDepth={}] allows to specify the comparison + * depth to use for each prop. Default is shallow compare (depth = 0). + */ +function useShouldUpdateBasedOnProps({ compareDepth = {} } = {}) { + const component = Component.current; + component.shouldUpdate = nextProps => { + const allNewProps = Object.assign({}, nextProps); + const defaultProps = component.constructor.defaultProps; + for (const key in defaultProps) { + if (allNewProps[key] === undefined) { + allNewProps[key] = defaultProps[key]; + } + } + return !isEqual(component.props, allNewProps, compareDepth); + }; +} + +return useShouldUpdateBasedOnProps; + +}); -- cgit v1.2.3