繁体   English   中英

反应-异步调用具有意外的返回顺序

[英]React - Async calls with an unexpected return order

我有一个react组件(我们称它为Logs ),其中包括另一个react组件(我们称它为DateChanger ),就本例而言,它仅用于更改父组件的日期。 当Logs获得日期更改时,它将进行一次异步调用,以使用服务器中的数据更新其自身的状态:

class Logs extends React.Component {
    ....

    onDateChange(newDate) {
        this.setState({loading: true, date: newDate});
        asyncCall(newDate)
            .then(results => {
                this.setState({data: results, loading: false})
            });
    }

    render() {
        return (
            ....
            <DateChanger onChange={this.onDateChange}>
            ....
        )
    }
}

我遇到的问题是,如果有人快速连续两次更改日期,则呈现的“数据”并不总是来自正确的日期。

因此,我的具体意思是,在此示例中,DateChanger具有一个按钮,可以将日期向前和向后更改1天。 因此,今天是5号,有人可以单击日期更改器上的后退按钮以从4号请求数据,然后再次单击它以从3号请求数据。

asyncCallasyncCall会以错误的顺序返回结果-您单击一次返回并请求第4个,然后再次单击它并请求第3个,然后返回第4个,有时服务器会返回第3个的数据,并且然后第4的数据,因为这是最近它呈现给用户第四届数据.then进行处理。

无论哪个服务器调用首先返回,React如何确保从第三位而不是第四位呈现数据?

[edit]这与使用setState回调无关。 如果将asyncCall移到setState回调中,则此问题仍然存在。 (尽管如果您想建议我将asyncCall移到setState回调中,我会很乐意接受该建议-但这并不能解决我的问题)

对于一种快速而肮脏的解决方案,您可以检查得到的响应是否是您的组件正在寻找的响应。

onDateChange(newDate) {
    this.setState({loading: true, date: newDate});
    asyncCall(newDate)
        .then(results => {
            // Update state only if we are still on the date this request is for.
            if (this.state.date === newDate) {
                this.setState({data: results, loading: false})
            }
        });
}

但是,这样的解决方案具有更好的关注点分离和更高的可重用性。

// Request manager constructor.
// Enables you to have many different contexts for concurrent requests.
const createRequestManager = () => {
    let currentRequest;

    // Request manager.
    // Makes sure only last created concurrent request succeeds.
    return promise => {
        if (currentRequest) {
            currentRequest.abort();
        }

        let aborted = false;

        const customPromise = promise.then(result => {
            if (aborted) {
                throw new Error('Promise aborted.');
            }

            currentRequest = null;

            return result;
        });

        customPromise.abort = () => {
            aborted = true;
        };

        return customPromise;
    };
};

class Logs extends React.Component {
    constructor(props) {
        super(props);

        // Create request manager and name it “attemptRequest”.
        this.attemptRequest = createRequestManager();
    }

    onDateChange(newDate) {
        this.setState({loading: true, date: newDate});
        // Wrap our promise using request manager.
        this.attemptRequest(asyncCall(newDate))
            .then(results => {
                this.setState({data: results, loading: false})
            });
    }

    render() {
        return (
            ....
            <DateChanger onChange={this.onDateChange}>
            ....
        )
    }
}

请记住,我们实际上并没有中止请求,只是忽略了它的结果。

暂无
暂无

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

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