DOM 属性不会在 Gatsby 中更新

DOM Attributes do not update in Gatsby

我在 运行 gatsby build.

之后的静态文件上遇到了一个奇怪的问题

DOM 的属性(如 className)无法通过监听 prop 更改来更新,但 DOM 的内容(如文本或 DOM的children.

// 版本 1,不工作

const ThemeProvider = ({ isLight, children }) => {
  return (
    <div className={isLight ? 'light-theme' : 'dark-theme'}> // <- does not change when `isLight` updating
       <h1>{isLight ? 'light-theme' : 'dark-theme'}</h1> // <- changes when `isLight` updating
      {children}
    </div>
  )
}

// 版本 2,不工作

// still having the same issue
const ThemeProvider = ({ isLight, children }) => {
  if (isLight)
    return (
      <div className="light-theme">
       <h1>{isLight ? 'light-theme' : 'dark-theme'}</h1>
        {children}
      </div>
    )
  return (
    <div className="dark-theme">
       <h1>{isLight ? 'light-theme' : 'dark-theme'}</h1>
      {children}
    </div>
  )
}

// 版本 3,正在运行

const ThemeProvider = ({ isLight, children }) => {
  if (isLight)
    return (
      <div className="light-theme">
       <h1>{isLight ? 'light-theme' : 'dark-theme'}</h1>
        {children}
      </div>
    )
  return (
    <section className="dark-theme"> // <-- change to the different DOM, everything works fine
       <h1>{isLight ? 'light-theme' : 'dark-theme'}</h1>
      {children}
    </section>
  )
}

没有你的最小例子很难说,但我想我可能知道你可能犯的错误。

当你在服务器上的虚拟-DOM(所以在 SSR 期间)和水合作用的虚拟-DOM(浏览器中的第一个 运行)是不同。

如果您编写如下代码,就会发生这种情况:

export default function Comp() {
  let test = 1;
  if (typeof window !== undefined) {
    test = 2;
    // usually you'd be using some browser API or
    // reading query params or something here
  }
  return <a href={test}>not updated</a>;
}

发生的情况是来自服务器的 DOM 将包含 <a href=1>,然后当 React/Gatsby 是 运行ning ReactDOM.hydrate 一旦浏览器有加载后,它使用它作为 DOM 在服务器上呈现时的 "truth"。

唯一的问题是 ReactDOM.hydrate 的结果是 <a href=2>。所以当真实的ReactDOM.render之后是运行(做所谓的和解)时,虚拟的DOM会看到<a href=2><a href=2>是一回事.即使实际DOM有<a href=1>。所以它不会得到更新。


那么如何解决这个问题?好吧,上面的代码有问题,你不应该那样写,你可以在副作用中这样做,比如 useEffectcomponentDidMount。这样SSR和rehydrate会得到一样的结果。

对于我们上面的简单示例,它可能如下所示:

export default function Comp() {
  const [test, setTest] = useState();
  useEffect(() => { setTest(2); }, [setTest])
  return <a href={test}>updated</a>;
}