当前位置: 首页 > 面试题库 >

React-在复杂的应用程序中智能地控制异步调用而没有任何副作用

周龙光
2023-03-14
问题内容

React提出的有问题的codelayer1提出的解决方案-控制多个Ajax调用存在直接在动作创建者内部访问状态的问题-反模式

因此,如果不访问动作创建者内部的状态,我将监听组件中的batchRequestCompleted状态。当组件prop
batchRequestCompleted变为true(表示先前的请求已完成)时,我将检查是否有任何待处理的请求。如果是,我将调度操作以处理这些下一个请求。因此从根本上讲,佐贺将行动称为行动,而行动又会改变状态。修改状态后,将从组件分派另一个处理进一步请求的操作。这样,传奇就永远不会访问状态。

上面的解决方案听起来不错,但是要付出行动创建者完成之前路线更改中提到的问题。也就是说,如果在清除队列之前有人导航到其他路由,放置在队列中的请求将发生什么情况。

我可以解决React-控制多个Ajax调用中提到的问题,而无需访问动作创建者内部的状态,也不必使组件回到图片中以调度动作来清除挂起的队列。


问题答案:

我做了一些回购github.com/adz5a/so-stream-example来说明我将如何解决您的问题。

此仓库使用两个库xstreamrecompose。前者通过其运算符提供ObservableStreams的实现,而后者则通过React进行连接。

在一切之前必须有一个概念:ES
Observables。它们覆盖在文章的深度,如这个(我强烈建议阅读和听力从莱什奔过去的文章/会谈,对这个问题)。

Observabes是一种惰性原语,用于随时间建模值。在JS中,我们还有另一个用于执行异步操作的原语:Promises。这些模型eventualvalue or error并不因此而懒惰,而是渴望。对于React组件(或更常见的是UI),我们对延迟很感兴趣,因为 事情可能出错
用户可能想要中断长时间运行的进程,它可能崩溃,更改路线等。

那么,我们如何解决您的问题:控制一个长时间运行的进程,该进程可能会由于用户交互而中断(获取很多行)?

首先,UI:

export class AnswerView extends React.Component {
    static propTypes = {
        // called when the user make a batch 
        // of request
        onStart: PropTypes.func.isRequired,
        // called when you want to stop the processing
        // of requests ( when unmounting or at the request
        // of the user )
        onStop: PropTypes.func.isRequired,
        // number of requests completed, 0 by default
        completedRequests: PropTypes.number.isRequired,
        // whether it's working right now or not
        processing: PropTypes.bool.isRequired
    };
    render () {

        // displays a form if no work is being done,
        // else the number of completed requests
        return (
            <section>
                <Link to="/other">Change Route !</Link>
                <header>
                    Lazy Component Example
                </header>
                {
                    this.props.processing ?
                        <span>{"requests done " + this.props.completedRequests}<button onClick={this.props.onStop}>Stop !</button></span>:
                        <form onSubmit={e => {
                                e.preventDefault();
                                this.props.onStart(parseInt(e.currentTarget.elements.number.value, 10));
                            }}>
                            Nb of posts to fetch<input type="number" name="number" placeholder="0"/>
                            <input type="submit" value="go"/>
                        </form>
                }
            </section>
        );

    }
    componentWillMount () {
        console.log("mounting");
    }
}

非常简单:一种带有输入的表单,用于输入要执行的请求数(可以在表组件上选中复选框…)。

其道具如下:

  • onStart:带所需数字的fn
  • onStop:fn,不包含任何参数,不表示我们要停止。可以钩在按钮上,或者在这种情况下,可以钩住componentWillUnmout
  • completeRequests:整数,计数已完成的请求,0。
  • 处理:布尔值,指示是否正在进行工作。

它本身并不能做太多事情,所以让我们介绍一下recompose。其目的是通过HOC
增强
组件。在此示例中,我们将使用帮助器。mapPropsStream

注意:在此答案中,我可以交替使用stream /
Observable,但这在通常情况下不正确。流是一个Observable,operators可将发射的值转换为新的Observable。

对于React组件,我们可以使用标准api来观察其道具:第一个在componentWillMount,然后在componentWillReceiveProps。我们还可以通过componentWillUnmount发出何时不再发出道具的信号。我们可以构建以下(大理石)图:(p1 --p2--..--pn--|管道指示流的完成)。

增强程序代码发布在下面并带有注释。

需要理解的是,所有带有流的事物都可以像信号一样被接近:通过将所有事物建模为流,我们可以确保通过发送适当的信号,我们可以具有所需的行为。

