downshift: useSelect `cannot update during an existing state transition` warning using onSelectItemChange with react-router push

  • downshift version: 4.0.1
  • node version: 10.16.3
  • npm (or yarn) version: yarn 1.21.1

Relevant code or config

import React from "react";
import * as ReactDOM from "react-dom";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  useHistory
} from "react-router-dom";
import { useSelect } from "downshift";

function App() {
  return (
    <Router>
      <div>
        <NavSelect />
        <Switch>
          <Route path="/about">
            <About />
          </Route>
          <Route path="/users">
            <Users />
          </Route>
          <Route path="/">
            <Home />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

function NavSelect() {
  const { push } = useHistory();
  const items = ["/about", "/users", "/"];
  const {
    getToggleButtonProps,
    isOpen,
    getMenuProps,
    getItemProps
  } = useSelect({
    onSelectedItemChange: changes => {
      push(changes.selectedItem);
    },
    items
  });
  return (
    <>
      <button type="button" {...getToggleButtonProps()}>
        Toggle
      </button>
      <ul {...getMenuProps()}>
        {isOpen &&
          items.map((item, index) => {
            return (
              <li key={item} {...getItemProps({ index })}>
                {item}
              </li>
            );
          })}
      </ul>
    </>
  );
}

function Home() {
  return <h2>Home</h2>;
}

function About() {
  return <h2>About</h2>;
}

function Users() {
  return <h2>Users</h2>;
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

What you did:

I’m using useSelect with an onSelectItemChange callback that calls react-router’s push function to change the route.

What happened:

The push works, but I end up with this warning in the console:

Screen Shot 2020-01-03 at 4 01 13 PM
Warning: Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state. 
    in NavSelect (at src/​index.js:15)
    in App (at src/​index.js:78)

Reproduction repository:

https://codesandbox.io/s/hopeful-currying-hvx2p

Problem description:

There seems to be some specific incompatibility between where onSelectITemChange is called and how push works. I don’t quite understand it yet. push calls history’s setState (not React’s), which calls notifyListeners which calls react-router’s history listener.

Suggested solution:

Unsure.

    onSelectedItemChange: changes => {
      setTimeout(() => {
        push(changes.selectedItem);
      });
    },

works as a workaround.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 2
  • Comments: 17 (6 by maintainers)

Most upvoted comments

🎉 This issue has been resolved in version 4.0.8 🎉

The release is available on:

Your semantic-release bot 📦🚀

@silviuavram I prepared pull request and tested it with react-router-dom and react-instantsearch-dom, works good as for me.