问题描述
我正在使用最新版本的 react 和最新的 mobx 状态。
我试图让错误(或任何消息)出现一次,然后在操作时消失。 仅使用状态我没有想到简单的方法来做到这一点,所以我“发明”的是以下内容:
在 mobx 状态下,我会保留是否有错误、错误及其描述。 在简单的 JS 对象(对更改没有反应)中,我会保留是否显示此错误。 因此,当第一次显示错误时,布局呈现 - 它保存在显示错误的简单 JS 对象中,第二次如果显示错误 - 从 mobx 中删除它(从而更新 UI)。这是我对一次性消息使用反应式和非反应式状态的解决方法。
但是尝试实现我遇到了以下行为:
console.log('Render MainLayout...');
if (state.hasError || state.hasSuccess) {
console.log('Has message');
if (NonReactiveState.messagedisplayed) {
console.log('Message already displayed - hiding');
state.clearMessages();
} else {
console.log('displaying message...');
NonReactiveState.messagedisplayed = true;
}
}
if (state.hasError) {
console.log('displaying error...');
dummy = <div className="dummy-element-error"></div>
errorAlert = (
<Alert variant="danger">
<Alert.heading>{state.errorTitle}</Alert.heading>
<p>{state.errorDescription}</p>
</Alert>
)
} else {
console.log('No error to display');
dummy = <div className="dummy-element-no-error"></div>
}
if (state.hasSuccess) {
console.log('displaying success...');
successAlert = (
<Alert variant="success">
<Alert.heading>{state.successtitle}</Alert.heading>
<p>{state.successDescription}</p>
</Alert>
)
}
console.log('error el',errorAlert);
console.log('success el',successAlert);
console.log(state);
jsx 看起来像这样:
...
<div className="content">
<div className="container-fluid">
<div className="invalid-class-bla">
{dummy}
{errorAlert}
{successAlert}
</div>
...
</div>
</div>
...
但是我无法理解为什么我的 HTML 不显示警报而是具有以下内容:
它确实有来自正确 IF 块的控制台日志。它确实表明 errorAlert 不为空。但最后 HTML 错误警报是空的,“调试”元素包含无错误类。
我可能会遗漏一些真正在这里转储的东西,但我一直坚持下去。
解决方法
归根结底 - 我对一次性消息使用非反应状态的“天才”想法不适用于 React。经过大量调试,我发现 React 出于各种原因秘密重新渲染组件,隐藏了所有 console.log 和其他输出。
我设置了一个静态渲染计数器,每次调用该函数时都会递增 1。我在功能组件的开头生成了一次该编号,并在 console.log 和组件 JSX 中打印了该编号。
计数器看起来像这样:
let id = 0;
const uniqueId = () => {
return 'id-' + id++;
};
export default uniqueId;
简单组件:
const Debug = () => {
const id = uniqueId();
console.log('Render id: ' + id);
return (
<div className="render-id">
{id}
</div>
);
};
export default Debug;
结果是组件中的console.log总是在JSX实际渲染内容后面1-2次渲染。这意味着组件在后台渲染,在我看到 console.log 打印的初始渲染后禁用输出。
这实际上意味着它多次执行“一次性”逻辑,这打破了我基于真实渲染计数的非反应状态的整个想法。
我想我必须考虑另一种基于反应状态的单次消息渲染的想法,以便 React 可以在隐藏的重新渲染期间在内部进行管理。
编辑
好吧,经过更多的调试,我发现我的问题来自于开发模式下 React 的严格模式。文档明确指出:
严格模式无法自动为您检测副作用,但它可以 可以通过使它们更具确定性来帮助您发现它们。 这是通过有意重复调用以下函数来完成的:
- 类组件构造函数、render 和 shouldComponentUpdate 方法
- 类组件静态 getDerivedStateFromProps 方法
- 函数组件主体
- 状态更新器函数(setState 的第一个参数)
- 传递给 useState、useMemo 或 useReducer 的函数
没有严格模式,功能按预期工作......
编辑 2
我最初关于非反应状态不起作用的声明是错误的。它正在工作,但对它的任何更改都必须在 useEffect
挂钩中发生,因此它不会在严格模式双重执行期间执行。所以我的错误是我试图直接在功能组件的主体中进行更改(副作用)。这相当于类组件中的 render() 函数。这是不正确的。
对任何事物的任何更改,无论它是否具有反应性都必须发生在 useEffect
钩子中,否则结果是不可预测和随机的。基本错误,但违反了基本原则。