vercel / next.js

The React Framework
https://nextjs.org
MIT License
125.76k stars 26.85k forks source link

unable to initialize state via props passed to children of layout #56650

Open jrnxf opened 1 year ago

jrnxf commented 1 year ago

Link to the code that reproduces this issue

https://github.com/jrnxf/angry-hornet

To Reproduce

Demo: https://angry-hornet-lawxc3oeh-jrnxf-team.vercel.app/ Detailed Video Explanation: https://www.loom.com/share/9292792f1c8c46f996d05198dc236256

  1. I enter my email in a form
  2. a server action sets the email in a cookie
  3. the submit handler redirects me to the root
  4. the cookie value is correct, and passed to an auth provider defined in my layout
  5. the value I attempt to initialize state with is correct, but the state is never initialized

Current vs. Expected behavior

Following the steps above, I would expect to see the values I'm seeing logged actually initialize the state variable. I believe this to be an issue with layouts, because I do not see this issue if I add the providers to the root page instead.

Verify canary release

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 22.6.0: Wed Jul  5 22:22:05 PDT 2023; root:xnu-8796.141.3~6/RELEASE_ARM64_T6000
Binaries:
  Node: 18.16.1
  npm: 9.7.2
  Yarn: 1.22.19
  pnpm: 7.9.3
Relevant Packages:
  next: 13.5.5-canary.4
  eslint-config-next: 13.5.4
  react: 18.2.0
  react-dom: 18.2.0
  typescript: 5.2.2
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

App Router, Routing (next/router, next/navigation, next/link)

Additional context

No response

jrnxf commented 1 year ago

Any chance I could get some feedback here as to whether or not this is indeed a bug?

jrnxf commented 12 months ago

This is becoming a major blocker – would love to get some help here if possible and have a discussion about what is happening, why, and determine if this is a bug.

subooom commented 10 months ago

Hi, I'm also facing this issue. What I'm trying to do this:

posts/layout.tsx

import React from "react";

async function getData() {
  try {
    const res = await fetch(
      "http://localhost:5555/api/posts"
    );

    if (!res.ok) {
      throw new Error(`Failed to fetch data. Status: ${res.status}`);
    }

    return res.json();
  } catch (error) {
    console.error(
      `Error during fetch http://localhost:5555/api/posts:`,
      error
    );
    throw error;
  }
}

export default async function Layout(props: React.PropsWithChildren) {
  const post: Post[] = await getData();

  const childrenWithProps = React.Children.map(
    props.children,
    (child): React.ReactElement => {
      if (React.isValidElement(child)) {
        // TypeScript might complain about the type, so you can explicitly cast
        return React.cloneElement(child, {
          exams,
        } as React.HTMLAttributes<HTMLElement>);
      }
      return <>{child}</>;
    }
  );
  console.log(childrenWithProps); // it has the posts prop

  return <>{childrenWithProps}</>;
}

posts/page.tsx

export default function Page(props: { posts: Post[] }) {
     // props.posts is undefined
}
pingustar commented 9 months ago

@subooom did you find a solution? I just came across the same issue with cloneElement not actually passing the props down

Hi, I'm also facing this issue. What I'm trying to do this:

posts/layout.tsx

import React from "react";

async function getData() {
  try {
    const res = await fetch(
      "http://localhost:5555/api/posts"
    );

    if (!res.ok) {
      throw new Error(`Failed to fetch data. Status: ${res.status}`);
    }

    return res.json();
  } catch (error) {
    console.error(
      `Error during fetch http://localhost:5555/api/posts:`,
      error
    );
    throw error;
  }
}

export default async function Layout(props: React.PropsWithChildren) {
  const post: Post[] = await getData();

  const childrenWithProps = React.Children.map(
    props.children,
    (child): React.ReactElement => {
      if (React.isValidElement(child)) {
        // TypeScript might complain about the type, so you can explicitly cast
        return React.cloneElement(child, {
          exams,
        } as React.HTMLAttributes<HTMLElement>);
      }
      return <>{child}</>;
    }
  );
  console.log(childrenWithProps); // it has the posts prop

  return <>{childrenWithProps}</>;
}

posts/page.tsx

export default function Page(props: { posts: Post[] }) {
     // props.posts is undefined
}