微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

TypeScript React / MobX 在组件禁用回调上“重新渲染太多”

如何解决TypeScript React / MobX 在组件禁用回调上“重新渲染太多”

我有一个提交按钮,一个依赖于以下计算出的 MobX 函数的表单

  @computed
  isSurveySubmitEnabled = (): boolean => {
    let questionsTovalidate = [];

    switch (this.surveyType) {
      case "preSurvey": {
        questionsTovalidate = PRE_SURVEY_QUESTIONS;
      }
      case "postSurvey": {
        questionsTovalidate = POST_SURVEY_QUESTIONS;
      }
      case "postTest": {
        questionsTovalidate = POST_TEST_QUESTIONS;
      }
    }

    runInAction(() => {
      this.isValidated = true;
    });

    questionsTovalidate.forEach((qObj: any) => {
      qObj.value.forEach((qVal: any) => {
        if (qVal.type === "date") {
          if (!this.entryPostParams[qVal.entryIdYear]) {
            runInAction(() => {
              this.isValidated = false;
            });
          }
        } else {
          if (!this.entryPostParams[qVal.entryId]) {
            runInAction(() => {
              this.isValidated = false;
            });
          }
        }
      });
    });

    return this.isValidated;
  };

回调在按钮上的disabled属性中使用如下

 <Button disabled={!isSurveySubmitEnabled()} style={{ marginTop: '5%' }} onClick={() => { doSubmitForm(); incrementActivityIndex() }}>Submit</Button>

我希望这仅在我的回调中使用的 this.entryPostParams observable 发生更改时运行,但在组件呈现时出现以下错误

The above error occurred in the <GoogleForm> component:
    in GoogleForm (at PageContent.tsx:32)
    in PageContent (at Continue.tsx:52)
    in Continue (at App.tsx:12)
    in Route (at App.tsx:12)
    in App (created by Context.Consumer)
    in withRouter(App) (at src/index.tsx:12)
    in Router (at src/index.tsx:11)

Consider adding an error boundary to your tree to customize error handling behavior.
Visit [url] to learn more about error boundaries.
console.<computed> @ index.js:1
Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
    at renderWithHooks (react-dom.development.js:14815)
    at updateFunctionComponent (react-dom.development.js:17034)
    at updateSimpleMemoComponent (react-dom.development.js:16972)
    at beginWork (react-dom.development.js:18687)
    at HTMLUnkNownElement.callCallback (react-dom.development.js:188)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:237)
    at invokeGuardedCallback (react-dom.development.js:292)
    at beginWork$1 (react-dom.development.js:23203)
    at performunitOfWork (react-dom.development.js:22154)
    at workLoopSync (react-dom.development.js:22130)
    at performSyncWorkOnRoot (react-dom.development.js:21756)
    at react-dom.development.js:11089
    at unstable_runWithPriority (scheduler.development.js:653)
    at runWithPriority$1 (react-dom.development.js:11039)
    at flushSyncCallbackQueueImpl (react-dom.development.js:11084)
    at flushSyncCallbackQueue (react-dom.development.js:11072)
    at flushPassiveEffectsImpl (react-dom.development.js:22883)
    at unstable_runWithPriority (scheduler.development.js:653)
    at runWithPriority$1 (react-dom.development.js:11039)
    at flushPassiveEffects (react-dom.development.js:22820)
    at react-dom.development.js:22699
    at workLoop (scheduler.development.js:597)
    at flushWork (scheduler.development.js:552)
    at MessagePort.performWorkUntilDeadline (scheduler.development.js:164)

跟踪堆栈跟踪并不清楚问题是什么。这个回调不应该只在 observable 发生变化时执行吗?

解决方法

不鼓励在计算得到的 getter 中运行操作,这可能是导致过多重新渲染的原因。每个 runInAction 都有可能导致观察者重新触发,显然,当计算 getter 的底层 observable 发生变化时,计算值本身也可能发生了变化。意思是,使用所述计算值的观察者也必须被通知有变化并重新触发。您可能会在这里看到无限循环的潜力。

您还应该收集更改的 observable 并尝试通过一个操作推送一批中的所有更改,以尽量减少重新渲染。 mobx 中最外层的 action 决定事务是否结束以及观察者是否被更新。这意味着动作中的动作不会单独通知观察者,但计算得到的 getter 不是动作。在您的案例中单独运行的每个 runInAction 都会引起反应。

TL;DR:不要在计算得到的 getter 中运行操作,也不要在 runInAction 上循环。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。