Ful*_*ine 5 reactjs material-ui server-side-rendering
我正在material-ui与SSR一起使用。我已经根据文档上的说明material-ui在我的应用程序上设置了 SSR 机制。它确实有效,但并非没有渲染问题,迄今为止很难调试。更多详细信息如下。
SSR + 加载状态(这会导致相关组件无法在其中一个 SSR 渲染通道中渲染,更多内容见下文)导致在第二个 SSR 渲染通道上渲染的特定组件的 className 中 ID 不一致,但不会在第二个 SSR 渲染通道上渲染首先(因为它的渲染以数据可用为条件)。
这会导致从服务器发送的标记对该组件具有不同的 CSS 类名称,从而导致水合作用时出现视觉不一致,如下所示:
SSRed 成分:
水合成分:
DOM 中实际可用的类是:
.PrivateSwitchBase-input-393 {
top: 0;
left: 0;
width: 100%;
cursor: inherit;
height: 100%;
margin: 0;
opacity: 0;
padding: 0;
z-index: 1;
position: absolute;
}
Run Code Online (Sandbox Code Playgroud)
但由于 CSS 类名不匹配,一个不存在的类PrivateSwitchBase-input-411被应用于 CheckBox input,并且它没有像应该的那样变得不可见,导致客户端水合时出现视觉故障。
我从 React 收到以下警告:
警告:道具
className不匹配。服务器:“PrivateSwitchBase-input-411”客户端:“PrivateSwitchBase-input-393”。
我希望className服务器和客户端中的匹配和组件渲染是相同的。
我有一个TodoItem组件:
import React from 'react';
import {
FormControlLabel,
Checkbox
} from '@material-ui/core';
const TodoItem = (props) => {
return (
<FormControlLabel style={props.style} control={<Checkbox/>} label={props.title} />
)
}
export default TodoItem;
Run Code Online (Sandbox Code Playgroud)
和一个Todos组件(简化版):
import React from 'react';
import SortableTree, { getFlatDataFromTree } from '../lib/sortable-tree';
import { observer } from "mobx-react";
import { useQuery } from '../models/reactUtils';
import { Paper } from '@material-ui/core';
const Todos = observer((props) => {
const {store, loading} = useQuery(store => store.fetchActiveTodoTree());
return (
<>
<Paper style={{padding: '20px'}}>
<SortableTree
treeData={store.activeTodoTree.toJSON()}
generateNodeProps={({node, path}) => ({
title: (
<TodoItem title={node.title} />
),
})}
/>
</Paper>
)
});
Run Code Online (Sandbox Code Playgroud)
我加载呈现该组件的应用程序Todos。该组件使用mst-gql从后端 API 加载一些数据,并将其传递给SortableTree组件;
从服务器运行时,我使用getDataFromTree函数来mst-gql等待数据承诺得到解析,并最终将 HTML 发送回客户端(我在此处省略了此代码,但如果需要,可以共享它。它看起来像这里的那个,只是我的版本使用mst-gql而不是Redux)。请注意,组件树需要渲染两次:
第一次触发任何数据获取承诺;
然后,一旦解决了这些承诺,就会完成最后一次传递,以使用可用的数据来渲染树。
将标记从服务器发送到客户端后,就会React.hydrate发生。此时,由于 CSS 类不存在,相关组件将使用可见输入进行渲染。
我确信问题的发生是由于2上述几点。第一次Todos渲染组件时,store.activeTodoTree数据尚不可用,因此SortableTree组件不会渲染任何内容,因此应该TodoItem由 内联使用的组件作为SortableTree其树节点(请参阅上面的屏幕截图)不会渲染第一次(但其他一切都是)。我不太清楚className中的 ID 后缀生成逻辑是如何工作的MUI,但正因为如此,类的后缀PrivateSwitchBase-input(用于 MUI 的 CheckBox 组件的内部复选框输入)在服务器和客户端之间 ID 不匹配,导致视觉上的我在上面的屏幕截图中显示了故障。
不过,一件有趣的事情是,即使在水合之后,该节点的子节点Foobar也会按预期渲染,如下所示:
您可以看到这些节点的复选框输入被隐藏,这意味着 CSS 类已正确应用。我不知道为什么这只发生在根节点上。
不过,我设法找到了一个肮脏的解决方法:如果我添加一个始终在所有 SSR 渲染通道中渲染的虚拟对象,如下所示:
import SortableTree, { getFlatDataFromTree } from '../lib/sortable-tree';
import { observer } from "mobx-react";
import { useQuery } from '../models/reactUtils';
import { Paper } from '@material-ui/core';
const Todos = observer((props) => {
const {store, loading} = useQuery(store => store.fetchActiveTodoTree());
return (
<>
<TodoItem title="I am here so that my className ID matches :("/>
<Paper style={{padding: '20px'}}>
<SortableTree
treeData={store.activeTodoTree.toJSON()}
generateNodeProps={({node, path}) => ({
title: (
<TodoItem title={node.title} />
),
})}
/>
</Paper>
)
});
Run Code Online (Sandbox Code Playgroud)
然后问题就消失了,所有内容都从服务器和客户端中完美呈现。这证实了这样的理论:发生不匹配是因为在第一个 SSR 渲染过程中组件未渲染(作为 SortableTree 的一部分)。
"@material-ui/core": "^4.9.10",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "^4.0.0-alpha.49",
"mobx-react": "^6.1.8",
"mobx-state-tree": "^3.15.0",
"mst-gql": "^0.7.1"
"react": "^16.10.2",
"react-dnd": "7.3.0",
"react-dnd-html5-backend": "7.0.1",
"react-dom": "^16.10.2",
"react-helmet": "^5.2.1",
"react-helmet-async": "^1.0.2",
Run Code Online (Sandbox Code Playgroud)
浏览器: Chrome 和 Firefox,最新版本。
我该如何处理这个问题?我无法确定这是否是我正在使用的库之一(MUI、mst-gql和SortableTree)中的错误,或者我是否遗漏了某些内容。
如果您需要我提供任何详细信息,请告诉我。任何见解表示赞赏!
提前致谢!
我花了一些时间尝试按照@Girish 的建议提取一个最小的示例,最终发现了问题。
它与material-ui或 无关mst-gql。react-router它与在a之外渲染的组件有关<Switch>。
我有一个组件,它基本上是s 的<FlashMessage>包装器。它曾经位于我的主要应用程序组件的底部。它的显示是由我观察到的一些 MST 属性控制的。这是我的应用程序组件的 JSX 标记:material-ui<SnackBar>
<>
<CssBaseline />
<Helmet
defaultTitle="Foobar"
/>
<Switch>
{this.flatRoutes}
</Switch>
<FlashMessage />
</>
Run Code Online (Sandbox Code Playgroud)
使用上面的 JSX,我原来的帖子中报告的问题仍然会发生。但是,如果我将其更改为:
<>
<CssBaseline />
<Helmet
defaultTitle="Foobar"
/>
<Switch>
{this.flatRoutes}
<FlashMessage />
</Switch>
</>
Run Code Online (Sandbox Code Playgroud)
那么这个问题就不会再发生了。请注意,我将该<FlashMessage/>组件移至“react-router”<Switch>组件内。
我仍然不知道为什么会导致这个问题的详细信息。如果我发现我会更新这篇文章。如果其他人有任何见解,请分享:)
| 归档时间: |
|
| 查看次数: |
2547 次 |
| 最近记录: |