material-ui icon indicating copy to clipboard operation
material-ui copied to clipboard

React "each child in a list should have a unique key" error when bundling MUI

Open lancej1022 opened this issue 3 years ago • 8 comments

Duplicates

  • [X] I have searched the existing issues

Latest version

  • [X] I have tested the latest version

Current behavior 😯

bundling mui in production mode generates key errors for seemingly no reason

Expected behavior 🤔

bundling mui in production mode does not generate key errors for components that arent actually part of a list

Steps to reproduce 🕹

Steps:

  1. To reproduce the error, clone this minimal reproduction to your local machine and then npm ci to load the dependencies.
  2. Next, run npm run build:prod to create a production bundle
  3. Use npm link to run the library against a fresh React app via Create-react-app or something similar.
  4. Within your fresh React application, just import the Tooltip and/or Checkbox components from this library while linked and then render them to the page. You will see the react errors.
  5. While linked, you can then run npm run build:dev to generate a development build. The Tooltip and Checkbox in you React app should no longer throw the unique key errors in the console.

Context 🔦

my team manages an internal component library that is based on mui and gets consumed by all other frontend developers at my job. This component library imports MUI components/helpers and makes any modifications needed, and then exports all of the modified stuff as well as all of the unmodified MUI functions (think export * from @mui/material basically).

What we've been doing is bundling mui into our component library at build time, that way consuming applications just install our component library, rather than having to install our component library + a bunch of MUI peer dependencies. However, we noticed that when we publish the production build of our component library, certain components such as the Tooltip and Checkbox will throw "each child in a list should have a unique key" warnings once they're imported and used in our applications.

Initially we thought this was an issue with our custom components or maybe our build chain, but I was able to recreate the problem by just making a brand new npm package, importing and then re-exporting all the MUI stuff and building the npm package in production mode. I then used npm link to run the package in a fresh Create React App and the Checkbox + Tooltip components immediately threw the key warnings. image image

Is this intended behavior and/or is this documented anywhere? Are we just supposed to build a development bundle instead? Ultimately our goal is to be able to bundle mui into our company's internal component library so that the component library maintainers can control the version of things and consumers can simply rely on importing our package

Your environment 🌎

npx @mui/envinfo

Unfortunately the npx @mui/envinfo command doesnt seem to be working for me?? I attempt to execute it and it just hangs on the CLI. I am using Node 14 on macOs monterrey

image

lancej1022 avatar Aug 31 '22 07:08 lancej1022

Any progress on this? Experiencing the same issue.

kinoli avatar Sep 21 '22 17:09 kinoli

We're also experiencing this issue.. has there been any progress?

mattburge avatar Sep 26 '22 09:09 mattburge

I am not able to reproduce your issue. Instead I am getting the following error in my React application after linking @company/components to my React app with npm link:

Uncaught TypeError: Cannot set properties of undefined (setting 'runtime')

Code:

import {Tooltip, Button} from '@company/components'

function App() {
  return (
    <Tooltip>
    <Button>
      abc
    </Button>
    </Tooltip>
  );
}

export default App;

ZeeshanTamboli avatar Nov 18 '22 12:11 ZeeshanTamboli

Im not totally sure what changed with CRA since initially opening this issue, but since CRA doesn't seem to offer any way to run an older version I just created a barebones react-app from scratch that can be used for npm link to work. The barebones react app can be cloned from https://github.com/lancej1022/bare-react

Ive also updated the MUI POC for the issue so that the webpack config can output to UMD rather than ESM if you just change some of the commented code (notes are within webpack.config.cjs) https://github.com/lancej1022/mui-poc

The repro steps for the issue should now look like:

  1. To reproduce the error, clone this minimal reproduction to your local machine and then npm ci to load the dependencies.
  2. Next, run npm run build:prod to create a production bundle
  3. Use npm link to run the library against the barebones react app
  4. Within your fresh React application, run npm start since the relevant components should already be imported within App.js. You will see the react errors about keys.
  5. While linked, you can then run npm run build:dev within the mui-poc repo to generate a development build. The Tooltip and Checkbox in your React app will no longer throw the unique key errors in the console.

lancej1022 avatar Nov 23 '22 19:11 lancej1022

@lancej1022 If there is no key error in the UMD development build but rather in the ESM bundle, is this an issue with Webpack?

ZeeshanTamboli avatar Nov 24 '22 13:11 ZeeshanTamboli

@lancej1022 If there is no key error in the UMD development build but rather in the ESM bundle, is this an issue with Webpack?

I think my previous message may have been confusing. To be clear, the key error is present in both UMD and ESM builds. I simply made it easy to switch between ESM/UMD within the reproduction so that whoever investigates this issue can see that the issue is not related to webpack's build target (as far as I can tell)

lancej1022 avatar Nov 26 '22 21:11 lancej1022

