kovac
kovac

Reputation: 5389

onClick event handler not firing when updating state directly - ReactJS

I'm new to ReactJS. I have the following code, in which I try to update UI by updating state using an onClick event handler. However, the onClick is not firing and it's not even showing on the final HTML. I can see the button but the button is not bound to the event handler. My code is:

import React from 'react';

class App extends React.Component {
   constructor() {
      super();
      this.state = {
         data: []
      }
      this.setStateHandler = this.setStateHandler.bind(this);
   }
   setStateHandler() {
      var item = "setState...";
      var myArray = this.state.data;
      myArray.push(item)
      this.setState({data: myArray})
   }
   render() {
      return (
         <div>
            <button onClick = {this.setStateHandler}>SET STATE</button>
            <h4>State Array: {this.state.data}</h4>
         </div>
      );
   }
}

export default App;

My final HTML rendered on the browser is:

<html lang="en"><head>
      <meta charset="UTF-8">
      <title>React App</title>
   <style type="text/css"></style></head>

   <body>
      <div id="app">
        <div>
          <button>SET STATE</button><h4>State Array: </h4>
        </div>
      </div>
 <script src="index.js"></script>
</body>
</html>

What am I doing wrong?

EDIT:

When I change var myArray = this.state.data; to var myArray = this.state.data.slice(); it works as intended.

Upvotes: 0

Views: 1098

Answers (2)

Pulkit Gambhir
Pulkit Gambhir

Reputation: 41

Try using {String(this.state.data)} or {this.state.data.toString()} instead.

Upvotes: 0

Tomasz Mularczyk
Tomasz Mularczyk

Reputation: 36179

You shouldn't see onClick handler in your HTML, that's how React works.

Working code:

class App extends React.Component {
   constructor(props) {
      super(props);
      this.state = {
         data: []
      }
      this.setStateHandler = this.setStateHandler.bind(this);
   }

   setStateHandler() {
      var item = "setState...";
      this.setState(prevState => ({ data: [...prevState.data, item]}));
   }

   render() {
      return (
         <div>
            <button onClick = {this.setStateHandler}>SET STATE</button>
            <h4>State Array: {this.state.data}</h4>
         </div>
      );
   }
}

export default App

The problem in your example is within setStateHandler definition. You are modifying state directly which is not a correct way to update state.

   setStateHandler() {
      var item = "setState...";
      this.setState(prevState => ({ data: [...prevState.data, item]}));
   }

In my example I use function as an argument to setState (read when to use function as argument) which will pass me a copy of component state. Then I use destructuring assignment (...prevState) to create new array with a item element. At last I return object to be merged into state.

Note that I used destructuring assignment for convenience and brevity. You can accomplish the same by just cloning the array:

   setStateHandler() {
      var item = "setState...";
      this.setState(prevState => {
        const newArray = prevState.data.slice();
        newArray.push(item);
        return { data: newArray };
      });
   }

Beside that you should also pass props to base constructor. But that wasn't the issue in this case.

Upvotes: 1

Related Questions