繁体   English   中英

如何在渲染前设置父 state?

[英]How do I set the parent state before rendering?

我知道这是一个很长的介绍,但我真的很难在这里说清楚

在我正在处理的这个Next.js应用程序中,我需要在具有特定iddiv上设置 CSS 网格布局。 我用 styled-components createGlobalStyle做到这一点。 我希望此 App 组件的每个子组件都能够更改此网格布局(可选地传入替代 CSS 值)。

我在 state 中设置了默认网格 CSS 值(后备,以防 function 未被孩子调用)。 我创建了一个 class 方法 ( s setLayout ),它可以使用接收到的参数设置布局 state。 我将这个 function 的引用传递给每个具有 React 上下文的子组件。

在我的子组件中,我现在可以导入此上下文并使用替代布局调用 function。 有用。 但是......当我首先刷新页面时,后备布局会在子组件布局应用于根 div 之前闪烁一秒钟。

换句话说,在第一次渲染时,App 组件使用回退值渲染,然后子组件通过调用 function 来渲染,这会更改 App 组件的 state,从而触发重新渲染。 然后在第二次渲染时,App 组件以正确的布局渲染。
我真的希望 App 组件在第一次渲染时渲染子提供的布局(如果已提供),而无需先渲染回退。 请帮助我实现这一目标。

PS我正在使用服务器端渲染,虽然我不认为这有任何影响

// 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);
  }, []);

发生的事情是,由于孩子是App App它之后它才会变热。 到那时App已经呈现并使用默认布局。

  1. App呈现(默认)
  2. 部分App渲染触发Child的渲染
  3. Child中,执行setLayout
  4. App布局已更新。

不幸的是,您想要实现的目标(至少是您想要实现的方式)无法完成。 您可以做的是在调用setLayout之前没有默认布局。 在子项中调用setLayout之前,基本上不会呈现特定的布局。 然后,在setLayout中,您可以执行以下操作。

  setLayout(layout: Layout): void {
   if (layout){
     this.setState({ layout: layout });
   } else {
     this.setState({ 
        layout: {
           columns: "100vw",
           rows: "50px 50px",
           areas: "
             header 
             content
           ",
        } 
     });
   }
  }

初始 state 可能是

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之前,您至少不会显示任何内容。 我不确定你是否更喜欢你描述的默认布局的小闪烁,但这可能是你最好的选择。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM