Search code examples
reactjsbuttonmaterial-uibackground-colorcolor-picker

Material UI this.state error for color background


My eyes hurt and I am attempting to not only allow a slider to change the radius of a button but also the color.

This sandbox succeeds at updating the radius.

Here is the code:
import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import Grid from "@material-ui/core/Grid";
import Slider from "@material-ui/core/Slider";
import Input from "@material-ui/core/Input";
import Button from "@material-ui/core/Button";
import reactCSS from "reactcss";
import { SketchPicker } from "react-color";

const useStyles = makeStyles((theme) => ({
  root: {
    "& > *": {
      margin: theme.spacing(1)
    }
  },
  Button: {
    width: 150,
    height: 50,
    borderRadius: "var(--borderRadius)"
  }
}));

export default function InputSlider() {
  const classes = useStyles();
  const [value, setValue] = React.useState(30);
  const handleSliderChange = (event, newValue) => {
    setValue(newValue);
  };
  const handleInputChange = (event) => {
    setValue(event.target.value === "" ? "" : Number(event.target.value));
  };
  const handleBlur = () => {
    if (value < 0) {
      setValue(0);
    } else if (value > 30) {
      setValue(30);
    }
  };
  return (
    <div className={classes.root}>
      <style>
        {`:root {
          --borderRadius = ${value}px;
        }`}
      </style>
      <Button
        style={{ borderRadius: value }}
        variant="contained"
        color="primary"
        value="value"
        onChange={handleSliderChange}
        className={classes.Button}
      >
        Fire laser
      </Button>
      <Grid container spacing={2}>
        <Grid item xs>
          <Slider
            value={typeof value === "number" ? value : 0}
            onChange={handleSliderChange}
            aria-labelledby="input-slider"
          />
        </Grid>
        <Grid item>
          <Input
            value={value}
            margin="dense"
            onChange={handleInputChange}
            onBlur={handleBlur}
            inputProps={{
              step: 10,
              min: 0,
              max: 24,
              type: "number"
            }}
          />
        </Grid>
      </Grid>
    </div>
  );
}

NOW I am working on using color picker to change the button's background color and eventually the font color as well.

I am receiving an error message for my this.state. Advice?

Here is a different sandbox that is in progress and has the error message - https://codesandbox.io/s/material-demo-forked-l35qy?file=/demo.js

Here is the code with the error message:

import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import Grid from "@material-ui/core/Grid";
import Slider from "@material-ui/core/Slider";
import Input from "@material-ui/core/Input";
import Button from "@material-ui/core/Button";
import reactCSS from "reactcss";
import { SketchPicker } from "react-color";

const useStyles = makeStyles((theme) => ({
  root: {
    "& > *": {
      margin: theme.spacing(1)
    }
  },
  Button: {
    width: 150,
    height: 50,
    borderRadius: "var(--borderRadius)",
    background: `rgba(${this.state.color.r}, ${this.state.color.g}, ${this.state.color.b}, ${this.state.color.a})`
  },
  color: {
    width: "36px",
    height: "14px",
    borderRadius: "2px",
    background: `rgba(${this.state.color.r}, ${this.state.color.g}, ${this.state.color.b}, ${this.state.color.a})`
  },
  swatch: {
    padding: "5px",
    background: "#fff",
    borderRadius: "1px",
    display: "inline-block",
    cursor: "pointer"
  },
  popover: {
    position: "absolute",
    zIndex: "2"
  },
  cover: {
    position: "fixed",
    top: "0px",
    right: "0px",
    bottom: "0px",
    left: "0px"
  }
}));

