【问题标题】:React not updating state with setState correctly inside promises在 Promise 中正确响应不使用 setState 更新状态
【发布时间】:2019-12-23 08:34:42
【问题描述】:

我正在尝试在单击按钮时从反应组件内的 api 加载更多数据。此数据应与已加载的数据合并。加载时我想显示一个微调器。我正在使用 axios 和 react 钩子组件。

我的代码:

const App = props => {
    const [data, setData] = useState({ ... });
// ...
    function handleLoadMore(uri) {
        setData({...data, isLoadingMore: true})
        axios
            .get(uri)
            .then(response => {
                setData({
                    ...data,
                    items: [...data.items, response.data.items]
                })
            })
            .catch(error => {
                setData({
                    ...data,
                    error: 'An error occured'
                })
            })
            .finally(() => {
                setData({
                    ...data,
                    isLoadingMore: false
                })
            })
    }

我希望这首先显示微调器,加载新数据,将其与预先存在的数据合并并显示新的项目列表,但新数据不会合并。 ajax调用返回正确的结果,所以没有问题。令我意想不到的是,如果我删除 .finally(..) 一切都按预期工作,即使微调器也会消失。

那么问题来了,setData 如何更新 Promise 中的状态?在我看来,省略.finally(..) 一点也不明确,因为isLoadingMore 在代码中从未设置为false,但无论如何它都会以某种方式更新为false

【问题讨论】:

  • 该代码不会做任何事情,它缺少第二个 setData 调用中的结束 ]。请不要重新输入代码;使用复制和粘贴。
  • 我修好了。除了那个错误,我认为它应该做什么很清楚

标签: javascript reactjs promise setstate


【解决方案1】:

很难说,因为代码不完整,但我发现了两个问题(假设缺少的] 只是问题中的一个错字):

  1. 您没有在setData 通话中分散response.data.items
  2. 您正在使用现有状态来设置新状态,但没有使用状态设置器的回调版本进行设置。文档对此不一致,但您应该在基于现有状态设置状态时使用回调版本,除非您知道更新某些特定事件处理程序中的状态(如 @ 987654325@),React 专门为其处理刷新更新。

所以(见 cmets):

const App = props => {
    const [data, setData] = useState({ ... });
    // ...
    function handleLoadMore(uri) {
        // *** Use callback
        setData(current => ({...current, isLoadingMore: true}));
        axios
            .get(uri)
            .then(response => {
                // *** Use callback, spread response.data.items
                setData(current => ({
                    ...current,
                    items: [...current.items, ...response.data.items]
                }));
            })
            .catch(error => {
                // *** Use callback
                setData(current => ({...current, error: 'An error occured'}));
            })
            .finally(() => {
                // *** Use callback
                setData(current => ({...current, isLoadingMore: false}));
            });
    }
}

但是,如果您使用这样的组合状态,我会将isLoadingMore 的清除与上面的内容结合起来(更多内容见下文)。

const App = props => {
    const [data, setData] = useState({ ... });
    // ...
    function handleLoadMore(uri) {
        setData(current => ({...current, isLoadingMore: true}));
        axios
            .get(uri)
            .then(response => {
                setData(current => ({
                    ...current,
                    items: [...current.items, ...response.data.items],
                    isLoadingMore: false // ***
                }));
            })
            .catch(error => {
                setData(current => ({
                    ...current,
                    error: 'An error occured',
                    isLoadingMore: false // ***
                }));
            });
    }
}

但是:您应该将separate useState calls 用于不同的状态项。

const App = props => {
    const [items, setItems] = useState([]);
    const [loadingMore, setLoadingMore] = useState(false);
    const [error, setError] = useState("");
    // ...
    function handleLoadMore(uri) {
        setLoadingMore(true);
        axios
            .get(uri)
            .then(response => {
                setItems(currentItems => [...currentItems, ...response.data.items]);
            })
            .catch(error => {
                setError('An error occured');
            })
            .finally(() => {
                setLoadingMore(false);
            });
    }
    // ...
}

注意这如何让更新更加离散,从而减少工作量(而不是不断地重新传播所有状态项)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-01-28
    • 2019-03-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多