3

I'm new to React. I'm currently studying react from a youtube video called "The Net Ninja" "Complete React Tutorial (& Redux)". on the tutorial, nothing is mentioned about debugging in chrome (yet). I had a bug (same Key for 2 child elements) with adding a new UI component to a list of the same components. Now I use arrow function to auto bind "this". yet in the chrome debugger, I can't watch "this.state" cuz "this" is undefined. the real bug is that in code line like "if(this.state.id)" it's always false - cuz "this" undefined. but if I do code line like "let a = this.state" I can watch "a" with all the data that "this.state" has. her some of my code and some photos from the debugger.

App.js

import React, { Component } from 'react';
import Ninjas from './Ninjas';
import AddNinja from './AddNinja';

class App extends Component {
  state = {
    ninjas: [
      { name: 'Ryu', age: 30, belt: 'black', id: 1 },
      { name: 'Yoshi', age: 20, belt: 'green', id: 2 },
      { name: 'Crystal', age: 25, belt: 'blue', id: 3 }
    ]
  }

  addNinja = (ninja) => {
    ninja.id = Math.random();
    let ninjas = [...this.state.ninjas, ninja];
    this.setState({
      ninjas: ninjas
    })
  }

  deleteNinjs = (id) => {   
    let ninjas =  this.state.ninjas.filter(ninja => ninja.id !== id)
    this.setState({
      ninjas: ninjas
    })
  }

  render() {
    return (
      <div className="App">
        <h1>My first React app!</h1>
        <p>Welcome :)</p>
        <Ninjas deleteNinjs={this.deleteNinjs} ninjas={this.state.ninjas} />
        {/*passing the func to bind the data from the child component*/}
        <AddNinja addNinja={this.addNinja} />
      </div>
    );
  }
}

export default App;

Ninja.js

import React from 'react';

const Ninjas = ({ ninjas, deleteNinjs }) => {   

    const ninjaList = ninjas.map(ninja => {
        return ninja.age > 20 ? (
            <div className="ninja" key={ninja.id}>
                <div>Name: {ninja.name}</div>
                <div> Age: {ninja.age}</div>
                <div>Belt: {ninja.belt}</div>                                          
                <button onClick={() => { deleteNinjs(ninja.id) }} >Delete ninja</button>
            </div>
        ) : null;
    });   

    return (
        <div className="ninja-list">
            {ninjaList}
        </div>                  
    )
}

export default Ninjas

AddNinja.js

import React, { Component } from 'react';

class AddNinja extends Component {

state = {
    name: null,
    age: null,
    belt: null
}

hendleChange = (e) => {
    this.setState({
        [e.target.id]: e.target.value
    })
}

hendleSubmit = (e) => {       
    debugger
//this is my testing to see if the state exists
    console.log(this.state)
//
// this is what will fix the bug of 2 childs with same key
    if (this.state.id)
        this.setState({
            id: null
        })
//
    e.preventDefault();
    this.props.addNinja(this.state);
}

render() {
    return (
        <div>
            <form onSubmit={this.hendleSubmit}>
                <label htmlFor="name">Name:</label>
                <input type="text" id="name" onChange={this.hendleChange} />
                <label htmlFor="age">Age:</label>
                <input type="text" id="age" onChange={this.hendleChange} />
                <label htmlFor="belt">Belt:</label>
                <input type="text" id="belt" onChange={this.hendleChange} />
                <button>Submit</button>
            </form>
        </div>
    )
}
}

export default AddNinja

Now I can make myself an easy "life" and bind all the funcs in the HTML to "this". and then I can see in the debugger "this.state". but I don't understand why the console is printing this not as undefined but as it is. and "if" statement doesn't work when I try to access to id in state. her is an image of the problem image of the debugger in chrome

2 Answers2

2

this isn't necessarily this in transpiled code. In this case it's likely something like _this temporary variable to simulate how lexical this behaves in arrow functions.

Check variables in the scope of current function at breakpoint instead of using 'watch' feature.

See also this explanation for the reference.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • so the answer is to upgrade babel to 7? and I tried to check in the console with _this and it did find state. yet the console.log(this.state) will print _this.state. so why this code knows to find the connection? but if statement doesn't. the code works fine only in some cases. – Snufkin4life Nov 21 '18 at 17:22
  • The answer is to not use `this` in console in this case. *so why this code knows to find the connection?* - the code 'knows' that because Babel replaces `this` in this place with temporary variable like `_this`. This is what linked answer explains. You can check Babel output to understand better what's going on with your code. – Estus Flask Nov 21 '18 at 17:33
  • well, I guess you didn't understand my question or I didn't explain myself good. my goal is to write a code and to be able to debug it in the chrome. I understand that babel replaces "this" with _this. so what should I do? not write "this" in arrow functions? use "that" "self" trick? I'm an angular dev, so I can use "this" in any case when I use angular, sometimes I need to manual bind. in react, it seems I need to always manual bind. I believe there is a "right" way to solve this. – Snufkin4life Nov 23 '18 at 00:04
0

I've asked the same question here:

Chrome, Firefox debuggers not displaying the correct value for 'this' in a react app

Basically the issue is that when you define an class variable arrow function - (ie. hendleSubmit = () =>), this isn't actually valid JavaScript - and there's actually the transform-class-property babel plugin that is running, and binding those functions to the class instance.

My understanding is that when chrome is looking at the sourcemaps to relate it back to your written code - it doesn't see that binding.

The only solution I know of - is to manually bind your class functions like this:

  constructor() {
    super();
    this.click2 = this.click2.bind(this);
  }

  click2() {
    console.log(this, "hello");
    let x = 1 + 1;
  }
dwjohnston
  • 11,163
  • 32
  • 99
  • 194