export const enhance = mapPropsStream(prop$ => {

    /*
     * createEventHandler will help us generates the callbacks and their
     * corresponding streams. 
     * Each callback invocation will dispatch a value to their corresponding
     * stream.
     */

    // models the requested number of requests
    const { handler: onStart, stream: requestCount$ } = createEventHandler();
    // models the *stop* signals
    const { handler: onStop, stream: stop$ } = createEventHandler();

    // models the number of completed requests
    const completedRequestCount$ = requestCount$.map( n => {

        // for each request, generate a dummy url list
        const urls = Array.from({ length: n }, (_, i) => `https://jsonplaceholder.typicode.com/posts/${i + 1}` );

        // this is the trick : we want the process to be aware of itself when
        // doing the next operation. This is a circular invocation so we need to
        // use a *proxy*. Note : another way is to use a *subject* but they are
        // not present in __xstream__, plz look at RxJS for a *subject* overview
        // and implementation.
        const requestProxy$ = xs.create();

        const count$ = requestProxy$
        // a *reduce* operation to follow where we are
        // it acts like a cursor.
            .fold(( n ) => n + 5, 0 )
        // this will log the current value
            .debug("nb");

        const request$ = count$.map( n => Promise.all(urls.slice(n, n + 5).map(u => fetch(u))) )
            .map(xs.fromPromise)
            .flatten()
            .endWhen(xs.merge(
        // this stream completes when the stop$ emits
        // it also completes when the count is above the urls array length
        // and when the prop$ has emitted its last value ( when unmounting )
                stop$,
                count$.filter(n => n >= urls.length),
                prop$.last()
            ));



        // this effectively activates the proxy
        requestProxy$.imitate(request$);

        return count$;

    } )
        .flatten();

    // models the processing props,
    // will emit 2 values : false immediately,
    // true when the process starts.
    const processing$ = requestCount$.take(1)
        .mapTo(true)
        .startWith(false);

    // combines each streams to generate the props
    return xs.combine(
        // original props
        prop$,
        // completed requests, 0 at start
        completedRequestCount$.startWith(0),
        // boolean indicating if processing is en route
        processing$
    )
        .map(([ props, completedRequests, processing ]) => {

            return {
                ...props,
                completedRequests,
                processing,
                onStart,
                onStop
            };

        })
    // allows us to catch any error generated in the streams
    // very much equivalent to the new ErrorBoundaries in React
        .replaceError( e => {
            // logs and return an empty stream which will never emit,
            // effectively blocking the component
            console.error(e);
            return xs.empty();
        } );

});

export const Answer = enhance(AnswerView);

我希望这个答案不会太过复杂,随时问任何问题。

附带说明一下,经过一番研究,您可能会注意到processing逻辑中并没有真正使用布尔值,而只是用来帮助UI知道发生了什么:这比将状态附加到状态表上要干净得多this。一个组件。



 类似资料:
  • 当技能处于开发阶段时,我可以在开发控制台中调用它。在Echo或Amazon Alexa应用程序中,该技能无法调用。 技能在技能之下显现 有人知道如何解决这个问题吗?

  • 要在控制台应用程序中开始使用Hangfire,您需要首先将Hangfire包安装到控制台应用程序。因此,使用您的软件包管理器控制台窗口进行安装: PM> Install-Package Hangfire.Core 然后添加任务存储安装所需的软件包。例如,使用SQL Server: PM> Install-Package Hangfire.SqlServer 仅需 Hangfire.Core 软件包

  • 我们正在升级一个J2EE应用程序(JSP 日志中显示以下内容: 应用程序确实安装(State=“Installed”),但无法启动。如果我们删除并重新安装它,则不会发生错误,其状态为“活动”,我们可以测试应用程序。然而,当服务器重新启动时,上述异常会出现在日志中,并且应用程序处于“ADMIN”状态。它无法启动,因此我们将其删除,并通过管理控制台重新安装,然后运行它,让我们对其进行测试。 应用程序中

  • 问题内容: 我有两个MVC网站。站点1的控制器使用以下代码调用站点2 我可以直接浏览URL并查看JSON。但是从MVC中的对等网站下载JSON的正确方法是什么? 问题答案: 你或许应该把控制器方法为方法和用途,以避免死锁。 Microsoft的ASP.NET教程包括有关ASP.NET MVC 4中异步方法 的页面。

  • 到目前为止,我认为我已经掌握了async await如何使应用程序更具响应性的概念,但我有两点悬而未决: 层注意事项异步等待是否必须从存储库层一直到MVC或WCF层才能获得性能优势,或者我可以只对需要很长时间的存储库方法进行异步操作吗? “等待”用法如果我只能在存储库级别工作,有一部分我不明白。使用这种(低层)方法,线程能够在等待io绑定代码完成的同时为传入的客户端请求提供服务吗? 在我看来,当长

  • 没有await并且在非异步方法中调用异步方法的行为是什么?我这样问是因为我看到Visual Studio在调用异步方法时没有显示任何警告,好像这是一件非常正常的事情。在这种情况下,异步方法的行为是否像是同步的?