从rxjs observable订阅触发反应组件渲染(跳过初始渲染)

时间:2018-05-03 20:23:02

标签: reactjs redux rxjs

我正在尝试使用rxjs obserable包装器在redux应用程序中使用redux状态存储。 Source tutorial

当我从角度切换到nxx到redux时,我首先尝试了这种方法。现在我在反应应用程序中使用此模式。但是,我有点问题。当我订阅一些状态存储流时,我使用setState(foo)将值存储在组件中。这反过来会触发一个新的渲染周期。我希望每个组件有一个渲染周期而不是2个或更多。

我一直在尝试禁止组件的初始呈现,以便首先触发它,并且只通过状态存储订阅触发一次。当您有多个嵌套组件和多个订阅时,它们往往会创建浪费的渲染,只是为了执行app init。我知道React在优化多个渲染方面做得很好,但我仍然发现,关注渲染周期对于避免细微的错误是健康的。

有关如何从州商店订阅触发第一次呈现的任何建议吗?

app.module.tsx

private subscribeToAppSettings() {
    DEBUG.cmp && debug('Subscribe appSettings$');

    appSettings$().pipe(
        skip(1), // For REST api calls I skip the initial state
        takeUntil(this.destroyed$),
    )
        .subscribe(settings => {
            DEBUG.subscribe && debug('==> Observe appSettings$', [settings]);
            this.setState({ settings });
        });
}

正如您所看到的那样,AppModule和其他所有内容都会被渲染两次。这是一组经过筛选的日志,在应用程序运行render()方法时展示。只是初始阶段,没有用户交互。

enter image description here

1 个答案:

答案 0 :(得分:0)

在再次审查整个架构之后,我想我需要在组件中手动设置初始状态。现在,初始渲染正在进行有用的工作,第二次渲染将被反应变化检测忽略。

我还有额外的渲染周期。但是,我看到这是变化检测的状态。很多事情都会触发第二次渲染:init,路由器,事件处理程序,observables。只要React使用虚拟dom进行变更检测来清除实际没有变化的值,就不应该对性能产生实际影响。正如他们所说:我在错误的树上吠叫。

<强> state.service.tsx

{
  "access_token" : "myAccessToken",
  "token_type" : "bearer",
  "expires_in" : 3600,
  "refresh_token" : "myRefreshToken",
  "refresh_token_expires_in" : 604800,
  "scope" : "Meetings VoipCalling Glip SubscriptionWebhook Faxes Contacts RingOut SMS",
  "owner_id" : "11111111",
  "endpoint_id" : "22222222"
}

<强> app.module.tsx

/** Access state changes as an observable stream */
export const store$ = new Observable<AppState>(observer => {

    // All state store observable use `distinctUntilChanged()` operator.
    // Without this initial state, `distinctUntilChanged()` will be unable to compare previous and current state.
    // As a result, the webapi observable will miss the first response fron the server.
    observer.next(appInitialState);

    let appState: AppState;
    store.subscribe( () => {
        appState = store.getState();
        observer.next(appState);
    });

})

<强> search.overlay.smart.tsx

constructor(props: any) {
    super(props);
    DEBUG.construct && debug('Construct AppModule');

    this.state = {
        navigatorIsVisible: appInitialState.navigator.isVisible,
        searchOverlayIsVisible: appInitialState.search.isVisible
    } as State;

    getAppSettings();
}

<强> search.overlay.service.tsx

searchOverlayIsVisible$().pipe(
    takeUntil(this.destroyed$),
    skip(1), // Ignore init state
)
    .subscribe(searchOverlayIsVisible => {
        DEBUG.subscribe && debug('Observe searchOverlayVisiblity$', searchOverlayIsVisible);
        this.setState({ searchOverlayIsVisible });
        this.state.searchOverlayIsVisible
    });

<强>结论

  • export function toggleSearchOverlay(isVisible?: boolean) { if (DEBUG.service && DEBUG.verbose) debug('Toggle search overlay', isVisible); store.dispatch( searchActions.toggleSearch(isVisible) ); return searchOverlayIsVisible$(); } export const searchOverlayIsVisible$ = () => store$.pipe( map( state => SEARCH_VISIBILITY(state) ), distinctUntilChanged() ); observable中推送初始状态是必要的,因为我们需要所有状态存储可观察量来接收它们的第一个状态。如果没有此初始状态store$将无法运行先前和当前状态之间的比较。如果distinctUntilChanged()阻止了可见的内容,那么我们最终会阻止来自webapi的响应。这意味着即使状态存储收到第一组数据,我们也会看到空页。
  • 请注意,我们正在使用组件构造函数来设置初始状态。因此,我们使用第一个渲染周期进行有用的工作。在所有状态存储可观察量中使用distictUntilChanged将禁止第二次渲染。
  • 即使我们在构造函数中设置了init状态,我们仍然将初始状态保留在reducers中。所有skip(1)操作都需要初始状态才能开始。
  • 请注意,很多进程会触发第二次渲染:init,路由器,事件处理程序,observable。只要React使用虚拟dom进行更改检测来清除实际没有更改的值,就不会对DOM呈现性能产生实际影响。
  • 这意味着在TOGGLE中每次路线更改只有一次componentDidUpdate次呼叫几乎不可能。这意味着我们仍需要过滤掉对LessonsPage的重复调用。