Repository: next.js
Issue: Using Turnstone component causes infinite refresh loop
Labels: bug

### Link to the code that reproduces this issue

https://github.com/jzellis/ufrisbee

### To Reproduce

1. Clone repo and add a valid MongoDB URI as `MONGODB_URI` in .env.local
2. `npm run dev`
3. Navigate to /games and open the console
4. Watch the page refresh endlessly

### Current vs. Expected behavior

I'm trying to use a [Turnstone component](https://github.com/tomsouthall/turnstone) to do autocomplete on a search field. Turnstone uses an `onSelect` event to handle selecting an option, like so:

` <Turnstone listbox={listbox} onSelect={searchSelected}  />`

In my case, this is an async function:

```
const searchSelected = async (selectedItem, displayName) => {
    setResults([]);
    if (selectedItem) {
      let gameList = await (
        await fetch(
          `/api?lat=${selectedItem.lat}&lng=${selectedItem.lon}&distance=${
            distance * 1609.344
          }`
        )
      ).json();
      setResults(gameList.games);
    }
  };
```

But when I add this async function as the onSelect function for the component, it causes the page to refresh endlessly. It's not the `listbox` object, which also makes a call to an async function as the user types; it's only when I add `onSelect={searchSelected}` to the component that the page goes ballistic, refreshing dozens of times a second.

Weirdly, this doesn't happen when I do `npm run build` and then `npm run start`, but when I do that and actually choose an item from the dropdown, it goes into an infinite loop again.

The full code looks like this:

```
"use client";
import { useState, useEffect } from "react";
import Turnstone from "turnstone";

export default function Page() {
  const [query, setQuery] = useState(""),
    [results, setResults] = useState([]),
    [distance, setDistance] = useState(10),

  let distanceArray = [5, 10, 25, 50, 100];

  const searchSelected = async (selectedItem, displayName) => {
    setResults([]);
    if (selectedItem) {
      let gameList = await (
        await fetch(
          `/api?lat=${selectedItem.lat}&lng=${selectedItem.lon}&distance=${
            distance * 1609.344
          }`
        )
      ).json();
      setResults(gameList.games);
    }
  };

  const listbox = {
    displayField: "display_name",
    searchType: "contains",
    minQueryLength: 3,
    data: async (query) => {
      return await (
        await fetch(`https://geocode.maps.co/search?q=${query}`)
      ).json();
    },
  };

  const styles = {
    input:
      "w-full h-12 border border-slate-300 py-2 pl-10 pr-7 text-xl outline-none rounded",
    inputFocus:
      "w-full h-12 border-x-0 border-t-0 border-b border-blue-300 py-2 pl-10 pr-7 text-xl outline-none sm:rounded sm:border",
    query: "text-slate-800 placeholder-slate-400",
    typeahead: "text-blue-300 border-white",
    cancelButton: `absolute w-10 h-12 inset-y-0 left-0 items-center justify-center z-10 text-blue-400 inline-flex sm:hidden`,
    clearButton:
      "absolute inset-y-0 right-0 w-8 inline-flex items-center justify-center text-slate-400 hover:text-rose-400",
    listbox:
      "w-full bg-white sm:border sm:border-blue-300 sm:rounded text-left sm:mt-2 p-2 sm:drop-shadow-xl",
    groupHeading:
      "cursor-default mt-2 mb-0.5 px-1.5 uppercase text-sm text-rose-300",
    item: "cursor-pointer p-1.5 text-lg overflow-ellipsis overflow-hidden text-slate-700",
    highlightedItem:
      "cursor-pointer p-1.5 text-lg overflow-ellipsis overflow-hidden text-slate-700 rounded bg-blue-50",
  };

  return (
    <>
      <div className="flex gap-4">
        <div>
          <Turnstone
      // All of these are commented out for testing
            // cancelButton={true}
            // clearButton={true}
            // debounceWait={250}
            // id="search"
            listbox={listbox}
            // defaultListbox={{ data: [] }}
            // listboxIsImmutable={true}
            // matchText={true}
            // maxItems={10}
            // name="search"
            // noItemsMessage="We found no places that match your search"
            // placeholder="Search for a location"
            styles={styles}
            // typeahead={true}
            onSelect={searchSelected}
          />
          {results && (
            <ul>
              {results.map((game) => (
                <li key={game._id}>{game.description}</li>
              ))}
            </ul>
          )}
        </div>
        <div>
          Distance:{" "}
          <select
            className="select select-bordered"
            onChange={(e) => {
              setDistance(e.target.value);
              searchSelected();
            }}
            defaultValue={distance}
          >
            {distanceArray.map((d) => (
              <option key={d} value={d}>
                {d}
              </option>
            ))}
          </select>
        </div>
      </div>
    </>
  );
}

```

It seems to have something to do with calling these async functions, but I'm not sure what or why. Any thoughts?

### Verify canary release

- [X] I verified that the issue exists in the latest Next.js canary release

### Provide environment information

```bash
Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 21.6.0: Thu Sep 29 20:13:56 PDT 2022; root:xnu-8020.240.7~1/RELEASE_ARM64_T6000
Binaries:
  Node: 20.1.0
  npm: 9.7.2
  Yarn: N/A
  pnpm: N/A
Relevant Packages:
  next: 14.0.4-canary.17
  eslint-config-next: 14.0.3
  react: 18.2.0
  react-dom: 18.2.0
  typescript: 5.3.2
Next.js Config:
  output: N/A
```


### Which area(s) are affected? (Select all that apply)

Not sure

### Additional context

_No response_