【问题标题】:React useState with Object with multiple boolean fields使用具有多个布尔字段的 Object 反应 useState
【发布时间】:2021-03-23 10:59:47
【问题描述】:

我用 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,
    });
  };

我做错了什么?

【问题讨论】:

    标签: reactjs typescript use-state


    【解决方案1】:

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

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

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

    解决方案:

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

    【讨论】:

    • 感谢您的回答,我确实尝试了您的解决方案,但打字稿抱怨:元素隐式具有“任何”类型,因为“字符串”类型的表达式不能用于索引类型“电子邮件通知” .在“emailNotifications”类型上找不到带有“字符串”类型参数的索引签名。
    • @EdoardoTrotta 这种错误在处理表单时很常见,因为e.target.value 等总是只是string,即使你知道它更具体。所以你经常不得不使用as 断言。在这种情况下,我认为最好的类型安全方法是柯里化函数。我会写一个答案。
    • @EdoardoTrotta 要解决此问题,您需要像这样断言id 的类型:!emailNotifications[event.target.id as keyof EmailNotifications]
    • 谢谢@Linda,这正是我想要的。
    【解决方案2】:

    @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 作为参数传递给每个属性创建单独的处理程序。

    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 总是返回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;
    

    【讨论】:

    • 解释得很漂亮 :) 我不知道这个问题谢谢 :D
    猜你喜欢
    • 1970-01-01
    • 2011-08-09
    • 2015-12-15
    • 1970-01-01
    • 2021-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多