Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Each child in a list should have a unique "key" prop. #70

Open
1 task done
AmazingTurtle opened this issue Mar 9, 2023 · 4 comments
Open
1 task done

Each child in a list should have a unique "key" prop. #70

AmazingTurtle opened this issue Mar 9, 2023 · 4 comments
Labels
bug Something isn't working
Milestone

Comments

@AmazingTurtle
Copy link

Verify Next.js canary release

  • I verified that the issue exists in the latest Next.js canary release

Describe the bug

I see the Each child in a list should have a unique "key" prop. warning message on the client side on multiple occassions, basically every
I figured out, that is, because I'm feeding the data to the react context api.

    <DocumentListContextProvider
      documents={documents}
      data-superjson
    >
        <div />
        <SomeServerComponentSvg />
        <SomeClientComponentAccessingContext />
    </DocumentListContextProvider>

Which components are affected?
Apparently the warning appears for every node where there are more than 1 children. That goes for normal components as well as plain jsx.

A full error log:

hydration-error-info.js?7847:27 Warning: Each child in a list should have a unique "key" prop. See https://reactjs.org/link/warning-keys for more information.
    at div
    at DocumentListContextProvider (webpack-internal:///(app-client)/./app/(index)/documents/document-list-context.tsx:25:11)
    at WithSuperJSON (webpack-internal:///(app-client)/../../node_modules/next-superjson-plugin/dist/tools.js:153:55)
    at SuperJSONComponent (webpack-internal:///(app-client)/../../node_modules/next-superjson-plugin/dist/client.js:18:24)
    at ScrollAndFocusHandler (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/layout-router.js:153:1)
    at InnerLayoutRouter (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/layout-router.js:195:11)
    at RedirectErrorBoundary (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/layout-router.js:361:9)
    at RedirectBoundary (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/layout-router.js:368:11)
    at NotFoundBoundary (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/layout-router.js:404:11)
    at LoadingBoundary (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/layout-router.js:317:11)
    at ErrorBoundary (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/error-boundary.js:72:11)
    at RenderFromTemplateContext (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/render-from-template-context.js:12:34)
    at OuterLayoutRouter (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/layout-router.js:23:11)
    at div
    at ScrollAndFocusHandler (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/layout-router.js:153:1)
    at InnerLayoutRouter (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/layout-router.js:195:11)
    at RedirectErrorBoundary (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/layout-router.js:361:9)
    at RedirectBoundary (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/layout-router.js:368:11)
    at NotFoundBoundary (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/layout-router.js:404:11)
    at LoadingBoundary (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/layout-router.js:317:11)
    at ErrorBoundary (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/error-boundary.js:72:11)
    at RenderFromTemplateContext (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/render-from-template-context.js:12:34)
    at OuterLayoutRouter (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/layout-router.js:23:11)
    at div
    at div
    at ScrollAndFocusHandler (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/layout-router.js:153:1)
    at InnerLayoutRouter (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/layout-router.js:195:11)
    at RedirectErrorBoundary (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/layout-router.js:361:9)
    at RedirectBoundary (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/layout-router.js:368:11)
    at NotFoundErrorBoundary (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/layout-router.js:397:9)
    at NotFoundBoundary (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/layout-router.js:404:11)
    at LoadingBoundary (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/layout-router.js:317:11)
    at ErrorBoundary (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/error-boundary.js:72:11)
    at RenderFromTemplateContext (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/render-from-template-context.js:12:34)
    at OuterLayoutRouter (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/layout-router.js:23:11)
    at body
    at html
    at ReactDevOverlay (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/react-dev-overlay/internal/ReactDevOverlay.js:61:9)
    at HotReload (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/react-dev-overlay/hot-reloader-client.js:20:11)
    at Router (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/app-router.js:48:11)
    at ErrorBoundaryHandler (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/error-boundary.js:59:9)
    at ErrorBoundary (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/error-boundary.js:72:11)
    at AppRouter (webpack-internal:///(app-client)/../../node_modules/next/dist/client/components/app-router.js:24:13)
    at ServerRoot (webpack-internal:///(app-client)/../../node_modules/next/dist/client/app-index.js:147:11)
    at RSCComponent
    at Root (webpack-internal:///(app-client)/../../node_modules/next/dist/client/app-index.js:164:11)

I am using the 13.2.4-canary.8 version of next.

Expected behavior

I expect to see no warning about missing unique key props

Reproduction link

No response

Version

0.5.6

Config

module.exports = {
  reactStrictMode: true,
  experimental: {
    appDir: true,
    swcPlugins: [
      [ 'next-superjson-plugin', {} ]
    ]
  },
  async redirects() {
    return [
      {
        source: '/',
        destination: '/documents',
        permanent: true,
      },
    ]
  },
  transpilePackages: ['ui'],
};

Additional context

File
document-list-context.tsx

'use client';

import type { Dispatch, ReactNode } from 'react';
import { createContext, useReducer } from 'react';
import type { DocumentRevisionModel } from 'api-client';

export enum DocumentListContextActionKind {
  SetDocuments = 'setDocuments',
  AddDocument = 'addDocument',
}

export type DocumentListContextAction =
  | {
      type: DocumentListContextActionKind.SetDocuments;
      documents: Array<DocumentRevisionModel>;
    }
  | {
      type: DocumentListContextActionKind.AddDocument;
      document: DocumentRevisionModel;
    };

export interface DocumentListContextState {
  documents: Array<DocumentRevisionModel>;
}

export interface DocumentListContextInterface extends DocumentListContextState {
  documents: Array<DocumentRevisionModel>;
  dispatch: Dispatch<DocumentListContextAction>;
}

export const DocumentListContext = createContext<DocumentListContextInterface>({
  documents: [],
  dispatch: () => {
    throw new Error('DocumentListContext not initialized');
  },
});

interface DocumentListContextProviderProps {
  children: ReactNode;
  initialDocuments?: Array<DocumentRevisionModel>;
}

export function DocumentListContextProvider({
  children,
  initialDocuments = [],
}: DocumentListContextProviderProps) {
  const [state, dispatch] = useReducer(
    (state: DocumentListContextState, action: DocumentListContextAction) => {
      switch (action.type) {
        case DocumentListContextActionKind.SetDocuments:
          return {
            ...state,
            documents: action.documents,
          };
        case DocumentListContextActionKind.AddDocument:
          return {
            ...state,
            documents: [...state.documents, action.document],
          };
        default:
          return state;
      }
    },
    {
      documents: initialDocuments,
    },
  );

  return (
    <DocumentListContext.Provider
      value={{
        documents: state.documents,
        dispatch,
      }}
    >
      {children}
    </DocumentListContext.Provider>
  );
}

File
page.tsx

export default async function Page() {
  const apiConfiguration = createConfiguration({
    baseServer: new ServerConfiguration('http://localhost:3001', {}),
  });
  const documentsApi = new DocumentsApi(apiConfiguration);
  const documents = await documentsApi.documentsControllerList();

  return (
    <DocumentListContextProvider
      initialDocuments={documents.documents.map((document) => ({
        ...document,
      }))}
      data-superjson
    >
      {/* both will warn on this layer*/}
      <div />
      <div>
        <div>this div will not warn because there is only one child</div>
      </div>
    </DocumentListContextProvider>
  );
}
    ```
@AmazingTurtle AmazingTurtle added the bug Something isn't working label Mar 9, 2023
@orionmiz orionmiz added this to the Planned milestone Mar 12, 2023
@AmazingTurtle
Copy link
Author

Im on next 13.5.3 and next-superjson-plugin 0.5.9, bug is still alive.

@aplr
Copy link

aplr commented Feb 13, 2024

For me it's happening even with just 1 child. Notice, that ProductDetails is a server component, while ProductProvider (ofc) is not.

<ProductProvider value={state} data-superjson>
    <ProductDetails />
</ProductProvider>

observed on next@14.1.1-canary.49, next-superjson-plugin@0.6.2

@AmazingTurtle
Copy link
Author

A workaround that worked for me at the moment is wrapping the values passed in a "wrapper" using the class-transformer.

import { instanceToPlain } from 'class-transformer';

export function superjsonWrapper<T extends object>(obj: T): T {
  return instanceToPlain(obj) as T;
}

That seems to work for me!

@grmlin
Copy link

grmlin commented Mar 27, 2024

experience the exact same issue as described and have no idea how to fix it. For now, I don't use the plugin and simply ignore the warnings about dates passed around between server and client components. 🤷🏻

The solution proposed by @AmazingTurtle sadly didn't work for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants