繁体   English   中英

反应无法在promise内使用setState正确更新状态

[英]React not updating state with setState correctly inside promises

我试图在单击按钮时从React组件内的api加载更多数据。 该数据应与已经加载的数据合并。 加载时,我想显示一个微调器。 我正在使用axios并响应钩子组件。

我的代码:

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如何更新承诺内的状态? 在我看来,根本不清除.finally(..) ,因为在代码中isLoadingMore从未设置为false,但是无论如何它都会更新为false

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

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

因此(请参阅评论):

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 // ***
                }));
            });
    }
}

但是 :您应该对不同的状态项使用单独的useState调用

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);
            });
    }
    // ...
}

请注意,这如何使更新更加离散,从而减少了工作量(不会不断重新散布所有状态项)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM