问题描述
我有一个提交按钮,一个依赖于以下计算出的 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;
};
<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 上循环。