user6518178
user6518178

Reputation:

React - Uncaught ReferenceError: add is not defined

So I'm pretty new to React (and JS) in general. Normally code with Java or C# and I'm not used to the syntax just yet. I did manage to make a simple calculator with React and wanna add the functionality but whenever I try, I get an error that says that my method is not defined.

Basically the function is used to get the prop from my Button component and append it into the Screen component value.

class Button extends React.Component {
    constructor(props) {
        super(props)
    }

    add(button) {
        let value = button.value
        let text = document.getElementByID('screenInput').value
        if (value == '=') {
            document.getElementById('screenInput').innerHTML = eval(text).toString();
        } else if (value == 'C') {
            document.getElementById('screenInput').innerHTML = '0':
        } else if (value == '+/-' && text != '0') {
            document.getElementById('screenInput').innerHTML = `` - (${ text })``;
        } else if (value == '\d' || (text.slice(-1) != value && text != '0')) {
            document.getElementById('screenInput').innerHTML = text.append(value);
        }
    }

    render() {
        return (
            <button type='button' value={this.props.value} onClick={() => add(this)}> {this.props.value}</button >
        )
    }
}

https://jsfiddle.net/q7yakt1v/22/`

Upvotes: 0

Views: 1213

Answers (4)

Cameron Downer
Cameron Downer

Reputation: 2048

As pointed out in a couple of the other answers, the key issue with your code is onClick={() => add(this)}. But I think this is because of a misunderstanding of how state is handled in React.

I have lifted the state of the value of screen into the <Calculator /> component. This way we pass the state into the <Screen /> component to display it and can manipulate it from the <Buttons /> component without calling the DOM directly (as @Avanthika pointed out in their answer).

class Calculator extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            value: '0'
        }

        this.handleButtonClick = this.handleButtonClick.bind(this)
    }

    handleButtonClick(value) {
        const { value: currentValue } = this.state;
        let newValue;

        if (value == '=') {
            newValue = eval(currentValue).toString();
        } else if (value == 'C') {
            newValue = 0;
        } else if (value == '+/-' && currentValue != '0') {
            newValue = `-(${text})`;
        } else if (currentValue === '0') {
            newValue = value;
        } else {
            newValue = currentValue.concat(value);
        }

        this.setState({ value: newValue });
    }

    render() {
        const { value } = this.state;
        return (
            <div id="body">
                <Screen value={value} />
                <Buttons onButtonClick={this.handleButtonClick} />
            </div>
        )
    }
}

....

const Button = ({ value, onClick }) => (
    <button type="button" onClick={() => onClick(value)}>
        {value}
    </button>
)

I have renamed your function add and changed its behaviour. Instead of getting/updating the values to DOM, it uses the state in the <Calculator /> component.

You can see the working code here: https://jsfiddle.net/zvxpkq46/

Upvotes: 0

Tom Finney
Tom Finney

Reputation: 2928

So the reason you can't just refer to add like that is it intrinsically bound to the instance of your class Button. So if you want to call it, you'd have to do this.add(this).

The problem you will run into next is that the this you are passing to add isn't the button element, but a reference to the class instance.

I guess I should also add that you don't want to be modifying the DOM directly outside of React like that because React internally keeps track of changes. You should prefer using state/context to apply changes to things outside the immediate scope your component.

Upvotes: 1

Avanthika
Avanthika

Reputation: 4182

  1. Constructor doesn't have any initial state set, we don't need it.
  2. Auto bind your function.
add = (button) => {
   let value = this.buttonRef.value;
   // rest of your code
}
  1. Change the render this way:
render() {
    return (
      <button ref={(button) => { this.buttonRef = button; }} type="button" value={this.props.value} onClick={() => this.add()}>
        {this.props.value}
      </button>
    );
  }
  1. I also suggest avoiding referencing document directly with document.getElementById in react. Instead manage a state variable.

Upvotes: 0

AlexOwl
AlexOwl

Reputation: 867

class Button extends React.Component {
  constructor(props) {
    super(props);
  }

  add() {
    let value = this.value;
    let text = document.getElementByID("screenInput").value;
    if (value == "=") {
      document.getElementById("screenInput").innerHTML = eval(text).toString();
    } else if (value == "C") {
      document.getElementById("screenInput").innerHTML = "0";
    } else if (value == "+/-" && text != "0") {
      document.getElementById("screenInput").innerHTML = `-(${text})`;
    } else if (value == "d" || (text.slice(-1) != value && text != "0")) {
      document.getElementById("screenInput").innerHTML = text.append(value);
    }
  }

  render() {
    return (
      <button type="button" value={this.props.value} onClick={() => this.add()}>
        {this.props.value}
      </button>
    );
  }
}

Upvotes: 0

Related Questions