【问题标题】:How do I update states `onChange` in an array of object in React Hooks如何在 React Hooks 的对象数组中更新状态`onChange`
【发布时间】:2019-09-23 01:42:48
【问题描述】:

我已检索使用useState 存储在对象数组中的数据,然后将数据输出到表单字段中。现在我希望能够在我输入时更新字段(状态)。

我已经看到人们更新数组中属性状态的示例,但从来没有更新对象数组中的状态,所以我不知道该怎么做。我已经将对象的索引传递给回调函数,但我不知道如何使用它来更新状态。

// sample data structure
const datas = [
  {
    id: 1,
    name: 'john',
    gender: 'm'
  }
  {
    id: 2,
    name: 'mary',
    gender: 'f'
  }
]

const [datas, setDatas] = useState([]);

const updateFieldChanged = index => e => {
  console.log('index: ' + index);
  console.log('property name: '+ e.target.name);

  setData() // ??
}

return (
  <React.Fragment>
    {datas.map((data, index) => {
      <li key={data.name}>
        <input type="text" name="name" value={data.name} onChange={updateFieldChanged(index)} />
      </li>
    })}
  </React.Fragment>
)

【问题讨论】:

    标签: javascript reactjs react-hooks


    【解决方案1】:

    这是你的做法:

    // sample data structure
    /* const data = [
      {
        id:   1,
        name: 'john',
        gender: 'm'
      }
      {
        id:   2,
        name: 'mary',
        gender: 'f'
      }
    ] */ // make sure to set the default value in the useState call (I already fixed it)
    
    const [data, setData] = useState([
      {
        id:   1,
        name: 'john',
        gender: 'm'
      }
      {
        id:   2,
        name: 'mary',
        gender: 'f'
      }
    ]);
    
    const updateFieldChanged = index => e => {
      console.log('index: ' + index);
      console.log('property name: '+ e.target.name);
      let newArr = [...data]; // copying the old datas array
      newArr[index] = e.target.value; // replace e.target.value with whatever you want to change it to
    
      setData(newArr);
    }
    
    return (
      <React.Fragment>
        {data.map((datum, index) => {
          <li key={datum.name}>
            <input type="text" name="name" value={datum.name} onChange={updateFieldChanged(index)}  />
          </li>
        })}
      </React.Fragment>
    )
    

    【讨论】:

    • 感谢它的工作。我可以定位到对象中的任何特定属性名称,例如let propertyName = e.target.name;newArr[index][propertyName] = e.target.value;。但是在输入一个字母后,它并没有停留在文本字段上,我必须再次单击该字段才能输入另一个字母。
    • 这很奇怪。我在这里对 jsfiddle 做了一个小提琴,对我来说效果很好:jsfiddle.net/5w9n7cy6
    • 好吧,我想通了,是&lt;li&gt;标签中的键导致了它,因为它与输入字段共享相同的值,所以当该值改变时,整个
    • 刷新也是。设置key={index} 修复它。
  • 这会改变当前状态。扩展运算符只做浅拷贝。
  • @vinayr 在这种情况下,不需要深拷贝。
  • 【解决方案2】:

    您可以通过将旧数组映射到新数组来执行此操作而无需更改,同时将您想要更改的内容换成更新的项目。

    setDatas(
        datas.map(item => 
            item.id === index 
            ? {...item, someProp : "changed"} 
            : item 
    ))
    

    【讨论】:

    • Myindex 是指对象在数组中的索引,而不是对象的属性id
    • 我喜欢这里的发展方向,但它确实需要一些解释。
    • 对我来说这不太奏效。我不得不使用:setUsers(users.map(usr =&gt; usr.subjectId === updatedUser.subjectId ? { ...updatedUser } : usr)); 在我的情况下,subjectId 是我列表中的唯一键。本质上,我正在映射我的列表并返回列表中的原始对象,除非 id 与我要更新的那个匹配。在这种情况下,我返回一个新对象,其中包含我更新对象的所有属性。
    • 这有点用......但是,如果一个人想要更改多个属性,而不仅仅是一个呢?
    • @GuilhermeCrozariol 你的意思是{...item, someProp : "changed", someOtherProp: 42}?
    【解决方案3】:
    setDatas(datas=>({
       ...datas,
       [index]: e.target.value
    }))
    

    index 为目标位置,e.target.value 为新值

    【讨论】:

    • Maaaan,我希望我很久以前就知道了。
    • 这并不真正起作用(在所有情况下),因为生成的对象不是真正的数组,Array.isArray 将返回 false,您将无法对数据使用 Array 方法。
    【解决方案4】:

    您甚至不需要使用索引(如果需要,可以使用键除外),也不需要复制旧的数据数组,甚至可以内联进行操作,或者如果您不希望 updateFieldChanged 成为参数,则只需将数据作为参数传递排队。这样做很快:

      const initial_data = [
        {
          id: 1,
          name: "john",
          gender: "m",
        },
        {
          id: 2,
          name: "mary",
          gender: "f",
        },
      ];
    
      const [datas, setDatas] = useState(initial_data);
    
      return (
        <div>
          {datas.map((data, index) => (
            <li key={index}>
              <input
                type="text"
                value={data.name}
                onChange={(e) => {
                  data.name = e.target.value;
                  setDatas([...datas]);
                }}
              />
            </li>
          ))}
        </div>
      );
    };
    

    【讨论】:

    • 这个对我很有效。谢谢。
    【解决方案5】:

    这就是我的工作:

    const [datas, setDatas] = useState([
      {
        id: 1,
        name: "john",
        gender: "m",
      },
      {
        id: 2,
        name: "mary",
        gender: "f",
      },
    ]);
    
    const updateFieldChanged = (name, index) => (event) => {
      let newArr = datas.map((item, i) => {
        if (index == i) {
          return { ...item, [name]: event.target.value };
        } else {
          return item;
        }
      });
      setDatas(newArr);
    };
    
    return (
      <React.Fragment>
        {datas.map((data, index) => {
          <li key={data.name}>
            <input
              type="text"
              name="name"
              value={data.name}
              onChange={updateFieldChanged("name", index)}
            />
          </li>;
          <li key={data.gender}>
            <input
              type="text"
              name="gender"
              value={data.gender}
              onChange={updateFieldChanged("gender", index)}
            />
          </li>;
        })}
      </React.Fragment>
    );
    

    【讨论】:

      【解决方案6】:

      在此之前展开数组。因为如果不使用 useState 返回的方法,您无法直接更新钩子

      const newState = [...originalState]
      newState[index] = newValue
      setOriginalState(newState)
      

      如果它是一个字符串数组,这将修改状态的值并更新 useState 钩子。

      【讨论】:

        【解决方案7】:

        聚会有点晚了,但是可以选择将数组的内容传播到一个新对象中,然后在选定的索引中替换所需的对象,最后从该结果生成一个数组,这是一个简短的答案,可能不是大型数组的最佳选择。

        // data = [{name: 'tom', age: 15, etc...}, {name: 'jerry', age: 10, etc...}]
        // index = 1
        setData(Object.values({...data, [index]: {...data[index], name: 'the mouse' }}))
        // data = [{name: 'tom', age: 15, etc...}, {name: 'the mouse', age: 10, etc...}]
        

        【讨论】:

          【解决方案8】:

          基于@Steffan,因此作为你的方式使用:

          const [arr,arrSet] = useState(array_value);
          ...
          let newArr = [...arr];
            arr.map((data,index) => {
              newArr[index].somename= new_value;
            });
          arrSet(newArr);
          

          使用 useEffect 检查新的 arr 值。

          【讨论】:

            猜你喜欢
            相关资源
            最近更新 更多
            热门标签