React - 具有意外退货订单的异步调用

时间:2018-03-05 10:36:21

标签: javascript reactjs asynchronous

我有一个反应组件(让我们称之为Logs),其中包含另一个反应组件(让我们称之为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个请求数据。

有些时候,asyncCall以错误的顺序返回结果 - 您单击一次并请求第4个,然后再次单击它并在返回第4个之前请求第3个 .then。

确保从第3个而不是第4个呈现数据的React方法是什么,无论哪个服务器调用首先返回?

[edit]这不是关于使用setState回调。如果我在setState回调中移动了我的asyncCall,这个问题仍然存在。 (虽然如果你想建议我应该在setState回调中移动我的asyncCall,我很乐意接受这个建议 - 它只是没有解决我的问题)

1 个答案:

答案 0 :(得分:1)

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

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

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