mui / material-ui

Material UI: Comprehensive React component library that implements Google's Material Design. Free forever.
https://mui.com/material-ui/
MIT License
93.92k stars 32.27k forks source link

Stepper component's svg is not displayed when used as nested component #44262

Closed s1n7ax closed 6 days ago

s1n7ax commented 1 week ago

Steps to reproduce

Link to live example: https://github.com/s1n7ax/poc-react-mobx-complex-form/blob/976f20590d597d45ff9bbcd20cca521060cb0d38/src/components/Form.tsx#L1

Steps:

  1. Create a new NextJS 15 template using npx create-next-app@latest
  2. Add MUI libraries and NextJS app router config for caching
  3. Create two Stepper component as follows
  4. When 'use client' is removed, the icons are displayed correctly for both Steppers but once the 'use client' is added, entire svg disappears from the DOM.
'use client'

import { Step, StepLabel, Stepper } from "@mui/material";

const steps = ["form one", "form two", "form three"];

const Form = () => {
  return (
    <div>
      <Stepper activeStep={1} alternativeLabel>
        {steps.map((label) => (
          <Step key={label}>
            <StepLabel>{label}</StepLabel>
          </Step>
        ))}
      </Stepper>
      <Items />
    </div>
  );
};

export default Form;

const Items = () => {
  return (
    <Stepper activeStep={1} alternativeLabel>
      {steps.map((label) => (
        <Item key={label} label={label} />
      ))}
    </Stepper>
  );
};

const Item = ({ label }: { label: string }) => {
  return (
    <Step key={label}>
      <StepLabel>{label}</StepLabel>
    </Step>
  );
};

Current behavior

While the First stepper shows the Icons correctly, second does not

screenshot 24-10-30 08:04:32

Expected behavior

Expected to show icons correctly as follows

screenshot 24-10-30 08:06:46

Context

No response

Your environment

npx @mui/envinfo ``` ❯ npx @mui/envinfo Need to install the following packages: @mui/envinfo@2.0.28 Ok to proceed? (y) System: OS: Linux 6.6 NixOS 24.11 (Vicuna) 24.11 (Vicuna) Binaries: Node: 22.9.0 - /etc/profiles/per-user/s1n7ax/bin/node npm: 10.8.3 - /etc/profiles/per-user/s1n7ax/bin/npm pnpm: 9.12.2 - /etc/profiles/per-user/s1n7ax/bin/pnpm Browsers: Firefox: 131.0.3 (64-bit) ```

Search keywords: Stepper Icon

DiegoAndai commented 1 week ago

Hey @s1n7ax, thanks for the report!

I wasn't able to repro with the provided repo and a simple stepper demo:

https://github.com/user-attachments/assets/05601f31-b8a8-471b-988b-bd17e5e67660

Could you provide a new repo with a minimal reproduction? It should have a simpler demo, also please indicate where to add/remove "use client" to repro. This would help a lot.

s1n7ax commented 1 week ago

@DiegoAndai Hi, Here is a minimal repo

https://github.com/s1n7ax/temp-issue-mui-stepper

DiegoAndai commented 1 week ago

Thanks for the repro! It was really helpful. Here's what I found:

Fix for the issue

The expected behavior is that this code:

const Items = () => {
  return (
    <Stepper activeStep={1} alternativeLabel>
      {steps.map((label) => (
        <Item key={label} label={label} />
      ))}
    </Stepper>
  );
};

const Item = ({ label }: { label: string }) => {
  return (
    <Step key={label}>
      <StepLabel>{label}</StepLabel>
    </Step>
  );
};

Should not work. This is because Stepper relies on providing props to Step via cloneElement, here's the reference:

https://github.com/mui/material-ui/blob/87a87824c8bd61bd62a4579bf412c7d682ae9607/packages/mui-material/src/Stepper/Stepper.js#L86-L92

So, if we introduce a wrapper (Item in this case), the props won't get to Step unless we forward them. Forwarding the props fixes the issue:

-const Item = ({ label }: { label: string }) => {
+const Item = ({ label, ...rest }: { label: string }) => {
   return (
-    <Step key={label}>
+    <Step key={label} {...rest}>
       <StepLabel>{label}</StepLabel>
     </Step>
   );
 };

@s1n7ax please let me know if this fixes your issue.

Explanation of why it seems to work without "use client"

When the Form.tsx file (from the provided repo) doesn't have the "use client" directive, the Item component is not present in the client components tree, you can check this with the React Components dev tool:

With "use client"

Screenshot 2024-11-05 at 16 43 55

Without "use client"

Screenshot 2024-11-05 at 16 43 29

Because Item is "removed" from the client tree, cloneElement works.

Does that make sense?

s1n7ax commented 6 days ago

Provided solution worked. Thanks!

github-actions[bot] commented 6 days ago

This issue has been closed. If you have a similar problem but not exactly the same, please open a new issue. Now, if you have additional information related to this issue or things that could help future readers, feel free to leave a comment.

[!NOTE] @s1n7ax How did we do? Your experience with our support team matters to us. If you have a moment, please share your thoughts in this short Support Satisfaction survey.