0

I have simple hello world code:

import React from 'react';

class ShoppingList extends React.Component {

    getComponent(event) {
        console.log('li item clicked!');
        event.currentTarget.style.backgroundColor = '#ccc';
    }

    render() {
        return (

            <div className="shopping-list">
                <h1>This is props name: {this.props.name}</h1>
                <ul>
                    <li>Item 1</li>
                    <li>Item 2</li>
                    <li>Item 3</li>
                </ul>
                <div>
                    <ul>
                        <li onClick={this.getComponent.bind(this)}>Component 1</li>
                    </ul>
                </div>
            </div>

        );
    }
}

module.exports = ShoppingList;

When I click on <li>Component 1</li> nothing happens.

Why? Page is rendered successfully. No errors,
everything is ok, but handler not working.

FULL EXAMPLE:

Node server: app.js

var express = require('express');
var app = express();

var routes = require('./routes/index')

app.use('/', routes);

app.set('port', process.env.PORT || 9080);

app.use('/public', express.static(__dirname + '/public/'));

app.set('views', __dirname + '/views');
app.set('view engine', 'jsx');
app.engine('jsx', require('express-react-views').createEngine());

var server = app.listen(app.get('port'), function () {
    console.log(__dirname + '/public/');
    console.log('STARTED');
});

route: index.js:

var express = require('express');

var router = express.Router();

router.get('/', function (req, res) {
    res.render('index', {name:"AHOJ"});
});

module.exports = router;

index.jsx:

import React from 'react';


var ShoppingList = require('./components/ShoppingList');

class IndexComponent extends React.Component {
    constructor(props) {
        super(props);
        this.getComponent = this.getComponent.bind(this);
    }

    getComponent(event) {
        console.log('li item clicked!');
        event.currentTarget.style.backgroundColor = '#ccc';
    }
    render() {
        return (

            <DefaultLayout name={this.props.name}>
                <div>
                    <ul>
                        <li onClick={this.getComponent}>Component 1</li>
                    </ul>
                </div>
            </DefaultLayout>

        )
    }
};

module.exports = IndexComponent;

master.jsx:

var React = require('react');

class MasterLayout extends React.Component {
    render() {
        return (
            <html lang="eng">
                <head>
                    <meta charset="utf-8" />

                    <title>{this.props.name}</title>
                    <meta name="description" content="The HTML5 Herald" />
                    <meta name="author" content="SitePoint" />
                    <link rel="stylesheet" type="text/css" href="/public/css/main.css" />
                </head>
                <body>
                    {this.props.children}
                </body>
            </html>
        )
    }
};

module.exports = MasterLayout;

I hope, this code is clear for you, its hello world project. In full example is class ShoppingList : IndexComponent.

I read some tutorials, and I thinks, my code is correct, page is rendered successfully. No errors,
everything is ok, but handler not working.

<li> have not data-reactid

5
  • I created this fiddle with your code and it seems to work as you expect jsfiddle.net/ay15ezyx Commented Jul 26, 2017 at 17:22
  • You sure? Works fine for me. jsfiddle.net/mkk7fksw Commented Jul 26, 2017 at 17:22
  • Yeah i am sure, I am using 15.6.1v Commented Jul 26, 2017 at 17:24
  • Then we'll need more information since at least two people have a working version w/o any changes. (Take heed of where you bind, though, you don't really want to do it in the render method.) Commented Jul 26, 2017 at 17:26
  • I added full code. Commented Jul 26, 2017 at 17:40

3 Answers 3

3

That isn't how you should structure your component. Do it like so:

class ShoppingList extends React.Component {
    constructor(props) {
        super(props);
        this.getComponent = this.getComponent.bind(this);
    }

    getComponent(event) {
        console.log('li item clicked!');
        event.currentTarget.style.backgroundColor = '#ccc';
    }

    render() {
        return (
            <div className="shopping-list">
                <h1>This is props name: {this.props.name}</h1>
                <ul>
                    <li>Item 1</li>
                    <li>Item 2</li>
                    <li>Item 3</li>
                </ul>
                <div>
                    <ul>
                        <li onClick={this.getComponent}>Component 1</li>
                    </ul>
                </div>
            </div>
        );
    }
}

What I have done here is I have created a constructor for your component. The constructor calls super() so that it can use the this keyword.

When used in a constructor, the super keyword appears alone and must be used before the this keyword can be used. This keyword can also be used to call functions on a parent object.

Then, it calls .bind(this) and binds getComponent to the component. Now the onClick handler can be changed to onClick={this.getComponent}. Moving the call to .bind() to the constructor offers a significant performance boost, since now the method is bound only once, instead of being bound over and over every time the component gets rendered.

Extra tip: change the name of getComponent's argument from event to something else. event is not a reserved keyword in JS, but it is however a global in certain versions of IE. Pretty tricky bug to track down.

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

5 Comments

Just to point out, the reason to use bind() in the constructor is that it's preferable to do it once when the component is initialized, instead of inside of render() which will redundantly bind and re-bind the function every time render is run.
Thank you for your reply, still same problem. Not working. Constructor is called successfully, but handler not working, I am missing data-reactid in dom.
@stybl still not working, console is clear without errors and logs.
@praeslai The problem is not related to the component. This code works properly. The problem is somewhere else. Try creating a new react project and adding everything to it bit by bit, testing along the way. When the handler stops working, you'll know what the problem is.
this.testMethod = this.test.bind(this); then call this.testMethod(); works sucesfully
1

Just an FYI, you can also use stateless functional components and use a method stored in a constant:

import React from 'react';
import { render } from 'react-dom';

const clickHandler = e => {
  console.log(`${e.target} \n element clicked!!! \n -------`);
};

const App = () => (
  <div>
    <ul>
      <li style={{border:"solid 1px"}}
        onClick={clickHandler}
      >Click Me</li>
    </ul>
    <h2 onClick={clickHandler}>I'm a header</h2>
  </div>
);

render(<App />, document.getElementById('root'));

If you click on the <li> the console will show:

[object HTMLLIElement] 
 element clicked!!! 
 -------

If you click on the header it'll show:

[object HTMLHeadingElement] 
 element clicked!!! 
 -------

But of course if you need to extend the component class you can use it, although is not necessary to create the constructor if you use the target instead of current target:

https://developer.mozilla.org/es/docs/Web/API/Event/currentTarget

Here's the code without the constructor:

import React, {Component} from 'react';
import { render } from 'react-dom';

class App extends Component {

  clickHandler(e){
    console.log(e.target);
    e.target.style.backgroundColor = '#ccc';
  }

  render(){
    return(
      <div>
        <ul>
          <li style={{border:"solid 1px"}}
            onClick={this.clickHandler}
          >Click Me</li>
        </ul>
        <h2 onClick={this.clickHandler}>I'm a header</h2>
      </div>
    );
  }

}

render(<App />, document.getElementById('root'));

As you can see it works in both cases:

https://codesandbox.io/s/nZY3W5x0p

Comments

0

Change your li to:

<li onClick={() => this.getComponent}>Component 1</li>

This way it will reference it properly when actually clicked, and not when rendered.

4 Comments

You should take a look at Why you shouldn't use inline arrow functions in JSX props. It is a bad practice and will cause you problems. Hurt performance. Causes increased work for the garbage collector and breaks PureComponent shallow compare.
(Oops, I missed the inline arrow function--my bad. But this is just a fancy way of doing the bind from the OP's source, so makes no difference.)
Using an inline arrow function makes a big difference when it comes to performance.
@KyleRichardson Appreciate the link.

Your Answer

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