如何在渲染前设置 parent 状态?
How do I set the parent state before rendering?
我知道这是一个很长的介绍,但我真的很难在这里说清楚
在我正在开发的这个 Next.js
应用程序中,我需要在具有特定 id
的 div
上设置一个 CSS 网格布局。我使用 styled-components createGlobalStyle
执行此操作。我希望此 App 组件的每个 child 都能够更改此网格布局(可选择传入替代 CSS 值)。
我已经在状态中设置了默认网格 CSS 值(回退,以防 child 调用该函数)。我创建了一个 class 方法 ( setLayout
),它可以使用接收到的参数设置布局状态。我将对此函数的引用传递给每个具有 React 上下文的 child 组件。
在我的 child 组件中,我现在可以导入此上下文并使用替代布局调用该函数。有用。但是...当我首先刷新页面时,后备布局会在 child 组件布局应用于根 div 之前闪烁一秒钟。
换句话说,在第一次呈现 App 组件时使用回退值呈现,然后 child 组件通过调用一个函数来呈现,该函数会更改 App 组件的状态,从而触发 re-render。然后在第二次渲染时,App 组件以正确的布局渲染。
我真的希望 App 组件在第一次渲染时渲染 child-provided 布局(如果提供的话)而不是首先渲染回退。 请帮助我实现这个。
P.S。我正在使用 server-side-rendering,虽然我认为这没有任何效果
// App component
// I have trimmed a lot of non relevant logic
export const LayoutSetter = createContext((layout: Layout): void => {});
export default class App {
constructor(props: AppProps) {
super(props);
this.state = {
layout: {
columns: "100vw",
rows: "50px 50px",
areas: "
header
content
",
},
};
}
setLayout(layout: Layout): void {
this.setState({ layout: layout });
}
render(): JSX.Element {
const { Component, pageProps } = this.props;
const __NextStyle = createGlobalStyle`
#__next {
display: grid;
grid-template-columns: ${this.state.layout.columns};
grid-template-rows: ${this.state.layout.rows};
grid-template-areas: "${this.state.layout.areas}";
}
`;
const setter = (layout: Layout): void => this.setLayout(layout);
return (
<>
<__NextStyle />
<LayoutSetter.Provider value={setter}>
<Component {...pageProps} />
</LayoutSetter.Provider>
</>
);
}
}
// Child component
const layout: Layout = {
columns: "90vw",
rows: "50px 500px",
areas: `
header
section
`,
};
const Home = (): JSX.Element => {
const setLayout = useContext(LayoutSetter);
useEffect(() => {
setLayout(layout);
}, []);
发生的事情是,由于 child 是 App
渲染的产物,它在 App
渲染之后才会变热。到那时 App
已经呈现并具有默认布局。
App
渲染(默认)
App
渲染的一部分触发了 Child
的渲染
- 在
Child
中执行setLayout
App
布局已更新。
不幸的是,您试图实现的目标(至少您试图实现它的方式)无法完成。您可以选择做的是在调用 setLayout
之前没有默认布局。在 child 中调用 setLayout
之前,基本上不会呈现特定布局。然后,在 setLayout
中,您可以执行如下操作。
setLayout(layout: Layout): void {
if (layout){
this.setState({ layout: layout });
} else {
this.setState({
layout: {
columns: "100vw",
rows: "50px 50px",
areas: "
header
content
",
}
});
}
}
初始状态可以是
layout: {
columns: "",
rows: "",
areas: "
header
content
",
},
...
但是你 运行 会遇到其他样式问题(比如没有布局可能看起来很糟糕)。您可以通过将 NextStyle
组件的不透明度设置为 0
直到 this.state.layout.columns.length > 0
.
来弥补这一点
var opacity = this.state.layout.columns.length > 0 ? 1 : 0;
const __NextStyle = createGlobalStyle`
#__next {
display: grid;
opacity: ${opacity};
grid-template-columns: ${this.state.layout.columns};
grid-template-rows: ${this.state.layout.rows};
grid-template-areas: "${this.state.layout.areas}";
}
`;
这样你至少在调用 setLayout
之前不会显示任何内容。我不确定你是否更喜欢你描述的默认布局的小闪烁,但这可能是你最好的选择。
我知道这是一个很长的介绍,但我真的很难在这里说清楚
在我正在开发的这个 Next.js
应用程序中,我需要在具有特定 id
的 div
上设置一个 CSS 网格布局。我使用 styled-components createGlobalStyle
执行此操作。我希望此 App 组件的每个 child 都能够更改此网格布局(可选择传入替代 CSS 值)。
我已经在状态中设置了默认网格 CSS 值(回退,以防 child 调用该函数)。我创建了一个 class 方法 ( setLayout
),它可以使用接收到的参数设置布局状态。我将对此函数的引用传递给每个具有 React 上下文的 child 组件。
在我的 child 组件中,我现在可以导入此上下文并使用替代布局调用该函数。有用。但是...当我首先刷新页面时,后备布局会在 child 组件布局应用于根 div 之前闪烁一秒钟。
换句话说,在第一次呈现 App 组件时使用回退值呈现,然后 child 组件通过调用一个函数来呈现,该函数会更改 App 组件的状态,从而触发 re-render。然后在第二次渲染时,App 组件以正确的布局渲染。
我真的希望 App 组件在第一次渲染时渲染 child-provided 布局(如果提供的话)而不是首先渲染回退。 请帮助我实现这个。
P.S。我正在使用 server-side-rendering,虽然我认为这没有任何效果
// App component
// I have trimmed a lot of non relevant logic
export const LayoutSetter = createContext((layout: Layout): void => {});
export default class App {
constructor(props: AppProps) {
super(props);
this.state = {
layout: {
columns: "100vw",
rows: "50px 50px",
areas: "
header
content
",
},
};
}
setLayout(layout: Layout): void {
this.setState({ layout: layout });
}
render(): JSX.Element {
const { Component, pageProps } = this.props;
const __NextStyle = createGlobalStyle`
#__next {
display: grid;
grid-template-columns: ${this.state.layout.columns};
grid-template-rows: ${this.state.layout.rows};
grid-template-areas: "${this.state.layout.areas}";
}
`;
const setter = (layout: Layout): void => this.setLayout(layout);
return (
<>
<__NextStyle />
<LayoutSetter.Provider value={setter}>
<Component {...pageProps} />
</LayoutSetter.Provider>
</>
);
}
}
// Child component
const layout: Layout = {
columns: "90vw",
rows: "50px 500px",
areas: `
header
section
`,
};
const Home = (): JSX.Element => {
const setLayout = useContext(LayoutSetter);
useEffect(() => {
setLayout(layout);
}, []);
发生的事情是,由于 child 是 App
渲染的产物,它在 App
渲染之后才会变热。到那时 App
已经呈现并具有默认布局。
App
渲染(默认)App
渲染的一部分触发了Child
的渲染
- 在
Child
中执行setLayout
App
布局已更新。
不幸的是,您试图实现的目标(至少您试图实现它的方式)无法完成。您可以选择做的是在调用 setLayout
之前没有默认布局。在 child 中调用 setLayout
之前,基本上不会呈现特定布局。然后,在 setLayout
中,您可以执行如下操作。
setLayout(layout: Layout): void {
if (layout){
this.setState({ layout: layout });
} else {
this.setState({
layout: {
columns: "100vw",
rows: "50px 50px",
areas: "
header
content
",
}
});
}
}
初始状态可以是
layout: {
columns: "",
rows: "",
areas: "
header
content
",
},
...
但是你 运行 会遇到其他样式问题(比如没有布局可能看起来很糟糕)。您可以通过将 NextStyle
组件的不透明度设置为 0
直到 this.state.layout.columns.length > 0
.
var opacity = this.state.layout.columns.length > 0 ? 1 : 0;
const __NextStyle = createGlobalStyle`
#__next {
display: grid;
opacity: ${opacity};
grid-template-columns: ${this.state.layout.columns};
grid-template-rows: ${this.state.layout.rows};
grid-template-areas: "${this.state.layout.areas}";
}
`;
这样你至少在调用 setLayout
之前不会显示任何内容。我不确定你是否更喜欢你描述的默认布局的小闪烁,但这可能是你最好的选择。