DOM 属性不会在 Gatsby 中更新
DOM Attributes do not update in Gatsby
我在 运行 gatsby build
.
之后的静态文件上遇到了一个奇怪的问题
DOM 的属性(如 className
)无法通过监听 prop 更改来更新,但 DOM 的内容(如文本或 DOM的children.
- 仅在 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>
)
}
没有你的最小例子很难说,但我想我可能知道你可能犯的错误。
当你在服务器上的虚拟-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>
。所以它不会得到更新。
那么如何解决这个问题?好吧,上面的代码有问题,你不应该那样写,你可以在副作用中这样做,比如 useEffect
或 componentDidMount
。这样SSR和rehydrate会得到一样的结果。
对于我们上面的简单示例,它可能如下所示:
export default function Comp() {
const [test, setTest] = useState();
useEffect(() => { setTest(2); }, [setTest])
return <a href={test}>updated</a>;
}
我在 运行 gatsby build
.
DOM 的属性(如 className
)无法通过监听 prop 更改来更新,但 DOM 的内容(如文本或 DOM的children.
- 仅在 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>
)
}
没有你的最小例子很难说,但我想我可能知道你可能犯的错误。
当你在服务器上的虚拟-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>
。所以它不会得到更新。
那么如何解决这个问题?好吧,上面的代码有问题,你不应该那样写,你可以在副作用中这样做,比如 useEffect
或 componentDidMount
。这样SSR和rehydrate会得到一样的结果。
对于我们上面的简单示例,它可能如下所示:
export default function Comp() {
const [test, setTest] = useState();
useEffect(() => { setTest(2); }, [setTest])
return <a href={test}>updated</a>;
}