【问题标题】:How do I set the parent state before rendering?如何在渲染前设置父状态?
【发布时间】:2020-03-10 17:23:31
【问题描述】:

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

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

我已经在状态中设置了默认的网格 CSS 值(回退,以防子函数未调用)。我创建了一个类方法 (setLayout),它可以使用接收到的参数设置布局状态。我将这个函数的引用传递给每个带有 React Context 的子组件。

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

换句话说,在第一次渲染时,App 组件会使用回退值进行渲染,然后子组件会调用一个函数来渲染,该函数会更改 App 组件的状态,从而触发重新渲染。然后在第二次渲染时,App 组件以正确的布局渲染。
我真的希望 App 组件在第一次渲染时渲染子提供的布局(如果提供的话),而不是先渲染后备。请帮助我实现这一点。

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

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

【问题讨论】:

    标签: javascript reactjs typescript next.js styled-components


    【解决方案1】:

    发生的情况是,由于孩子是 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
               ",
            } 
         });
       }
      }
    

    初始状态可能是

    layout: {
        columns: "",
        rows: "",
        areas: "
          header 
          content
        ",
    },
    ...
    

    但随后您会遇到其他样式问题(例如没有布局可能看起来很糟糕)。您可以通过将NextStyle 组件的不透明度设置为0 直到this.state.layout.columns.length &gt; 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 之前不会显示任何内容。我不确定你是否更喜欢你描述的默认布局的小闪烁,但这可能是你最好的选择。

    【讨论】:

    • 非常感谢!我去试试看!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-11-29
    • 1970-01-01
    • 2021-07-20
    • 1970-01-01
    • 1970-01-01
    • 2021-03-03
    • 1970-01-01
    相关资源
    最近更新 更多