React 组件从 类 更新到 functions/hooks 但状态不工作

React component updated from classes to functions/hooks but state not working

我试图将 React class 组件(fahrenheit/celcius 温度转换器)重构为带钩子的功能组件(在 NextJS 中),但是当数字添加到其中一个时,这些字段没有按预期更新状态TemperatureInput 字段。触发时 BoilingVerdict 也不会更新。

我尝试了多种更改手柄的变体,但找不到我缺少的东西。

有人能指出我正确的方向吗?

提前致谢!

原始 class 组件:

const scaleNames = {
  c: 'Celsius',
  f: 'Fahrenheit'
};

function toCelsius(fahrenheit) {
  return (fahrenheit - 32) * 5 / 9;
}

function toFahrenheit(celsius) {
  return (celsius * 9 / 5) + 32;
}

function tryConvert(temperature, convert) {
  const input = parseFloat(temperature);
  if (Number.isNaN(input)) {
    return '';
  }
  const output = convert(input);
  const rounded = Math.round(output * 1000) / 1000;
  return rounded.toString();
}

function BoilingVerdict(props) {
  if (props.celsius >= 100) {
    return <p>The water would boil.</p>;
  }
  return <p>The water would not boil.</p>;
}

class TemperatureInput extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(e) {
    this.props.onTemperatureChange(e.target.value);
  }

  render() {
    const temperature = this.props.temperature;
    const scale = this.props.scale;
    return (
      <fieldset>
        <legend>Enter temperature in {scaleNames[scale]}:</legend>
        <input value={temperature}
               onChange={this.handleChange} />
      </fieldset>
    );
  }
}

class Calculator extends React.Component {
  constructor(props) {
    super(props);
    this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
    this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
    this.state = {temperature: '', scale: 'c'};
  }

  handleCelsiusChange(temperature) {
    this.setState({scale: 'c', temperature});
  }

  handleFahrenheitChange(temperature) {
    this.setState({scale: 'f', temperature});
  }

  render() {
    const scale = this.state.scale;
    const temperature = this.state.temperature;
    const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
    const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;

    return (
      <div>
        <TemperatureInput
          scale="c"
          temperature={celsius}
          onTemperatureChange={this.handleCelsiusChange} />
        <TemperatureInput
          scale="f"
          temperature={fahrenheit}
          onTemperatureChange={this.handleFahrenheitChange} />
        <BoilingVerdict
          celsius={parseFloat(celsius)} />
      </div>
    );
  }
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Calculator />);

相同的组件转换为功能components/hooks(但不工作):

import React, { useState } from "react";

const scaleNames = {
    c: "Celsius",
    f: "Fahrenheit",
};

const toCelsius = (fahrenheit) => {
    return ((fahrenheit - 32) * 5) / 9;
};

const toFahrenheit = (celsius) => {
    return (celsius * 9) / 5 + 32;
};

const tryConvert = (temperature, convert) => {
    const input = parseFloat(temperature);
    if (Number.isNaN(input)) {
        return "";
    }
    const output = convert(input);
    const rounded = Math.round(output * 1000) / 1000;
    return rounded.toString();
};

const BoilingVerdict = (props) => {
    if (props.celsius >= 100) {
        return <p>The water would boil.</p>;
    }
    return <p>The water would not boil.</p>;
};

const TemperatureInput = (props) => {
    const handleChange = () => {
        props.onTemperatureChange();
    };

    const temperature = props.temperature;
    const scale = props.scale;
    return (
        <fieldset>
            <legend>Enter temperature in {scaleNames[scale]}:</legend>
            <input value={temperature} onChange={handleChange} />
        </fieldset>
    );
};

