1

我在运行后的静态文件上有一个奇怪的问题gatsby build

DOM 的属性(如className)无法通过侦听 prop 更改来更新,但 DOM 的内容(如文本或 DOM 的子项)则不然。

  • 仅在 gatsby-build 之后发生,也就是在 SSR 中

// 版本 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>
  )
}
4

1 回答 1

1

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

当您在服务器上的虚拟 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>,然后当ReactDOM.hydrate浏览器加载后 React/Gatsby 运行时,它使用它作为 DOM 在服务器上呈现时的“真相”。

唯一的问题是它ReactDOM.hydrate会得到<a href=2>它的结果。所以当真实ReactDOM.render的之后运行时(进行所谓的协调),虚拟 DOM 会看到它<a href=2>并且<a href=2>是相同的东西。即使实际的 DOM 有<a href=1>. 所以不会更新。


那么你如何解决这个问题呢?好吧,上面的代码有问题,你不应该这样写,你可以用副作用来做,比如useEffector componentDidMount。这样 SSR 和再水合物就会得到相同的结果。

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

export default function Comp() {
  const [test, setTest] = useState();
  useEffect(() => { setTest(2); }, [setTest])
  return <a href={test}>updated</a>;
}
于 2020-01-27T14:40:55.160 回答