0

When I try to learn the functional component of react, I'm try to change the class component to the functional component. But my code is not right, I need so help.

I change code from the below project. (https://github.com/ahfarmer/calculator/blob/37b56077e78b82bf2088ec993d55becb47538de9/src/component/App.js)

https://github.com/ahfarmer/calculator

import { useState, Component } from "react";
import { Display } from "./Display";
import { ButtonPanel } from "./ButtonPanel";
import calculate from "../logic/calculate";
import "./App.css";

function App2() {

  const [state, setState] = useState({
    total: null,
    next: null,
    operation: null,
  });
  
  const handleClick = (buttonName) => {
    let newValue = calculate({total: state.total, next: state.next, operation: state.operation}, buttonName);
    console.log(newValue);
    setState(newValue);
  };

  return (
    <div className="component-app">
      <Display value={state.next || state.total || "0"} />
      <ButtonPanel clickHandler={handleClick} />
    </div>
  );
}

class App extends Component {
  state = {
    total: null,
    next: null,
    operation: null,
  };

  handleClick = (buttonName) => {
    let newValue = calculate(this.state, buttonName);
    console.log(newValue);
    this.setState(newValue);
  };

  render() {
    return (
      <div className="component-app">
        <Display value={this.state.next || this.state.total || "0"} />
        <ButtonPanel clickHandler={this.handleClick} />
      </div>
    );
  }
}
// App is fine
export default App;

// App is not fine

// export default App2;

same step, but result is different. App 2 is {}

App2 Log

tap 1

{
    "next": "1",
    "total": null
}

tap +

{
    "total": "1",
    "next": null,
    "operation": "+"
}

tap 2

{
    "next": "2"
}

tap =

{}

App Log

tap 1

{
    "next": "1",
    "total": null
}

tap +

{
    "total": "1",
    "next": null,
    "operation": "+"
}

tap 2

{
    "next": "2"
}

tap =

{
    "total": "3",
    "next": null,
    "operation": null
}
2
  • What happens if you run the code, what is the problem? For me, you App2 component looks fine and should work Commented Jun 18, 2022 at 11:19
  • Thank you, frank. I add some logs. When I tap =, the result is diff. Do you know why? I think the problem might be the setState() in App2. I don't know how to fix. Commented Jun 18, 2022 at 13:20

1 Answer 1

1

The problem is a bit tricky: the two versions of App behave differently because setState works differently in functional and in class components.

Here is the fixed code for the functional component:


function App2() {
  const [state, setState] = useState({
    total: null,
    next: null,
    operation: null
  });

  const handleClick = (buttonName) => {
    let newValue = calculate(state, buttonName);
    console.log(newValue);
    setState((oldValue) => ({ ...oldValue, ...newValue }));
  };

  return (
    <div className="component-app">
      <Display value={state.next || state.total || "0"} />
      <ButtonPanel clickHandler={handleClick} />
    </div>
  );
}

The only line that is different from your version is this one:

// setState(newValue); -> old code
setState((oldValue) => ({ ...oldValue, ...newValue }));

Explanation:

In the class-based component, this.setState will update only one property of the object and leave all other properties unchanged. If {a: 1, b:2} is your state and you call this.setState({b: 3}), the new state will be {a: 1, b: 3}.

In a functional component, setState will replace the whole object: If {a: 1, b:2} is your state and you call setState({b: 3}), the new state will be {b: 3}.

So if you want to just update one property, you have you copy all properties that you want to keep from the old state to the new state. Because the old code did not do this, the total property got deleted from the state when the second number key was pressed, causing the wrong calculation result.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.