将对象数组作为状态处理的问题

Problem on handling array of object as state

我试图将状态设置为对象数组,但失败了。

我使用 CRA 创建了项目,并对状态使用了 react-hooks。 我使用 react-apollo-hooks 从 graphql 服务器获取数据。 我只是在codesandbox中声明了数据对象,但这并不影响我的问题。

对于每次点击,使用数据(对象数组)设置状态(对象数组)。

const data = {
  lists: [
    {
      id: "1"
    },
    {
      id: "2"
     },
    {
      id: "3"
    }
   ]
};

const Sample = () => {
  const [sample, setSample] = useState([]);

  const Updator = async () => {
    try {
      await data.lists.map(list => {
        setSample([
          ...sample,
          {
            label: list.id,
            value: list.id
          }
        ]);
        return true;
      });
      console.log(sample);
    } catch (err) {
      throw err;
    }
  };
  return (
    <div>
      <React.Fragment>
        <button
          onClick={e => {
            Updator();
          }}
        >
          Click me
        </button>
        <p>
          <strong>
            {sample.map(single => {
              return <div>{single.label}</div>;
            })}
          </strong>
        </p>
      </React.Fragment>
    </div>
  );
};

我在下面附上了所有测试代码。

这里是codesandboxlink。 https://codesandbox.io/s/zr50rv7qjp

我期待

的结果
123

通过点击,但结果是

3

此外,对于额外的点击,预期结果是

123
123

然后我明白了

3
3
.

当我使用 setSample() 时,我希望函数类似于 Array.push()。但它忽略了除最后一个之外的所有先前数据。

如有帮助将不胜感激!

您正在破坏 sample,当您循环和调用 setSample 时,它本身不会有最新版本。这就是为什么它只将 3 放入样本列表中的原因,因为地图的最后一次迭代将破坏一个空的样本列表并添加 3.

为确保您拥有样本的最新值,您应该将一个函数传递给 setSample。此函数将从 react 获取最新版本的状态变量。

setSample((latest) => {
    return [
      ...latest,
      {
        label: list.id,
        value: list.id
      }
    ]
});

state updater does batching 并且由于您在 map 中调用 setSample 方法,因此只有您的最后一个值被写入状态。

这里最好的解决方案是return来自地图的数据,然后像下面这样更新一次状态。

import React, { useState } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

const data = {
  lists: [
    {
      id: "1"
    },
    {
      id: "2"
    },
    {
      id: "3"
    }
  ]
};

const Sample = () => {
  const [sample, setSample] = useState([]);

  const Updator = async () => {
    try {
      const newData = data.lists.map(list => {
        return {
          label: list.id,
          value: list.id
        };
      });
      setSample([...sample, ...newData]);
    } catch (err) {
      throw err;
    }
  };
  return (
    <div>
      <React.Fragment>
        <button
          onClick={e => {
            Updator();
          }}
        >
          Click me
        </button>
        <p>
          <strong>
            {sample.map((single, index) => {
              return <div key={index}>{single.label}</div>;
            })}
          </strong>
        </p>
      </React.Fragment>
    </div>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<Sample />, rootElement);

Working Demo

另一种解决方案是使用回调方法来更新状态,但必须避免多次调用状态更新。