将 useState 与具有多个布尔字段的对象反应

React useState with Object with multiple boolean fields

我用 useState 初始化了这个对象:

const [
    emailNotifications,
    setEmailNotifications,
  ] = useState<emailNotifications>({
    rating: false,
    favourites: false,
    payments: false,
    refunds: false,
    sales: false,
  });

我创建了一个函数,该函数应该动态更改每个字段的值,但我正在努力为 onClick 分配相反的布尔值。这是函数:

const handleEmailNotificationsSettings = (
    event: React.ChangeEvent<HTMLInputElement>
  ) => {
    setEmailNotifications({
      ...emailNotifications,
      [event.target.id]: !event.target.id,
    });
  };

我做错了什么?

你的方法是正确的,只是你试图在这里实现的一件小事是错误的。

setEmailNotifications({
  ...emailNotifications,
  [event.target.id]: !event.target.id, //Here
});

当您将动态值设置为您期望的状态时,它是不是

的布尔值

解决方案:

setEmailNotifications({
  ...emailNotifications,
  [event.target.id]: !emailNotifications[event.target.id],
});

@kunal panchal 的回答完全有效 Javascript,但它确实会导致 Typescript 错误,因为 event.target.id 的类型是 string 所以 Typescript 不确定它是emailNotifications 的有效密钥。您必须使用 as.

来断言它是正确的
!emailNotifications[event.target.id as keyof EmailNotifications]

避免这种情况的一种方法是通过查看 input 上的 checked 属性 而不是切换状态来获取 boolean 值。

作为旁注,使用 setState 回调获取当前状态是一个很好的最佳实践,这样如果多个更新一起批处理,您始终可以获得正确的值。

const _handleEmailNotificationsSettings = (
  event: React.ChangeEvent<HTMLInputElement>
) => {
  setEmailNotifications(prevState => ({
    ...prevState,
    [event.target.id]: event.target.checked,
  }));
};

这可能是复选框的最佳解决方案。


另一种对其他情况更灵活的方法是使用柯里化函数。我们不是从 event.target.id 获取 属性,它总是 string,而是将 property 作为参数传递,为每个 [=52= 创建一个单独的处理程序].

const handleEmailNotificationsSettings = (
  property: keyof EmailNotifications
) => () => {
  setEmailNotifications((prevState) => ({
    ...prevState,
    [property]: !emailNotifications[property]
  }));
};

const handleEmailNotificationsSettings = (
  property: keyof EmailNotifications
) => (event: React.ChangeEvent<HTMLInputElement>) => {
  setEmailNotifications((prevState) => ({
    ...prevState,
    [property]: event.target.checked
  }));
};

你这样使用:

<input
  type="checkbox"
  checked={emailNotifications.favourites}
  onChange={handleEmailNotificationsSettings("favourites")}
/>

这些解决方案避免了必须在事件处理程序中做出 as 断言,但有时它们是不可避免的。我正在使用 (Object.keys(emailNotifications) 遍历你的状态,我需要在那里做一个断言,因为 Object.keys 总是 returns string[].

import React, { useState } from "react";

// I am defining this separately so that I can use typeof to extract the type
// you don't need to do this if you have the type defined elsewhere
const initialNotifications = {
  rating: false,
  favourites: false,
  payments: false,
  refunds: false,
  sales: false
};

type EmailNotifications = typeof initialNotifications;

const MyComponent = () => {
  // you don't really need to declare the type when you have an initial value
  const [emailNotifications, setEmailNotifications] = useState(
    initialNotifications
  );

  const handleEmailNotificationsSettings = (
    property: keyof EmailNotifications
  ) => (event: React.ChangeEvent<HTMLInputElement>) => {
    setEmailNotifications((prevState) => ({
      ...prevState,
      [property]: event.target.checked
    }));
  };

  return (
    <div>
      {(Object.keys(emailNotifications) as Array<keyof EmailNotifications>).map(
        (property) => (
          <div key={property}>
            <label>
              <input
                type="checkbox"
                id={property}
                checked={emailNotifications[property]}
                onChange={handleEmailNotificationsSettings(property)}
              />
              {property}
            </label>
          </div>
        )
      )}
    </div>
  );
};

export default MyComponent;