如何在 React Hooks 的对象数组中更新状态“onChange”

IT技术 javascript reactjs react-hooks
2021-01-25 07:57:02

我检索了存储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>
)
6个回答

这是你如何做到的:

// 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>
)
这很奇怪。我在这里做了一个关于 jsfiddle 的小提琴,它对我来说很好用:jsfiddle.net/5w9n7cy6
2021-03-15 07:57:02
好吧,我想通了,是<li>标签中的键导致了它,因为它与输入字段共享相同的值,因此当该值更改时,整个 <li> 也会刷新。设置key={index}修复了它。
2021-04-02 07:57:02
这会改变当前状态。扩展运算符只做浅拷贝。
2021-04-07 07:57:02
@vinayr 在这种情况下,不需要深拷贝。
2021-04-07 07:57:02
谢谢它有点工作。我可以像这样定位到对象中的任何特定属性名称let propertyName = e.target.name; newArr[index][propertyName] = e.target.value;但是在输入一个字母后,它并没有停留在文本字段上,我必须再次单击该字段才能输入另一个字母。
2021-04-12 07:57:02

您可以通过将旧数组映射到新数组,在此过程中将要更改的内容交换为更新的项目,而无需更改即可完成此操作。

setDatas(
    datas.map(item => 
        item.id === index 
        ? {...item, someProp : "changed"} 
        : item 
))
@GuilhermeCrozariol 你的意思是{...item, someProp : "changed", someOtherProp: 42}
2021-03-16 07:57:02
这有点工作......但是,如果想要更改多个属性,而不仅仅是一个呢?
2021-03-26 07:57:02
index指的是数组中对象的索引,而不是id对象的属性
2021-04-03 07:57:02
对我来说,这不太奏效。我不得不使用: setUsers(users.map(usr => usr.subjectId === updatedUser.subjectId ? { ...updatedUser } : usr)); 在我的情况下,subjectId 是我列表中的唯一键。本质上,我正在映射我的列表并返回列表中的原始对象,除非 id 与我想要更新的对象相匹配。在这种情况下,我会返回一个新对象,其中包含更新后的对象的所有属性。
2021-04-04 07:57:02
我喜欢这是怎么回事,但它真的可以使用一些解释。
2021-04-08 07:57:02
setDatas(datas=>({
   ...datas,
   [index]: e.target.value
}))

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

这并不真正有效(在所有情况下),因为结果对象不是真正的 Array,Array.isArray 将返回 false,您将无法对数据使用 Array 方法。
2021-04-07 07:57:02
Maaaan,我希望我很久以前就知道这个。
2021-04-14 07:57:02

您甚至不需要使用索引(如果需要,键除外)或复制旧的数据数组,甚至可以内联进行,或者如果您更喜欢 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>
  );
};
这个对我来说很好用。谢谢。
2021-03-21 07:57:02

这就是我所做的:

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>
);