无法在 React-Native 的 'useEffect' 挂钩中更新用 'useState' 定义的数组

Can't update the array defined with 'useState' in 'useEffect' hook in React-Native

我有一个 feedItems 数组。我想根据 axios 获取请求的响应来设置此数组的项目。在屏幕上,我想显示这个数组元素的信息。所以,在一切之前,我必须设置这个数组,这样我才能显示这个数组元素的信息。

我在发出 API 请求时没有问题,而且我确定 response.data 不为空。但是,当我使用 setFeedItems(...feedItems, response.data[i]); 函数时,出现以下错误。

[Unhandled promise rejection: TypeError: Invalid attempt to spread non-iterable instance.]

代码如下:

const [feedItems, setFeedItems] = useState([{}]);

  useEffect(async () => {
    console.log("here");
    let accessToken = await AsyncStorage.getItem("accessToken");
    const response = await axios
      .get(
        "http://repor****-env-1.eba-nj*******/feed",
        {
          headers: {
            Authorization: "Bearer " + accessToken,
          },
        }
      )
      .then((response) => {
        for (let i = 0; i < response.data.length; i++) {
          setFeedItems(...feedItems, response.data[i]);
        }

        //console.log(feedArray);
        console.log(feedItems);
      });
  }, []);

问题是您将 [{}] 分散到 setFeedItems 的离散参数中。要追加 response.datafeeditems,你这样做:

.then((response) => {
    setFeedItems(feedItems => [...feedItems, ...response.data]);
    // No `console.log(feedItems)` here, it will show you outdated information
});

通知:

  • 使用回调形式,因为您是根据现有状态更新状态,所以您要确保使用 up-to-date 状态。
  • return 值周围的 [],因此您正在创建一个数组并将项目分散到其中。
  • 它将旧的 feedItems 和新的 response.data 传播到新数组中。

但是如果你想用 response.data 中的数据 replace feedItems(在这种情况下你可能会这样做),它是更简单:

.then((response) => {
    setFeedItems(response.data);
    // No `console.log(feedItems)` here, it will show you outdated information
});

此外,在正常情况下,您的 feedItems 将以 数组开始,而不是其中包含空对象的数组。所以:

const [feedItems, setFeedItems] = useState([]);

另外:useEffect 不会对 async 函数 returns 的承诺做任何有用的事情(并且会查看 return 值,看看是否它是一个函数),所以你不应该将 async 函数传递给它。由于您已经在使用显式承诺回调,因此没有理由使用 awaitasync 函数。此外,您应该处理 promise 拒绝。

将所有这些放在一起(使用“替换”而不是“附加”选项):

const [feedItems, setFeedItems] = useState([{}]);

useEffect(() => {
    AsyncStorage.getItem("accessToken").
    then(accessToken => axios.get(
        "http://repor****-env-1.eba-nj*******/feed",
        {
            headers: {
            Authorization: "Bearer " + accessToken,
        },
    }))
    .then((response) => {
        setFeedItems(feedItems => [...feedItems, ...response.data]);
    })
    .catch(error => {
        // ...handle/report the fact an error occurred...
    });
}, []);

实例:

const { useState, useEffect } = React;

// Mock `AsyncStorage`
const AsyncStorage = {
    getItem() {
        return Promise.resolve("some token");
    },
};

// Mock `axios`
const axios = {
    get() {
        return new Promise(resolve => {
            setTimeout(() => {
                resolve({data: [
                    {id: 1, text: "Item 1"},
                    {id: 2, text: "Item 2"},
                    {id: 3, text: "Item 3"},
                    {id: 4, text: "Item 4"},
                ]});
            }, 800);
        });
    },
};

const Example = () => {
    const [feedItems, setFeedItems] = useState([]);

    useEffect(() => {
        AsyncStorage.getItem("accessToken").
        then(accessToken => axios.get(
            "http://repor****-env-1.eba-nj*******/feed",
            {
                headers: {
                Authorization: "Bearer " + accessToken,
            },
        }))
        .then((response) => {
            setFeedItems(response.data);
        })
        .catch(error => {
            // ...handle/report the fact an error occurred...
        });
    }, []);

    return <div>
        <div>Item count: {feedItems.length}</div>
        {feedItems.map(({id, text}) => <div key={id}>{text}</div>)}
    </div>;
};

ReactDOM.render(<Example />, document.getElementById("root"));
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>

好的,您的代码有几个问题:

  1. useEffect 不应使用 async 函数。你应该在里面创建一个异步函数并在那里调用它。
  2. 您正在使用 awaitthen。应该坚持一个。
  3. 您实际上是在此处添加单个元素 setFeedItems(...feedItems, response.data[i]); 而不是数组。

试试用这个 setFeedItems([...feedItems, response.data[i]]);

这将修复错误,但不会解决您的问题,因为您正在变老 feedItem

为什么不一次用 response.data 中的所有项目更新状态? 而不是

for (let i = 0; i < response.data.length; i++) {
    setFeedItems(...feedItems, response.data[i]);
}

就这样:

if (response.data.length) setFeedItems(prevState => [...prevState, ...response.data])