Just wanted to check in and see if the MUI team has any sort of recommendation for solving/avoiding this? I was under the impression that building a component library on top of MUI was pretty common but I haven't been able to find any documentation around proper bundling of MUI in the 4 months since this issue has been open. The only way I can think of to get around this issue would be to not bundle MUI at all and mark it as a peerDependency instead

lancej1022 avatar Dec 15 '22 17:12 lancej1022

@lancej1022 I can reproduce the error now. But unfortunately, I am not able to find a solution or workaround for this.

Could this be reated to https://github.com/facebook/react/issues/11537?

ZeeshanTamboli avatar Dec 16 '22 06:12 ZeeshanTamboli

@lancej1022 I can reproduce the error now. But unfortunately, I am not able to find a solution or workaround for this.

Could this be reated to facebook/react#11537?

Potentially? Im not overly familiar with babel transforms, but the babel config in the sample react app I provided is relatively simple so Im not sure if it is the issue. I was also thinking the issue might be with Emotion itself, but Im not familiar with how MUI + emotion work together internally.

lancej1022 avatar Dec 20 '22 21:12 lancej1022

The same warning appears using FocusTrap. I believe the issue originates at: image

Although it is not an ideal solution since a list is not being rendered, passing in a key eliminates the warning for all instances of the FocusTrap:

{

      {React.cloneElement(children, { ref: handleRef, onFocus, key:"some-key" })}
}

This could be the same issue with Tooltip which uses React clone element: image

React.cloneElement image

Sorry, I can't offer a solution, but I hope this is helpful.

Coreen-Cooper avatar Mar 04 '23 00:03 Coreen-Cooper

I am experiencing the same two warnings when using Tooltip and a nested FocusTrap within a custom components library based on MUI. I am hopeful that the team can find a solution to these warnings soon. I will continue to follow the discussion for any updates or resolutions.

dsanchezvalle avatar Apr 27 '23 22:04 dsanchezvalle

We have the same issue with Tooltip and FocusTrap.

aaronlademann-wf avatar Jun 21 '23 18:06 aaronlademann-wf

I'll take a look into this issue

DiegoAndai avatar Jul 07 '23 18:07 DiegoAndai

Thanks @lancej1022 for the useful reproduction and easy-to-follow steps, it was really helpful. Also thanks @Coreen-Cooper for finding out that cloneElement was the origin.

The issue can be solved by modifying the webpack config like this:

-const externals = {};

-Object.keys(pkg.peerDependencies).forEach((dep) => {
-  externals[dep] = dep;
-});

+const externals = new RegExp(
+  `^(?:${Object.keys(pkg.peerDependencies).join("|")})(?:/.*)?$`
+);

After this change, the externals config becomes /^(?:date-fns|react|react-dom)(?:\/.*)?$/. As you can see, the peer dependencies subpaths are also included in the externals, as they should. In particular react/jsx-runtime was the one that was missing, so it was being bundled even though it shouldn't.

You can check the difference this config change makes in this diff check: https://www.diffchecker.com/A143le9a/

I haven't dug deep enough to understand why this caused cloneElement to throw the error. The bottom line is React doesn't work properly when multiple versions of it or one of its dependencies are loaded simultaneusly.

As this is an issue various people stumbled upon, before closing it I will come up with some further actions to try to avoid it in the future and will post those here when I have them.

DiegoAndai avatar Jul 11 '23 19:07 DiegoAndai

Building a component library on top of Material UI seems to be a common user case, so to avoid configuration errors that might lead to frustration, I've opened the following RFC: https://github.com/mui/material-ui/issues/37952. The action point is adding a starting point and guidance for this user case.

I'm closing this issue with this action point and the solution for the original bug reported in my comment above.

If you're experiencing this issue with a different setup (maybe you're not using webpack like in this example), then the first thing you should check is that peer dependencies like react and its subpaths should not be bundled with your component library.

If you're still experiencing this and is not solved by the proposed solution, then please feel free to re-open this issue or create a new one.

DiegoAndai avatar Jul 13 '23 19:07 DiegoAndai

@DiegoAndai, Can you please reopen this issue? Or would it be better to create a new issue?


If anyone has a case where they encountered a key warning in Micro Frontend and solved it, I'd love to hear about it.

My environment was set up like this

And I've created a simple prototype that you can use to check this out, so you can see the problem.

Additional comments

  • Seeing that it happens for all components in material-ui that use focusTrap, I'm guessing that it's because focusTrap doesn't pass a key when using React.cloneElement in production bundling, as mentioned above(But I can't even figure out how to apply the above solution when bundling remoteApps in a vite-plugin-federation.🤣).
  • When I create a reproduce repo, it doesn't happen when I deploy the host app in production mode (e.g. vite preview, pm2 serve ...).

It would be great to find a solution, but I'd also like to know the cause, is there anyone who can help?

qsoo avatar May 07 '24 06:05 qsoo