【问题标题】:React Hooks: Is there a way to calculate state values based on another state valueReact Hooks:有没有办法根据另一个状态值计算状态值
【发布时间】:2019-06-20 03:48:25
【问题描述】:

我正在寻找一种使用 React Hooks 计算依赖于其他状态值的状态值的方法。

我正在熟悉 React Hooks。到目前为止,我一直在使用 useState 和 useEffect。我知道其他钩子,例如useReduce、useCallback 等。

我制作了一个简单的费率计算器,可以按小时、每周、每月和每年转换费率。当我更改相应的费率时,其他费率将相应更新。

应用代码如下:

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 链接 - tryConvert

Rate Kalkulator - tryConvert

据我测试,该应用程序按预期工作,但尚未在边缘情况下工作。

简化速率转换

我正在尝试使用其他 React Hooks 简化转换 tryConvert,但没有成功。这是上面代码转换的代码:


    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);
        }
    };

我尝试使用 useEffect

我尝试使用 useEffect 在状态更改时添加回调。下面的代码说明了我的尝试:

    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]);

如果我使用这种方法,当我更改 workHour 时,转换将无法按预期进行。我认为这是因为workHour 已经依赖于4 个不同的useEffect。我仍然不确定。

Codesandbox 链接 - useEffect

Rate Kalkulator - useEffect

如果您更改工作时间,转换将不起作用


目前,我正在尝试使用 useReduce 的其他方法,但我仍然不确定这是否是转换费率的最佳方法。

简化 tryConvert 的最佳方法是什么?

我将不胜感激。

【问题讨论】:

  • 我不确定我是否理解问题所在?看看你的沙盒,转换似乎按预期工作。你只是想集成 useEffect 吗?
  • @ChristopherNgo 当前转换工作使用tryConvert 函数。我正在尝试改变如何使用其他 React Hooks,例如useEffect。但是,当我为每个状态进行useEffect 回调时,转换在workHour 上不起作用。我会尝试建立一个代码框来展示这一点。

标签: reactjs react-hooks


【解决方案1】:

这里真的不需要useEffectuseEffect 用于副作用(例如,setIntervaldocument.title,不属于渲染周期的东西)。

您的第一个解决方案运行良好,但您可以通过为费率提供单一真实来源并根据该来源计算其他费率来简化它。像这样的:

function App() {
  const classes = useStyles();
  const [hourlyRate, setHourlyRate] = useState(10);
  const [workHour, setWorkHour] = useState(40);

  const convertTo = name => {
    const map = {
      weekly: hourlyRate * workHour,
      monthly: hourlyRate * workHour * 4,
      yearly: hourlyRate * workHour * 52
    };
    return map[name] || 0;
  };

  const convertFrom = name => e => {
    const { value } = e.target;
    const map = {
      weekly: value / workHour,
      monthly: value / workHour / 4,
      yearly: value / workHour / 52
    };
    setHourlyRate(map[name]);
  };

  return (
      <div className={classes.paper}>
        <Typography component="h1" variant="h5">
          Rate Kalkulator
        </Typography>
        <TextField
          label="Hourly Rate"
          value={hourlyRate || 0}
          onChange={e => setHourlyRate(e.target.value)}
        />
        <TextField
          label="Work Hours"
          value={workHour}
          onChange={e => setWorkHour(e.target.value)}
        />
        <TextField
          label="Weekly Rate"
          value={convertTo("weekly") || 0}
          onChange={convertFrom("weekly")}
        />
        <TextField
          label="Monthly Rate"
          value={convertTo("monthly") || 0}
          onChange={convertFrom("monthly")}
        />
        <TextField
          label="Yearly Rate"
          value={convertTo("yearly") || 0}
          onChange={convertFrom("yearly")}
        />
      </div>
  );
}

对于一个工作示例,这是一个代码框:https://codesandbox.io/s/rate-kalkulator-ej5m8

【讨论】:

  • 太棒了!谢谢你!我正在考虑将所有费率纳入州。但将一个比率作为单一事实来源是有道理的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-27
  • 2020-06-26
相关资源
最近更新 更多