为什么 Mobx observer 不触发 React 循环组件的重新渲染?

Why Mobx observer is not triggering re-render on React recurrent component?

我正在尝试创建一个将其状态存储在 mobx 可观察对象中的递归组件。

由于某些特殊原因,如果可观察对象在其属性中链接了另一个可观察对象,则 mobx-react-lite 中的 observer hoc 不会触发当可观察状态改变时重新渲染。

下面是示例,当可观察状态发生变化时,深度超过 1 的元素不会重新呈现。

...
const NodeView = (props: { node: Node }) => {
  useEffect(() => {
    autorun(() => {
      console.log(`(autorun) ${props.node.label} is expanded: ${props.node.expanded}`);
      // WRONG: when the node is a child of another node, this runs, but the NodeView is not re-rendered
    });
  }, []);

  console.log(`rerendering ${props.node.label} `);

  return (
    <>
      <div
        className="nodeHeader"
        onClick={action(() => {
          props.node.expanded = !props.node.expanded;
        })}
      >
        {props.node.label}
      </div>
      {props.node.expanded && (
        <div className="row">
          <div className="offset" />
          <div className="column">
            {props.node.children.map((child, index) => (
              <NodeView key={index} node={child} />
            ))}
          </div>
        </div>
      )}
    </>
  );
};
export default memo(observer(NodeView));

以下是可观察对象的创建方式:

...
interface Node {
  label: string;
  expanded: boolean;
  children: Node[];
  nonObservedStuff: any;
}

const turnIntoObservable = (node: Node) => {
  const children = node.children.map((child) => turnIntoObservable(child));

  let observableNode: Node = { ...node, children };

  // i'm trying to make observable not all, but only some specific properties
  observableNode = makeObservable(observableNode, {
    expanded: true,
    children: observable.shallow
  });

  return observableNode;
};

let rootNodes: Node[] = ...

rootNodes = rootNodes.map((node) => turnIntoObservable(node));

Here is codesandbox demo

为什么更改由另一个可观察对象的属性链接的可观察对象的状态不会导致重新呈现观察该可观察对象的组件?

您正在导出 observer 组件 (export default memo(observer(NodeView));),但在 NodeView 内部您使用的是非观察者 NodeView

你需要立即用观察者包装它,所以递归版本也是反应式的:

const NodeView = observer((props: { node: Node }) => {}

此外,memo 会自动应用于所有 observer 组件,您无需手动重新应用它。