React 路由器组件呈现一个非常错误的状态

React router component render a very wrong state

import React, { useEffect, useState } from 'react'
import { Route } from 'react-router-dom'
import { BrowserRouter as Router } from 'react-router-dom'

export default function Test() {
  const s = useS()
  if (s === 'aa') {
    return null
  }
  return (
    <Router>
      <Route component={MyCpt}></Route>
    </Router>
  )
}

function MyCpt() {
  const s = useS()
  return <h1>{s}</h1>
}

function useS() {
  const [state, setState] = useState('aa')
  useEffect(() => {
    subscribe(setState)
  }, [])
  return state
}

const listeners = []
function subscribe(cb) {
  listeners.push(cb)
}

setTimeout(() => {
  listeners.forEach((cb) => {
    cb('bb')
  })
}, 2000)

这是完全可运行的代码,简单而连线。如您所见,MyCpt 将从 useS 呈现的状态最终将变为 bb(2 秒后)。但是,无论您等待多长时间,它都会呈现 aa。如果您在 if (s === 'aa') 中注释掉 return null,它将按预期呈现 bb

发生了什么黑客攻击?

如果你使用一些日志(下面的代码),它会很清楚。看看 console.log:

1. Test aa 
2. Subscribed 
3. Running listeners 1 
4. Test bb 
5. MyCpt aa 
6. Subscribed 

步骤 1-2) 首先记录“Test aa”,订阅发生在钩子内,但由于返回 null,组件退出。

步骤 3-4) 之后,注册的侦听器(注意到目前为止只有一个侦听器已注册,请参阅计数 1)是 运行,因此输出“运行 listeners 1”,然后从组件 Test.

再次打印“Test bb”

步骤 5-6) 现在,MyCpt 正在 运行,并且发生订阅,但不会再次调用侦听器。


这是带有日志的代码:

import React, { useEffect, useState } from "react";
import { Route } from "react-router-dom";
import { BrowserRouter as Router } from "react-router-dom";

export default function Test() {
  const s = useS();
  console.log("Test", s);
  if (s === "aa") {
    return null;
  }
  return (
    <Router>
      <Route component={MyCpt}></Route>
    </Router>
  );
}

function MyCpt() {
  const s = useS();
  console.log("MyCpt", s);

  return <h1>{s}</h1>;
}

function useS() {
  const [state, setState] = useState("aa");
  useEffect(() => {
    subscribe(setState);
  }, []);
  return state;
}

const listeners = [];
function subscribe(cb) {
  console.log("Subscribed");
  listeners.push(cb);
}

setTimeout(() => {
  console.log("Running listeners", JSON.stringify(listeners.length));
  listeners.forEach((cb) => {
    cb("bb");
  });
}, 2000);