2
votes

I've been trying to understand and write code on the Box component in material-UI. (https://material-ui.com/components/box/#box)

I've been trying to override a Button component the two ways it describes in the documentation, but I have no idea how. When I run the code segment using both methods, the button appears but no color change. Then when I try to add an extra Button underneath the clone element code segment I get an error saying 'Cannot read property 'className' of undefined'.

            <Box color="primary" clone>
                <Button>Click</Button>
                <Button>Click</Button>
            </Box>

When I add a Button component underneath in the second render props way, the first button just disappears from the DOM completely.


             <Box color="secondary">
                {props => <Button {...props} > Click </Button>}
                <Button color="secondary">Click</Button>
            </Box> 

Would appreciate an explanation of how overriding underlying DOM elements work.

1
Does this answer your question? stackoverflow.com/a/60926017/11872246keikai
Unfortunately not. I do want to override styling, but only using either of the methods mentioned within the Box component documentation. Using clone element or render props.robert theboss

1 Answers

4
votes

There are a few issues with the code you've shown in your question.

  1. primary and secondary are not valid colors within the palette. They are valid options for the color prop of Button, but here you are trying to reference colors within the theme's palette object. For this purpose, you need primary.main and secondary.main (which is what Button uses when you specify <Button color="primary">).

  2. Box only supports a single child when using the clone property and it only supports a single child when using the render props approach. In both of your examples you have two children.


Here is the Material-UI source code that deals with the clone option:

      if (clone) {
        return React.cloneElement(children, {
          className: clsx(children.props.className, className),
          ...spread,
        });
      }

This is creating a new child element that combines the className generated by Box with any existing class name on the child. It gets at this existing class name via children.props.className, but when there are multiple children then children will be an array of elements and will not have a props property so you get the error:

Cannot read property 'className' of undefined


Here is the Material-UI source code that deals with the render props approach:

      if (typeof children === 'function') {
        return children({ className, ...spread });
      }

When you have more than one child, then typeof children === 'function' will not be true and it won't use the render props approach. In this case, both children just get normal react rendering and trying to render a function doesn't render anything.


Below is a working example that fixes all of these problems by using a single Button child in the clone case and a single function child in the render props case (a function that then renders two Button elements).

import React from "react";
import Button from "@material-ui/core/Button";
import Box from "@material-ui/core/Box";

export default function App() {
  return (
    <>
      <Box color="primary.main" clone>
        <Button>Click</Button>
      </Box>
      <Box color="secondary.main">
        {props => (
          <>
            <Button {...props}> Click </Button>
            <Button color="secondary">Click</Button>
          </>
        )}
      </Box>
    </>
  );
}

Edit Box clone and render props