From 755061fc9a233a6a68a8d9811a47db641014b326 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Wed, 16 Feb 2022 16:55:59 +0100 Subject: [PATCH] use a ref instead of a useCallback This allows us to guarantee that the ref is always referencing the latest callback. This also allows us to re-run fewer effects because we don't really care about intermediate callback values, just the last one. --- .../src/components/transitions/transition.tsx | 82 ++++++++----------- 1 file changed, 36 insertions(+), 46 deletions(-) diff --git a/packages/@headlessui-react/src/components/transitions/transition.tsx b/packages/@headlessui-react/src/components/transitions/transition.tsx index f3f8595273..de4bba6261 100644 --- a/packages/@headlessui-react/src/components/transitions/transition.tsx +++ b/packages/@headlessui-react/src/components/transitions/transition.tsx @@ -1,7 +1,6 @@ import React, { Fragment, createContext, - useCallback, useContext, useEffect, useMemo, @@ -24,6 +23,7 @@ import { Features, PropsForFeatures, render, RenderStrategy } from '../../utils/ import { Reason, transition } from './utils/transition' import { OpenClosedProvider, State, useOpenClosed } from '../../internal/open-closed' import { useServerHandoffComplete } from '../../hooks/use-server-handoff-complete' +import { useLatestValue } from '../../hooks/use-latest-value' type ID = ReturnType @@ -95,8 +95,8 @@ function useParentNesting() { interface NestingContextValues { children: MutableRefObject<{ id: ID; state: TreeStates }[]> - register: (id: ID) => () => void - unregister: (id: ID, strategy?: RenderStrategy) => void + register: MutableRefObject<(id: ID) => () => void> + unregister: MutableRefObject<(id: ID, strategy?: RenderStrategy) => void> } let NestingContext = createContext(null) @@ -110,48 +110,38 @@ function hasChildren( } function useNesting(done?: () => void) { - let doneRef = useRef(done) + let doneRef = useLatestValue(done) let transitionableChildren = useRef([]) let mounted = useIsMounted() - useEffect(() => { - doneRef.current = done - }, [done]) - - let unregister = useCallback( - (childId: ID, strategy = RenderStrategy.Hidden) => { - let idx = transitionableChildren.current.findIndex(({ id }) => id === childId) - if (idx === -1) return - - match(strategy, { - [RenderStrategy.Unmount]() { - transitionableChildren.current.splice(idx, 1) - }, - [RenderStrategy.Hidden]() { - transitionableChildren.current[idx].state = TreeStates.Hidden - }, - }) - - if (!hasChildren(transitionableChildren) && mounted.current) { - doneRef.current?.() - } - }, - [doneRef, mounted, transitionableChildren] - ) + let unregister = useLatestValue((childId: ID, strategy = RenderStrategy.Hidden) => { + let idx = transitionableChildren.current.findIndex(({ id }) => id === childId) + if (idx === -1) return + + match(strategy, { + [RenderStrategy.Unmount]() { + transitionableChildren.current.splice(idx, 1) + }, + [RenderStrategy.Hidden]() { + transitionableChildren.current[idx].state = TreeStates.Hidden + }, + }) - let register = useCallback( - (childId: ID) => { - let child = transitionableChildren.current.find(({ id }) => id === childId) - if (!child) { - transitionableChildren.current.push({ id: childId, state: TreeStates.Visible }) - } else if (child.state !== TreeStates.Visible) { - child.state = TreeStates.Visible - } - - return () => unregister(childId, RenderStrategy.Unmount) - }, - [transitionableChildren, unregister] - ) + if (!hasChildren(transitionableChildren) && mounted.current) { + doneRef.current?.() + } + }) + + let register = useLatestValue((childId: ID) => { + let child = transitionableChildren.current.find(({ id }) => id === childId) + if (!child) { + transitionableChildren.current.push({ id: childId, state: TreeStates.Visible }) + } else if (child.state !== TreeStates.Visible) { + child.state = TreeStates.Visible + } + + return () => unregister.current(childId, RenderStrategy.Unmount) + }) return useMemo( () => ({ @@ -227,14 +217,14 @@ function TransitionChild { if (!id) return - return register(id) + return register.current(id) }, [register, id]) useIsoMorphicEffect(() => { @@ -249,8 +239,8 @@ function TransitionChild unregister(id), - [TreeStates.Visible]: () => register(id), + [TreeStates.Hidden]: () => unregister.current(id), + [TreeStates.Visible]: () => register.current(id), }) }, [state, id, register, unregister, show, strategy]) @@ -314,7 +304,7 @@ function TransitionChild