简体   繁体   中英

Why does a useState value isn't updated inside useHotkeys callback?

I've got the following search suggest with React hooks that uses react-hotkeys-hooks to manage keypress.

Why does selectedUserItem not update on keypress Enter ? It stays 0 while the up and down keys change.

import { useHotkeys } from "react-hotkeys-hook";
import React, { useState } from "react";

import "./styles.css";
const itemsByName = [
  {
    id: 1,
    name: "Ice Cream"
  },
  {
    id: 2,
    name: "Banana Pudding"
  },
  {
    id: 3,
    name: "Chocolate Cake"
  },
  {
    id: 4,
    name: "Sponge Cake"
  },
  {
    id: 5,
    name: "Carrot Cake"
  }
];

const App = () => {
  const [selectedUserItem, setSelectedUserItem] = useState(0);

  // const [create] = useMutation(SAVE_USER_ITEM, {
  //   refetchQueries: ["UserItemsQuery"]
  // })

  const itemSelect = (e, item) => {
    e.preventDefault();
    // create({ variables: { input: { id: item.id } } });
    // console.log(item)
  };

  const increment = selectedUserItem => {
    const max = itemsByName.length - 1;
    return max > selectedUserItem ? selectedUserItem + 1 : max;
  };

  const decrement = selectedUserItem => {
    const min = 0;
    return min < selectedUserItem ? selectedUserItem - 1 : min;
  };

  useHotkeys(
    "*",
    (event, handler) => {
      // console.log(handler)
      switch (event.key) {
        case "ArrowDown":
          setSelectedUserItem(selectedUserItem => increment(selectedUserItem));
          break;
        case "ArrowUp":
          setSelectedUserItem(selectedUserItem => decrement(selectedUserItem));
          break;
        case "Enter":
          console.log(selectedUserItem);
          const userItem = itemsByName[selectedUserItem];
          console.log(userItem);
          break;
        default:
          console.log(event.key);
          break;
      }
    },
    {
      filter: () => true
    }
  );

  return (
    <div className="absolute w-3/4 mt-16 ml-8 py-2 bg-white shadow-xl rounded-lg">
      <h1>Index: {selectedUserItem}</h1>
      {itemsByName.map((item, i) => {
        return (
          <div
            href="#"
            onClick={e => itemSelect(e, item)}
            className={`${selectedUserItem === i ? "hovered" : ""} dessert`}
            key={item.id}
          >
            {item.id}: {item.name}
          </div>
        );
      })}
    </div>
  );
};

export default App;

useHotkeys internals use the useCallback and useEffect hooks, which need to know when some of its dependencies change. To make sure it works well with these hooks,useHotkeys offers to pass a deps array , like the other hooks mentioned, as its last parameter.

deps: any[] = [] : The dependency array that gets appended to the memoization of the callback. Here you define the inner dependencies of your callback. If for example your callback actions depend on a referentially unstable value or a value that will change over time, you should add this value to your deps array. Since most of the time your callback won't depend on any unstable callbacks or changing values over time you can leave this value alone since it will be set to an empty array by default.

In your code, it would looks like this:

// These never changes and do not rely on the component scope, so they
// can be defined safely outside the component.
const increment = selectedUserItem => {
  const max = itemsByName.length - 1;
  return max > selectedUserItem ? selectedUserItem + 1 : max;
};

const decrement = selectedUserItem => {
  const min = 0;
  return min < selectedUserItem ? selectedUserItem - 1 : min;
};

const App = () => {
  const [selectedUserItem, setSelectedUserItem] = useState(0);

  useHotkeys(
    "*",
    (event, handler) => {
      switch (event.key) {
        case "ArrowDown":
          setSelectedUserItem(increment);
          break;
        case "ArrowUp":
          setSelectedUserItem(decrement);
          break;
        case "Enter":
          console.log(selectedUserItem, itemsByName[selectedUserItem]);
          break;
        default:
          console.log(event.key);
          break;
      }
    },
    {
      filter: () => true
    },
    // The dependencies array which ensure that the data is up to date in the callback.
    [selectedUserItem, setSelectedUserItem]
  );

  // rest of the component

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM