在构造函数中使用上下文API 是可能的,您只需在构造函数和超级方法中将其作为参数传递,如下所示:
export class Child extends React.Component {
static contextTypes = { registerComponent: PropTypes.func };
constructor(props, context) {
super(props, context);
context.registerComponent(props.id);
}
render() {
return <div id={this.props.id} />;
}
}
更新:
所以,问题与不支持任何交互性的服务器上的第一个渲染有关,此方法将阻止在服务器setState
上考虑任何内容,在您的示例中
不会给予任何帮助,所以我们需要在任何情况下都将是一种解决方法。<Provider />
我得到的解决方案是window.ids
每个跟踪的组件渲染都有一个新的(使用 nextjs 提供的 Head):
export class Child extends React.Component {
registerComponent = (id) => `
if (window.ids) {
window.ids = [...window.ids, ${id}];
} else window.ids = [${id}];
`;
render() {
return (
<Fragment>
<Head>
<script
className="track"
dangerouslySetInnerHTML={{
__html: `${this.registerComponent(this.props.id)}`,
}}
/>
</Head>
<div id={this.props.id} />
</Fragment>
);
}
}
所以window.ids
变量将在<App /> renders
.
这是一个repos 链接来测试它。
另一种解决方案可能是在服务器上使用全局变量,然后在每个跟踪的组件中,我们可以使用componentWillMount生命周期挂钩来改变该全局变量,因为它将仅在服务器上执行,然后在 html 模板上注入这些 id <head />
,但是如果我们正在执行 renderToString 方法,这是可能的。
第二种解决方案:使用 pages/ _document而不是 pages/ _app所以我们可以访问服务器before it renders to string!
:
这是 repo 分支:origin/workaround-using-document
-子组件:
export class Child extends React.Component {
render() {
return (
<Fragment>
<Head>
<meta className="track" id={this.props.id} />
</Head>
<div id={this.props.id} />
</Fragment>
);
}
}
- 文档组件(替换应用程序):
export default class MyDocument extends Document {
render() {
const { head } = this.props;
const idsFromHead = head.reduce(
(acc, { props }) =>
(props.className.includes('track') && [...acc, props.id]) || acc,
[]
);
return (
<html>
<Head>
<script
dangerouslySetInnerHTML={{
__html: `window.ids=[${idsFromHead}]`,
}}
/>
</Head>
<body className="custom_class">
<h3>{idsFromHead}</h3>
<Main />
<NextScript />
</body>
</html>
);
}
}
这种方法在服务器上 100% 有效,因为我们可以在服务器渲染发生之前捕获跟踪的 id。
但是<NextScript />
正在生成警告(不知道为什么,可能是来自 nextjs 的错误):