From 5fb738f6934f71880411761ac2242806a18a27e4 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 5 Jul 2024 17:46:16 +0200 Subject: [PATCH] Fix `flushSync` warning for `Combobox` component with `immediate` prop enabled (#3366) * wrap flushSync call in microTask This will make sure that React is able to flush this correctly by delaying the call using a microTask. * update changelog * reformat comments Now that it's nested, let's adjust the width of the comments --- packages/@headlessui-react/CHANGELOG.md | 1 + .../src/components/combobox/combobox.tsx | 37 ++++++++++++++++--- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/packages/@headlessui-react/CHANGELOG.md b/packages/@headlessui-react/CHANGELOG.md index 81ce288d70..df8de60aa3 100644 --- a/packages/@headlessui-react/CHANGELOG.md +++ b/packages/@headlessui-react/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix `transition` and `focus` prop combination for `PopoverPanel` component ([#3361](https://github.com/tailwindlabs/headlessui/pull/3361)) - Fix outside click in nested portalled `Popover` components ([#3362](https://github.com/tailwindlabs/headlessui/pull/3362)) - Fix restoring focus to correct element when closing `Dialog` component ([#3365](https://github.com/tailwindlabs/headlessui/pull/3365)) +- Fix `flushSync` warning for `Combobox` component with `immediate` prop enabled ([#3366](https://github.com/tailwindlabs/headlessui/pull/3366)) ## [2.1.1] - 2024-06-26 diff --git a/packages/@headlessui-react/src/components/combobox/combobox.tsx b/packages/@headlessui-react/src/components/combobox/combobox.tsx index f0e0c464a5..1fc681a9be 100644 --- a/packages/@headlessui-react/src/components/combobox/combobox.tsx +++ b/packages/@headlessui-react/src/components/combobox/combobox.tsx @@ -1321,12 +1321,37 @@ function InputFn< if (!data.immediate) return if (data.comboboxState === ComboboxState.Open) return - flushSync(() => actions.openCombobox()) - - // We need to make sure that tabbing through a form doesn't result in incorrectly setting the - // value of the combobox. We will set the activation trigger to `Focus`, and we will ignore - // selecting the active option when the user tabs away. - actions.setActivationTrigger(ActivationTrigger.Focus) + // In a scenario where you have this setup: + // + // ```ts + // {condition && ( + // + // + // + // )} + // ``` + // + // Then we will trigger the `openCombobox` in a `flushSync`, but we are + // already in the middle of rendering. This will result in the following + // warning: + // + // ``` + // Warning: flushSync was called from inside a lifecycle method. React + // cannot flush when React is already rendering. Consider moving this call + // to a scheduler task or micro task. + // ``` + // + // Which is why we wrap this in a `microTask` to make sure we are not in the + // middle of rendering. + d.microTask(() => { + flushSync(() => actions.openCombobox()) + + // We need to make sure that tabbing through a form doesn't result in + // incorrectly setting the value of the combobox. We will set the + // activation trigger to `Focus`, and we will ignore selecting the active + // option when the user tabs away. + actions.setActivationTrigger(ActivationTrigger.Focus) + }) }) let labelledBy = useLabelledBy()