export default function Temp(props) {
    const [state, setState] = useState({ temperature: "", scale: "c" });

    const handleCelsiusChange = (temperature) => {
        setState({ scale: "c", temperature });
    };

    const handleFahrenheitChange = (temperature) => {
        setState({ scale: "f", temperature });
    }

    const scale = state.scale;
    const temperature = state.temperature;
    const celsius =
        scale === "f"
            ? tryConvert(temperature, toCelsius())
            : temperature;
    const fahrenheit =
        scale === "c"
            ? tryConvert(temperature, toFahrenheit())
            : temperature;

    return (
        <div>
            <TemperatureInput
                scale="c"
                temperature={celsius}
                onTemperatureChange={handleCelsiusChange}
            />
            <TemperatureInput
                scale="f"
                temperature={fahrenheit}
                onTemperatureChange={handleFahrenheitChange}
            />
            <BoilingVerdict celsius={parseFloat(celsius)} />
        </div>
    );
}

原文来自React Docs站点Lifting State示例,笔在此:https://codepen.io/gaearon/pen/WZpxpz?editors=0010

再次感谢!

您的代码中有两个小错误:

  1. input onChange 发出一个事件。您可以通过访问 e.target.value.
  2. 获取实际输入的值

如果没有此更改,温度将保持未定义状态,因为调用 Temp 中的 handleTemperatureChange-Handlers 中的 handleChange 时没有任何参数。

const TemperatureInput = (props) => {
  const handleChange = (e) => {
    props.onTemperatureChange(e.target.value);
  };

  const temperature = props.temperature;
  const scale = props.scale;
  return (
    <fieldset>
      <legend>Enter temperature in {scaleNames[scale]}:</legend>
      <input value={temperature} onChange={(e) => handleChange(e)} />
    </fieldset>
  );
};
  1. 您对 tryConvert 的调用不正确。

    tryConvert(温度, toCelsius())

在这种情况下,第二个参数不是函数而是它的 return 值。 如果要将函数提供给 tryConvert,只需添加函数名称:

tryConvert(temperature, toCelsius)

将这些代码放在一起工作正常

import React, { useState } from "react";

const scaleNames = {
  c: "Celsius",
  f: "Fahrenheit"
};

const toCelsius = (fahrenheit) => {
  return ((fahrenheit - 32) * 5) / 9;
};

const toFahrenheit = (celsius) => {
  return (celsius * 9) / 5 + 32;
};

const tryConvert = (temperature, convert) => {
  const input = parseFloat(temperature);
  if (Number.isNaN(input)) {
    return "";
  }
  const output = convert(input);
  const rounded = Math.round(output * 1000) / 1000;
  return rounded.toString();
};

const BoilingVerdict = (props) => {
  if (props.celsius >= 100) {
    return <p>The water would boil.</p>;
  }
  return <p>The water would not boil.</p>;
};

const TemperatureInput = (props) => {
  const handleChange = (e) => {
    props.onTemperatureChange(e.target.value);
  };

  const temperature = props.temperature;
  const scale = props.scale;
  return (
    <fieldset>
      <legend>Enter temperature in {scaleNames[scale]}:</legend>
      <input value={temperature} onChange={(e) => handleChange(e)} />
    </fieldset>
  );
};

export default function Temp(props) {
  const [state, setState] = useState({ temperature: "", scale: "c" });

  const handleCelsiusChange = (temperature) => {
    console.log(temperature)
      setState({ scale: "c", temperature });
  };

  const handleFahrenheitChange = (temperature) => {
      setState({ scale: "f", temperature });
  }

  const scale = state.scale;
  const temperature = state.temperature;
  
  const celsius =
      scale === "f"
          ? tryConvert(temperature, toCelsius())
          : temperature;
  const fahrenheit =
      scale === "c"
          ? tryConvert(temperature, toFahrenheit)
          : temperature;

  return (
      <div>
          <TemperatureInput
              scale="c"
              temperature={celsius}
              onTemperatureChange={handleCelsiusChange}
          />
          <TemperatureInput
              scale="f"
              temperature={fahrenheit}
              onTemperatureChange={handleFahrenheitChange}
          />
          <BoilingVerdict celsius={parseFloat(celsius)} />
      </div>
  );
}