UNPKG

@wordpress/compose

Version:
78 lines (65 loc) 2.88 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = useMergeRefs; var _element = require("@wordpress/element"); /** * WordPress dependencies */ /** @typedef {import('@wordpress/element').RefObject} RefObject */ /** @typedef {import('@wordpress/element').RefCallback} RefCallback */ /** * Merges refs into one ref callback. Ensures the merged ref callbacks are only * called when it changes (as a result of a `useCallback` dependency update) or * when the ref value changes. If you don't wish a ref callback to be called on * every render, wrap it with `useCallback( ref, [] )`. * Dependencies can be added, but when a dependency changes, the old ref * callback will be called with `null` and the new ref callback will be called * with the same node. * * @param {Array<RefObject|RefCallback>} refs The refs to be merged. * * @return {RefCallback} The merged ref callback. */ function useMergeRefs(refs) { const element = (0, _element.useRef)(null); const didElementChange = (0, _element.useRef)(false); const previousRefs = (0, _element.useRef)(refs); const currentRefs = (0, _element.useRef)(refs); // Update on render before the ref callback is called, so the ref callback // always has access to the current refs. currentRefs.current = refs; // If any of the refs change, call the previous ref with `null` and the new // ref with the node, except when the element changes in the same cycle, in // which case the ref callbacks will already have been called. (0, _element.useLayoutEffect)(() => { refs.forEach((ref, index) => { const previousRef = previousRefs.current[index]; if (typeof ref === 'function' && ref !== previousRef && didElementChange.current === false) { previousRef(null); ref(element.current); } }); previousRefs.current = refs; }, refs); // No dependencies, must be reset after every render so ref callbacks are // correctly called after a ref change. (0, _element.useLayoutEffect)(() => { didElementChange.current = false; }); // There should be no dependencies so that `callback` is only called when // the node changes. return (0, _element.useCallback)(value => { // Update the element so it can be used when calling ref callbacks on a // dependency change. element.current = value; didElementChange.current = true; // When an element changes, the current ref callback should be called // with the new element and the previous one with `null`. const refsToUpdate = value ? currentRefs.current : previousRefs.current; // Update the latest refs. refsToUpdate.forEach(ref => { if (typeof ref === 'function') { ref(value); } else if (ref && ref.hasOwnProperty('current')) { ref.current = value; } }); }, []); } //# sourceMappingURL=index.js.map