Search code examples
reactjstypescriptnext.jstrpct3

Error when returning children in nextjs server component


"use client";

-- Some imports and interfaces

const SubscriptionDataFetcher: React.FC<SubscriptionDataFetcherProps> = ({ children }) => {
    const [data, setData] = useState<SubscriptionData>({});

    -- Some functions
    return <>{children(data)}</>;
};

export default SubscriptionDataFetcher;

i have this app that needs to call a trpc route, and i have decided to call it like that and return the children to a server component:

export const dynamic = 'force-dynamic';

--some interfaces

const Packages: React.FC = () => {
    <SubscriptionDataFetcher>
        {({ x, y }) => (

        )}
    </SubscriptionDataFetcher>
export default Packages;

I always get this error and have no idea what it means or how to handle correctly the children return:

Error: Functions are not valid as a child of Client Components. This may happen if you return children instead of <children /> from render. Or maybe you meant to call this function rather than return it.
  <... children={function children}>

Solution

  • In your SubscriptionDataFetcher component, you're expecting children to be a function that takes data as an argument. However, in your Packages component, you're trying to use this function as a child of SubscriptionDataFetcher, which is causing the error.

    To fix this, you can change SubscriptionDataFetcher to render its children directly, and pass data as a prop to the children. Here's how you can do it:

    const SubscriptionDataFetcher: React.FC<SubscriptionDataFetcherProps> = ({ children }) => {
      const [data, setData] = useState<SubscriptionData>({});
    
      // Some functions
    
      return <>{children}</>;
    };
    

    Then, in your Packages component, you can use data as a prop:

    const Packages: React.FC = () => {
      return (
        <SubscriptionDataFetcher>
          <ChildComponent data={data} />
        </SubscriptionDataFetcher>
      );
    };
    

    If you want to keep the function-as-child pattern, you can use the React.cloneElement function to clone the child element and pass data as a prop:

    const SubscriptionDataFetcher: React.FC<SubscriptionDataFetcherProps> = ({ children }) => {
      const [data, setData] = useState<SubscriptionData>({});
    
      // Some functions
    
      return <>{React.cloneElement(children, { data })}</>;
    };
    

    Then, in your Packages component, you can use data as a prop:

    const Packages: React.FC = () => {
      return (
        <SubscriptionDataFetcher>
          <ChildComponent />
        </SubscriptionDataFetcher>
      );
    };