React 不呈现正确的条件元素

React does not render correct conditional element

我正在使用最新版本的 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 错误警报是空的,并且 'debug' 元素包含无错误 class.

我可能遗漏了一些真正转储在这里的东西,但我一直坚持下去。

归根结底 - 我对一次性消息使用非反应状态的“天才”想法将不适用于 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总是1-2渲染落后于渲染的JSX的实际内容。这意味着在我看到 console.log 打印的初始渲染后,组件在后台渲染并禁用输出。

这实际上意味着它多次执行 'one-time' 逻辑,这打破了我基于真实渲染计数的非反应状态的整个想法。

我想我必须考虑另一种基于反应状态的单次消息渲染的想法,以便 React 可以在隐藏的重新渲染期间在内部管理它。

编辑

好的,经过更多的调试,我发现我的问题来自React在开发模式下的严格模式。文档明确指出:

Strict mode can’t automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following functions:

  • Class component constructor, render, and shouldComponentUpdate methods
  • Class component static getDerivedStateFromProps method
  • Function component bodies
  • State updater functions (the first argument to setState)
  • Functions passed to useState, useMemo, or useReducer

在没有严格模式的情况下,功能会按预期工作...

编辑 2

我最初关于非反应状态不起作用的说法是错误的。它正在工作,但对它的任何更改都必须在 useEffect 挂钩中发生,因此它不会在严格模式双重执行期间执行。所以我的错误是我试图直接在功能组件的主体中进行更改(副作用)。这相当于 class 组件中的 render() 函数。这是不正确的。

对任何事物的任何改变,不管它是否是反应性的,都必须在 useEffect 钩子中发生,否则结果是不可预测的和随机的。基本错误但违反了基本原则。