I'm trying to create a font size and font weight previewer using Material-Ui sliders and useState, but when I click the first slider after clicking the second, the first slider is duplicated.
Here is my code:
import React from "react";
import { withStyles, makeStyles } from "@material-ui/core/styles";
import Slider from "@material-ui/core/Slider";
import Typography from "@material-ui/core/Typography";
import Container from "@material-ui/core/Container";
import TextField from '@mui/material/TextField';
const useStyles = makeStyles((theme) => ({
root: {
width: 300 + theme.spacing(3) * 2
},
margin: {
height: theme.spacing(3)
}
}));
const iOSBoxShadow =
"0 3px 1px rgba(0,0,0,0.1),0 4px 8px rgba(0,0,0,0.13),0 0 0 1px rgba(0,0,0,0.02)";
const marks = [
{
value: 0
},
{
value: 25
},
{
value: 50
},
{
value: 75
},
{
value: 100
}
];
const IOSSlider = withStyles({
root: {
color: "#3880ff",
height: 2,
padding: "15px 0"
},
thumb: {
height: 28,
width: 28,
backgroundColor: "#fff",
boxShadow: iOSBoxShadow,
marginTop: -14,
marginLeft: -14,
"&:focus, &:hover, &$active": {
boxShadow:
"0 3px 1px rgba(0,0,0,0.1),0 4px 8px rgba(0,0,0,0.3),0 0 0 1px rgba(0,0,0,0.02)",
// Reset on touch devices, it doesn't add specificity
"@media (hover: none)": {
boxShadow: iOSBoxShadow
}
}
},
active: {},
valueLabel: {
left: "calc(-50% + 12px)",
top: -22,
"& *": {
background: "transparent",
color: "#000"
}
},
track: {
height: 2
},
rail: {
height: 2,
opacity: 0.5,
backgroundColor: "#bfbfbf"
},
mark: {
backgroundColor: "#bfbfbf",
height: 8,
width: 1,
marginTop: -3
},
markActive: {
opacity: 1,
backgroundColor: "currentColor"
}
})(Slider);
export default function CustomizedSlider() {
const classes = useStyles();
const [userFontSize, setUserFontSize] = React.useState("1rem");
const [userFontWeight, setUserFontWeight] = React.useState("300");
const [sliderSizeTracker, setSliderSizeTracker] = React.useState(0);
const [sliderWeightTracker, setSliderWeightTracker] = React.useState(999);
const handleSizeChange = (e, newSizeValue) => {
switch (newSizeValue) {
case 0: {
setUserFontSize("1rem");
setSliderSizeTracker(0);
break;
}
case 25: {
setUserFontSize("1.19rem");
setSliderSizeTracker(25);
break;
}
case 50: {
setUserFontSize("1.37rem");
setSliderSizeTracker(50);
break;
}
case 75: {
setUserFontSize("1.55rem");
setSliderSizeTracker(75);
break;
}
case 100: {
setUserFontSize("1.75rem");
setSliderSizeTracker(100);
break;
}
default: {
break;
}
}
};
const handleWeightChange = (e, newWeightValue) => {
switch (newWeightValue) {
case 0: {
setUserFontWeight("300");
setSliderWeightTracker(0);
break;
}
case 25: {
setUserFontWeight("400");
setSliderWeightTracker(25);
break;
}
case 50: {
setUserFontWeight("500");
setSliderWeightTracker(50);
break;
}
case 75: {
setUserFontWeight("700");
setSliderWeightTracker(75);
break;
}
case 100: {
setUserFontWeight("900");
setSliderWeightTracker(100);
break;
}
default: {
break;
}
}
};
return (
<Container maxWidth="sm" style={{ marginTop: "2em" }}>
<div className={classes.root}>
<Typography gutterBottom>Font Size Slider</Typography>
<IOSSlider
aria-label="Font Size Slider"
key={sliderSizeTracker}
defaultValue={sliderSizeTracker}
marks={marks}
step={25}
onChangeCommitted={handleSizeChange}
valueLabelDisplay="off"
/>
<Typography gutterBottom>Font Weight Slider</Typography>
<IOSSlider
aria-label="Font Weight Slider"
key={sliderWeightTracker}
defaultValue={sliderWeightTracker}
marks={marks}
step={25}
onChangeCommitted={handleWeightChange}
valueLabelDisplay="off"
/>
<div className={classes.margin} />
<TextField
sx={{ border: 'none', "& fieldset": { border: 'none' }, }}
margin="normal"
required
fullWidth
inputProps={{ style: { fontSize: `${userFontSize}`, fontWeight: `${userFontWeight}` } }}
defaultValue="The quick brown fox jumps over the lazy dog"
multiline
rows={2}
/>
</div>
</Container>
);
}
I think it has something to do with the way I'm using useState, but the functionality itself seems to work.
Removing the key
props on the IOSSlider
components appears to fix the issue in your sandbox.
<IOSSlider
aria-label="Font Size Slider"
// key={sliderSizeTracker} // <-- remove this
defaultValue={sliderSizeTracker}
marks={marks}
step={25}
onChangeCommitted={handleSizeChange}
valueLabelDisplay="off"
/>
<IOSSlider
aria-label="Font Weight Slider"
// key={sliderWeightTracker} // <-- remove this
defaultValue={sliderWeightTracker}
marks={marks}
step={25}
onChangeCommitted={handleWeightChange}
valueLabelDisplay="off"
/>
The other issue in your sandbox is that you are using React 18 but using the pre-React 18 ReactDOM pattern to render your app. It should be updated to the following:
index.js
import React from "react";
import { createRoot } from "react-dom/client";
import Demo from "./demo";
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(<Demo />);