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

时间:2019-04-23 08:34:53

标签: reactjs react-hooks

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

我使用CRA创建了项目,并对状态使用了响应挂钩。 我使用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>
  );
};

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

这里是codeandbox链接。 https://codesandbox.io/s/zr50rv7qjp

我希望得到的结果

123
点击

,但结果是

3

此外,对于其他点击,预期结果是

123
123

我得到

3
3

当我使用setSample()时,我期望的功能类似于Array.push()。但是它忽略了所有先前的数据以及最后一个数据。

任何帮助都会令人感激!

2 个答案:

答案 0 :(得分:2)

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

最好的解决方案是从地图返回数据,然后如下所示更新状态。

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

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

答案 1 :(得分:1)

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

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

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