export default function InputSlider() {
  const classes = useStyles();
  const [value, setValue] = React.useState(30);
  const handleSliderChange = (event, newValue) => {
    setValue(newValue);
  };
  const handleInputChange = (event) => {
    setValue(event.target.value === "" ? "" : Number(event.target.value));
  };
  const handleBlur = () => {
    if (value < 0) {
      setValue(0);
    } else if (value > 30) {
      setValue(30);
    }
  };
  const handleClick = () => {
    this.setState({ displayColorPicker: !this.state.displayColorPicker });
  };

  const handleClose = () => {
    this.setState({ displayColorPicker: false });
  };

  const handleChange = (color) => {
    this.setState({ color: color.rgb });
  };
  return (
    <div className={classes.root}>
      <style>
        {`:root {
          --borderRadius = ${value}px;
        }`}
      </style>
      <Button
        style={{ borderRadius: value }}
        variant="contained"
        color="primary"
        value="value"
        onChange={handleSliderChange}
        className={classes.Button}
      >
        Fire laser
      </Button>
      <Grid container spacing={2}>
        <Grid item xs>
          <Slider
            value={typeof value === "number" ? value : 0}
            onChange={handleSliderChange}
            aria-labelledby="input-slider"
          />
        </Grid>
        <Grid item>
          <Input
            value={value}
            margin="dense"
            onChange={handleInputChange}
            onBlur={handleBlur}
            inputProps={{
              step: 10,
              min: 0,
              max: 24,
              type: "number"
            }}
          />
        </Grid>
      </Grid>
      <div>
        <Button style={useStyles.color}></Button>

        <div style={useStyles.swatch} onClick={this.handleClick}>
          <div style={useStyles.color} />
        </div>
        {this.state.displayColorPicker ? (
          <div style={useStyles.popover}>
            <div style={useStyles.cover} onClick={this.handleClose} />
            <SketchPicker
              color={this.state.color}
              onChange={this.handleChange}
            />
          </div>
        ) : null}
      </div>
    </div>
  );
}

Solution

  • You cant use state inside of useStyles because you only get access to the theme and not any props. You should use the makeStyles call instead as that will at least let you pass in some props. This has already been answered here and there is a good example.

    Passing props to material UI style

    Another issue is that you are using a functional based component where states are written differently. I have rewritten it using the useState hook for you so hopefully this will at least getting the color to use.

    export default function InputSlider() {
      const classes = useStyles();
      const [value, setValue] = React.useState(30);
      const [color, setColor] = React.useState({ background: "#fff" });
    
      const handleSliderChange = (event, newValue) => {
        setValue(newValue);
      };
      const handleInputChange = (event) => {
        setValue(event.target.value === "" ? "" : Number(event.target.value));
      };
      const handleBlur = () => {
        if (value < 0) {
          setValue(0);
        } else if (value > 30) {
          setValue(30);
        }
      };
      const handleClick = (color) => {
        setColor(color);
      };
    
      const handleClose = () => {
        setColor({ displayColorPicker: false });
      };
    
      const handleChange = (color) => {
        setColor(color);
      };
      return (
        <div className={classes.root}>
          <style>
            {`:root {
              --borderRadius = ${value}px;
            }`}
          </style>
          <Button
            style={{ borderRadius: value }}
            variant="contained"
            color="primary"
            value="value"
            onChange={handleSliderChange}
            className={classes.Button}
          >
            Fire laser
          </Button>
          <Grid container spacing={2}>
            <Grid item xs>
              <Slider
                value={typeof value === "number" ? value : 0}
                onChange={handleSliderChange}
                aria-labelledby="input-slider"
              />
            </Grid>
            <Grid item>
              <Input
                value={value}
                margin="dense"
                onChange={handleInputChange}
                onBlur={handleBlur}
                inputProps={{
                  step: 10,
                  min: 0,
                  max: 24,
                  type: "number"
                }}
              />
            </Grid>
          </Grid>
          <div>
            <Button style={useStyles.color}></Button>
    
            <div style={useStyles.swatch} onClick={handleClick}>
              <div style={useStyles.color} />
            </div>
            {color ? (
              <div style={useStyles.popover}>
                <div style={useStyles.cover} onClick={handleClose} />
                <SketchPicker color={color} onChange={handleChange} />
              </div>
            ) : null}
          </div>
        </div>
      );
    }