简体   繁体   English

React 组件从类更新为函数/挂钩但 state 不工作

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

I attempted to refactor a React class component (fahrenheit/celcius temperature converter) to functional components with hooks (in NextJS) but these fields are not updating state as expected when numbers are added to either TemperatureInput field.我试图将 React class 组件(华氏/摄氏温度转换器)重构为带钩子的功能组件(在 NextJS 中),但是当数字添加到任一TemperatureInput字段时,这些字段没有按预期更新 state。 Nor is BoilingVerdict updating when triggered.触发时BoilingVerdict也不会更新。

I've tried many variations to the change handles but can't find what I'm missing.我已经尝试了多种更改手柄的变体,但找不到我遗漏的东西。

Could someone please point me on the right track?有人可以指出我在正确的轨道上吗?

Thanks in advance!提前致谢!

Original class component:原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 />);

Same component converted to functional components/hooks (but not working):相同的组件转换为功能组件/挂钩(但不工作):

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

The original is from the React Docs site example on Lifting State, pen here: https://codepen.io/gaearon/pen/WZpxpz?editors=0010原文来自Lifting State上的React Docs站点示例,笔在此: https://codepen.io/gaearon/pen/WZpxpz?editors=0010

Thanks again!再次感谢!

You had two little errors in your code:您的代码中有两个小错误:

  1. input onChange emits an event. input onChange 发出一个事件。 You can get the actual value that was put in by accessing e.target.value.您可以通过访问 e.target.value 获取输入的实际值。

Without this change, the temperature is and stays undefined, since handleChange in your handleTemperatureChange-Handlers in Temp are called without any parameters.如果没有此更改,温度将保持未定义状态,因为在没有任何参数的情况下调用 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. Your calls to tryConvert were incorrect.您对 tryConvert 的调用不正确。

    tryConvert(temperature, toCelsius()) tryConvert(温度,toCelsius())

In this case the second parameter is not a function but its return value.在这种情况下,第二个参数不是 function 而是它的返回值。 If you want to feed a function to tryConvert add only the function name:如果您想将 function 提供给 tryConvert,则仅添加 function 名称:

tryConvert(temperature, toCelsius)

Putting this together the code works fine把这些放在一起代码工作正常

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM