Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Uncaught Error: flushSync was called from inside a lifecycle method. It cannot be called when React is already rendering #817

Closed
duhmojo opened this issue Sep 25, 2023 · 4 comments
Labels

Comments

@duhmojo
Copy link

duhmojo commented Sep 25, 2023

Version

react-bootstrap-typeahead@6.2.3

Steps to reproduce

Hard to reproduce with an example, but I can try. Basically when using the React Bootstrap Tabs/Tab component, if I stick a Typeahead component inside the tab, then open the dropdown and select another tab, the following blows up in my console:

import Tab from 'react-bootstrap/Tab'
import Tabs from 'react-bootstrap/Tabs'
import { Typeahead, Menu, MenuItem } from "react-bootstrap-typeahead";
...
<Tabs>
  <Tab>
    test
    <Typeahead
      options={ [] }
    />
  </Tab>
  <Tab>
    test
  </Tab>
</Tabs>

react-dom.development.js:21917 Uncaught Error: flushSync was called from inside a lifecycle method. It cannot be called when React is already rendering.
    at Object.flushSync (react-dom.development.js:21917:15)
    at fn (usePopper.js:44:9)
    at forceUpdate (createPopper.js:128:21)
    at useOverlay.js:55:20
    at commitHookEffectListMount (react-dom.development.js:19731:26)
    at commitPassiveHookEffects (react-dom.development.js:19769:11)
    at HTMLUnknownElement.callCallback (react-dom.development.js:188:14)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:237:16)
    at invokeGuardedCallback (react-dom.development.js:292:31)
    at flushPassiveEffectsImpl (react-dom.development.js:22853:9)
...
flushSyncCallbackQueue	@	react-dom.development.js:11072
scheduleUpdateOnFiber	@	react-dom.development.js:21199
enqueueSetState	@	react-dom.development.js:12639
Component.setState	@	react.development.js:471
(anonymous)	@	Typeahead.js:260
(anonymous)	@	useRootClose.js:52
(anonymous)	@	useEventCallback.js:6
(anonymous)	@	useRootClose.js:77

If I click off to close the dropdown, then switch tabs, no problem. The issue is tried to React rendering or attempting to render visible content that isn't visible/loaded anymore.

Typeahead line 260:

    _defineProperty(_assertThisInitialized(_this), "hideMenu", function () {
      _this.setState(hideMenu);
    });

Expected Behavior

Using React Bootstrap Tabs I expect to be able to select another tab when the Typehead dropdown is visible and not have it error.

Actual Behavior

Using React Bootstrap Tabs selecting another tab when the Typehead dropdown is visible throws:

react-dom.development.js:21917 Uncaught Error: flushSync was called from inside a lifecycle method. It cannot be called when React is already rendering.
    at Object.flushSync (react-dom.development.js:21917:15)
    at fn (usePopper.js:44:9)
    at forceUpdate (createPopper.js:128:21)
    at useOverlay.js:55:20
    at commitHookEffectListMount (react-dom.development.js:19731:26)
    at commitPassiveHookEffects (react-dom.development.js:19769:11)
    at HTMLUnknownElement.callCallback (react-dom.development.js:188:14)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:237:16)
    at invokeGuardedCallback (react-dom.development.js:292:31)
    at flushPassiveEffectsImpl (react-dom.development.js:22853:9)
...
flushSyncCallbackQueue	@	react-dom.development.js:11072
scheduleUpdateOnFiber	@	react-dom.development.js:21199
enqueueSetState	@	react-dom.development.js:12639
Component.setState	@	react.development.js:471
(anonymous)	@	Typeahead.js:260
(anonymous)	@	useRootClose.js:52
(anonymous)	@	useEventCallback.js:6
(anonymous)	@	useRootClose.js:77
@duhmojo duhmojo added the bug label Sep 25, 2023
@duhmojo
Copy link
Author

duhmojo commented Sep 25, 2023

I was inspecting _this in hideMenu and the inputNode can be used to detect if the menu is rendered in the DOM. e.g. if _this.inputNode.offsetParent is undefined it's not rendered. Probably a more standard way of detecting this.

@ericgio
Copy link
Owner

ericgio commented Oct 31, 2023

Hey @duhmojo, thanks for opening this issue, and for the detailed repro steps and additional investigation. It looks like the flushSync call is coming from react-popper which controls the menu overlay. As far as I can tell, the component is still mounted when setState is called, so it's possible there's some kind of race condition going on.

I plan to drop the react-popper dependency in a future version and the error no longer repros with the update, so I think it's safe to ignore the error for now and it will be resolved going forward, probably in v7.

@ericgio ericgio closed this as completed Oct 31, 2023
@hanorine
Copy link

@ericgio when would v7 come out? I would like to find a way to resolve this issue. Will ignore the warning for now. Thank you for your time.

@duhmojo
Copy link
Author

duhmojo commented May 9, 2024

I'm going to comment on this issue with my work around. I ignored this for months then had to come back to it.

I was able to set a ref for Typeahead, then onBlur get the ref and call hideMenu() to force the menu closed before anything else in the React lifecycle could execute.

                                            this.searchRefs[field.name] = React.createRef();
                                            return (
                                                <Col key={index}>
                                                    <FormGroup>
                                                        <Form.Label>
                                                            {field.label}
                                                        </Form.Label>
                                                        <Typeahead
                                                            ref={this.searchRefs[field.name]}
                                                            onBlur={(e) => {
                                                                if (this.searchRefs[field.name] !== undefined) {
                                                                    this.searchRefs[field.name].current.hideMenu();
                                                                }
                                                            }}

I will say that hideMenu was no where in the docs, but was in the reference object and code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants