I'm looking for a way to calculate state values which depend on other state values using React Hooks.
I'm familiarizing myself with React Hooks. So far I've been using useState and useEffect. I'm aware of other hooks e.g. useReduce, useCallback, etc.
I've made a simple rates calculator that converts rates on hourly, weekly, monthly, and yearly basis. When I change the respective rate other rates will be updated accordingly.
The code for the app as follows:
import React, { useState } from 'react';
import {
CssBaseline,
Container,
Typography,
TextField
} from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
const useStyles = makeStyles(theme => ({
paper: {
marginTop: theme.spacing(8), // = 4 * 2
display: 'flex',
flexDirection: 'column',
alignItems: 'center'
}
}));
function App() {
const classes = useStyles();
const [hourlyRate, setHourlyRate] = useState(10);
const [workHour, setWorkHour] = useState(40);
const [weeklyRate, setWeeklyRate] = useState(400);
const [monthlyRate, setMonthlyRate] = useState(1600);
const [yearlyRate, setYearlyRate] = useState(19200);
const tryConvert = ({ name, value }) => {
if (name === 'work-hours') {
setWorkHour(value);
setWeeklyRate(value * hourlyRate);
setMonthlyRate(value * hourlyRate * 4);
setYearlyRate(value * hourlyRate * 4 * 12);
} else if (name === 'hourly-rate') {
setHourlyRate(value);
setWeeklyRate(value * workHour);
setMonthlyRate(value * workHour * 4);
setYearlyRate(value * workHour * 4 * 12);
} else if (name === 'weekly-rate') {
setWeeklyRate(value);
setHourlyRate(value / workHour);
setMonthlyRate(value * 4);
setYearlyRate(value * 4 * 12);
} else if (name === 'monthly-rate') {
setMonthlyRate(value);
setWeeklyRate(value / 4);
setHourlyRate(value / workHour / 4);
setYearlyRate(value * 12);
} else if (name === 'yearly-rate') {
setYearlyRate(value);
setMonthlyRate(value / 12);
setWeeklyRate(value / 12 / 4);
setHourlyRate(value / 12 / 4 / workHour);
}
};
return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Typography component="h1" variant="h5">
Rate Kalkulator
</Typography>
<TextField
variant="outlined"
margin="normal"
id="hourly-rate"
label="Hourly Rate"
name="hourly-rate"
autoFocus
inputProps={{ 'data-testid': 'hourly-rate' }}
value={hourlyRate ? hourlyRate : 0}
onChange={event => tryConvert(event.target)}
/>
<TextField
variant="outlined"
margin="normal"
id="work-hours"
label="Work Hours"
name="work-hours"
inputProps={{ 'data-testid': 'work-hours' }}
value={workHour}
onChange={event => tryConvert(event.target)}
/>
<TextField
variant="outlined"
margin="normal"
id="weekly-rate"
label="Weekly Rate"
name="weekly-rate"
inputProps={{ 'data-testid': 'weekly-rate' }}
value={weeklyRate ? weeklyRate : 0}
onChange={event => tryConvert(event.target)}
/>
<TextField
variant="outlined"
margin="normal"
id="monthly-rate"
label="Monthly Rate"
name="monthly-rate"
inputProps={{ 'data-testid': 'monthly-rate' }}
value={monthlyRate ? monthlyRate : 0}
onChange={event => tryConvert(event.target)}
/>
<TextField
variant="outlined"
margin="normal"
id="yearly-rate"
label="Yearly Rate"
name="yearly-rate"
inputProps={{ 'data-testid': 'yearly-rate' }}
value={yearlyRate ? yearlyRate : 0}
onChange={event => tryConvert(event.target)}
/>
</div>
</Container>
);
}
export default App;
Codesandbox Link - tryConvert
The app worked as expected, as far as I've tested, haven't worked on the edge cases yet.
Simplifying Rate Conversion
I'm trying to simplify the conversion tryConvert with other React Hooks but haven't been successful. This is the code for the conversion from the codes above :
const tryConvert = ({ name, value }) => {
if (name === 'work-hours') {
setWorkHour(value);
setWeeklyRate(value * hourlyRate);
setMonthlyRate(value * hourlyRate * 4);
setYearlyRate(value * hourlyRate * 4 * 12);
} else if (name === 'hourly-rate') {
setHourlyRate(value);
setWeeklyRate(value * workHour);
setMonthlyRate(value * workHour * 4);
setYearlyRate(value * workHour * 4 * 12);
} else if (name === 'weekly-rate') {
setWeeklyRate(value);
setHourlyRate(value / workHour);
setMonthlyRate(value * 4);
setYearlyRate(value * 4 * 12);
} else if (name === 'monthly-rate') {
setMonthlyRate(value);
setWeeklyRate(value / 4);
setHourlyRate(value / workHour / 4);
setYearlyRate(value * 12);
} else if (name === 'yearly-rate') {
setYearlyRate(value);
setMonthlyRate(value / 12);
setWeeklyRate(value / 12 / 4);
setHourlyRate(value / 12 / 4 / workHour);
}
};
My attempt on using useEffect
What I've tried is using useEffect to add callbacks when the states are changed. The codes below illustrate what I've tried:
import React, { useState, useEffect } from 'react';
const [hourlyRate, setHourlyRate] = useState(10);
const [workHour, setWorkHour] = useState(40);
const [weeklyRate, setWeeklyRate] = useState(400);
const [monthlyRate, setMonthlyRate] = useState(1600);
const [yearlyRate, setYearlyRate] = useState(19200);
useEffect(() => {
setWeeklyRate(hourlyRate * workHour);
setMonthlyRate(hourlyRate * workHour * 4);
setYearlyRate(hourlyRate * workHour * 4 * 12);
}, [hourlyRate, workHour]);
useEffect(() => {
setHourlyRate(weeklyRate / workHour);
setMonthlyRate(weeklyRate * 4);
setYearlyRate(weeklyRate * 4 * 12);
}, [weeklyRate, workHour]);
useEffect(() => {
setHourlyRate(monthlyRate / 4 / workHour);
setWeeklyRate(monthlyRate / 4);
setYearlyRate(monthlyRate * 12);
}, [monthlyRate, workHour]);
useEffect(() => {
setHourlyRate(yearlyRate / 12 / 4 / workHour);
setWeeklyRate(yearlyRate / 12 / 4);
setMonthlyRate(yearlyRate / 12);
}, [yearlyRate, workHour]);
If I use this approach, when I change workHour the conversion would not work as expected. I think it is because the workHour has become dependency on the 4 different useEffect. I'm still not sure about this.
Codesandbox Link - useEffect
if you change the work hour the conversion won't work
Currently, I'm trying other approaches using useReduce, but I'm still not sure if this would be the best way to convert the rates.
What would be the best way to simplify my tryConvert?
I would appreciate any help on this.