The only thing I found that was odd was the fact that you were declaring your useStyles hook inside your components, so new styling was being created each render cycle. Move these outside the components.
Example:
App.js
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1
},
paper: {
padding: theme.spacing(0),
textAlign: "center",
color: theme.palette.text.primary,
backgroundColor: theme.palette.background
}
}));
function App() {
const classes = useStyles();
const { state, dispatch } = React.useContext(MainContext);
const handleClick = useCallback(
(areaName, monsterName, newCount) => {
newCount = newCount > 10 ? 10 : newCount;
newCount = newCount < 0 ? 0 : newCount;
dispatch({
type: "UPDATE_COUNT",
area: areaName,
monster: monsterName,
count: newCount
});
},
[dispatch]
);
const areaObjects = Object.entries(state).map(([area, monster]) => {
const monsters = Object.entries(monster).map(([monsterName, count]) => (
<Grid item xs={12} key={monsterName}>
<Monster
area={area}
name={monsterName}
count={count}
handleClick={handleClick}
/>
</Grid>
));
return (
<Grid item xs={12} sm={6} md={4} lg={3} xl={2} key={area}>
<Paper className={classes.paper}>
<Grid item xs={12}>
<h3>{area}</h3>
</Grid>
{monsters}
</Paper>
</Grid>
);
});
return (
<div className={classes.root} id={"app-container"} key={"app-container"}>
<Grid container spacing={0}>
{areaObjects}
</Grid>
</div>
);
}
Monster.js
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1
},
addButtonContainer: {
backgroundColor: "lightblue"
},
subtractButtonContainer: {
backgroundColor: "lightyellow"
},
maxButtonContainer: {
backgroundColor: "lightgreen"
},
monsterName: {
textAlign: "center"
},
row: {
backgroundColor: "lightgray"
},
rowSuccess: {
backgroundColor: "lightgreen"
}
}));
export default function Monster({ area, name, count, handleClick }) {
const handleIncrement = (areaName, monsterName) => {
handleClick(areaName, monsterName, count + 1);
};
const handleDecrement = (areaName, monsterName) => {
handleClick(areaName, monsterName, count - 1);
};
const handleMaxout = (areaName, monsterName) => {
handleClick(areaName, monsterName, 10);
};
const classes = useStyles();
const rowClass = count === 10 ? classes.rowSuccess : classes.row;
return (
<Grid container item className={rowClass}>
<Grid item xs={7} className={classes.monsterName}>
{name}
</Grid>
<Grid item xs={1} className={classes.maxButtonContainer}>
<IconButton
color={"primary"}
variant={"contained"}
size={"small"}
onClick={() => handleMaxout(area, name)}
>
<ArrowUpward fontSize={"small"} />
</IconButton>
</Grid>
<Grid item xs={1} className={classes.addButtonContainer}>
<IconButton
color={"primary"}
variant={"contained"}
size={"small"}
onClick={() => handleIncrement(area, name)}
>
<Add fontSize={"small"} />
</IconButton>
</Grid>
<Grid item xs={2}>
{count}
</Grid>
<Grid item xs={1} className={classes.subtractButtonContainer}>
<IconButton
color={"secondary"}
variant={"contained"}
size={"small"}
onClick={() => handleDecrement(area, name)}
>
<Remove fontSize={"small"} />
</IconButton>
</Grid>
</Grid>
);
}
I also memoized the state being provided by the context, though I don't entirely think this part was necessary.
index.js
import React, { useMemo } from "react";
import initialState from "./initialState";
import reducer from "./reducer";
const MainContext = React.createContext();
function MainContextProvider(props) {
const [state, dispatch] = React.useReducer(reducer, initialState);
const value = useMemo(() => ({ state, dispatch }), [state]);
return (
<MainContext.Provider value={value}>{props.children}</MainContext.Provider>
);
}
const MainContextConsumer = MainContext.Consumer;
export { MainContext, MainContextProvider, MainContextConsumer };
