react-rails icon indicating copy to clipboard operation
react-rails copied to clipboard

onClick not binding

Open matthew-russo opened this issue 7 years ago • 17 comments

Help us help you! Please choose one:

  • [ ] My app crashes with react-rails, so I've included the stack trace and the exact steps which make it crash.
  • [X] My app doesn't crash, but I'm getting unexpected behavior. So, I've described the unexpected behavior and suggested a new behavior.
  • [ ] I'm trying to use react-rails with another library, but I'm having trouble. I've described my JavaScript management setup (eg, Sprockets, Webpack...), how I'm trying to use this other library, and why it's not working.
  • [ ] I have another issue to discuss.

I'm new to React and was following a simple tutorial for a Modal component but cannot get the onClick binding to work.

This is the code:

class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = { isModalOpen: true }
    }

    openModal(){
        console.log("open modal");
        this.setState({ isModalOpen: true })
    }

    closeModal(){
        console.log("close modal");
        this.setState({ isModalOpen: false })
    }

    render () {
        return (
            <div>
                <button onClick={ () => this.openModal() }>Open Modal</button>
                <Modal isOpen={this.state.isModalOpen} onClose={() => this.closeModal()}>
                    <h1>Modal Title</h1>
                    <p>Hello World</p>
                    <p><button onClick={ () => this.closeModal() }>Close</button></p>
                </Modal>
            </div>
        )
    }
}

I've tried it a few different ways -- with a regular function expression, with just the function with no parentheses. Nothing seems to be working and its not showing up when I inspect any elements. Does anyone have any idea what the issue is? I've searched around for other's with this problem but none of their solutions did anything.

matthew-russo avatar Jul 06 '17 15:07 matthew-russo

You have to bind own methods, so this is referenced correctly.

class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = { isModalOpen: true }

        this.openModal = this.openModal.bind(this)
        this.closeModal = this.closeModal.bind(this)
    }

    // ...

This is not specific to Rails or React, see https://facebook.github.io/react/docs/handling-events.html:

You have to be careful about the meaning of this in JSX callbacks. In JavaScript, class methods are not bound by default. If you forget to bind this.handleClick and pass it to onClick, this will be undefined when the function is actually called.

This is not React-specific behavior; it is a part of how functions work in JavaScript. Generally, if you refer to a method without () after it, such as onClick={this.handleClick}, you should bind that method.

timomeh avatar Jul 13 '17 14:07 timomeh

@timomeh Thanks for the response. I should have read more carefully. For some reason, even after I add those bindings, it still doesn't work. This is in use via the asset pipeline and server-side rendering. Could that have anything to do with it?

matthew-russo avatar Jul 13 '17 15:07 matthew-russo

I use it with the asset pipeline, too, and it works just fine with the code example above. I don't do server side rendering, but that shouldn't have an effect on that.

timomeh avatar Jul 13 '17 16:07 timomeh

Going to close this without other reproduction steps. Will reopen if there's more details, thanks!

BookOfGreg avatar Apr 16 '18 21:04 BookOfGreg

@mcr431 I'm having the same issue, were you able to fix it ?

This is my code:

import React from "react"
import PropTypes from "prop-types"
class HelloWorld extends React.Component {

  constructor(props) {
      super(props)
      this.test = this.test.bind(this);
  }

  test(ev){
       console.log('the button was clicked ', ev);
  }

  render () {
    this.test("Hi");
    var b = <button type="button" onClick={this.test} id="678">Click me</button>;

    return (
      b
    );
  }


}

export default HelloWorld

This is how I render it

ActiveAdmin.register_page "Calendar" do
  content do
    # render plain: "Test text"
    link_to "test_page_action", calendar_test_page_action_path
  end
  page_action :test_page_action, method: :get do
    # ...
    render component: 'HelloWorld'
  end
end

AmaniKhalifa avatar Jun 24 '18 19:06 AmaniKhalifa

@AmaniSalah This was quite a while ago and I don't remember if/how I addressed it. In the long term I had ended up just making a SPA that hit a Rails API. Sorry that's not much help. Good luck!

matthew-russo avatar Jun 25 '18 14:06 matthew-russo

thanks @mcr431

@BookOfGreg @timomeh can anyone help with this ?

AmaniKhalifa avatar Jun 25 '18 17:06 AmaniKhalifa

@AmaniSalah Sure. Could you please explain your problem a little bit more in detail? What should happen, but what's happening instead? What's not working?

According to your code, each time HelloWorld is rendered, it should print the button was clicked Hi to the console. If the Button is clicked, it should log the button was clicked { SyntheticEvent ... }.

You aren't using this inside your test-method, so I don't see how binding is a problem.

timomeh avatar Jun 25 '18 17:06 timomeh

@timomeh the issue is that onClick action is not attached to the button, so only the button was clicked Hi is logged, but nothing happens when I click the button.

actually when I log the button b I get this as its props, and no onClick action attached to it. props : children : "Click me" id : "678" type : "button"

AmaniKhalifa avatar Jun 25 '18 17:06 AmaniKhalifa

This doesn't sound like a problem with method binding. You only need .bind(this) if you're using this inside your test-method.

However, I tested your code example and it worked just fine for me. The log was called each time I pressed the button.

If the onClick prop is missing on your button (you could also check that using React DevTools), it seems like an issue with your setup.

timomeh avatar Jun 25 '18 18:06 timomeh

@timomeh it seems I didn't correctly understand the question. thanks for your answer, I'm already using React DevTools, but it doesn't detect react running in my rails app. By the setup, do you mean the rails project setup ? do you have an idea what could affect the props like that ?

AmaniKhalifa avatar Jun 25 '18 21:06 AmaniKhalifa

If React DevTools doesn't detect the React App, and the button click is not triggered, it sounds like you're only doing server-side rendering, and the browser isn't actually running your JS code.
Regarding to the log of b you posted, was is logged to the browsers console, or to your terminal output? The format you posted looks a bit unfamiliar.

Do you have maybe some custom webpack config or other specific configuration in your project? I tired it with a clean install with webpacker and react-rails, which worked, so your problem seems specific to your environment.

timomeh avatar Jun 26 '18 07:06 timomeh

Reopening due to the amount of activity here, but it does seem that @timomeh is on the right lines about it not being run client-side. (Thanks @timomeh for helping out here by the way!)

BookOfGreg avatar Jun 26 '18 08:06 BookOfGreg

@timomeh it's logged in the browser console. nothing from reactjs is logged in the terminal.

don't I need to set some configuration for the server-side rendering to work ? I did not add any extra configs in config/application.rb

I tried the same steps with a fresh rails app and it's working, so I'm not sure what's the problem with my current rails app.

AmaniKhalifa avatar Jun 26 '18 09:06 AmaniKhalifa

Hummm, that sounds very strange. It's very weird that React DevTools doesn't recognize the React App (which would indicate server-side-rendering), but the log is still shown in the browsers console (which indicates client-side-rendering).

You could check if there's a newer version of webpacker, react-rails, etc you can update to. Also you could compare if you have some custom webpack config, which could cause this (for example compare config/webpack/*.js and config/webpacker.yml with your fresh rails app).

It also doesn't hurt to do the good old rm -rf node_modules && yarn install, just in case.

I don't have any other ideas which could cause this strange behavior. 🙁

timomeh avatar Jun 26 '18 09:06 timomeh

@AmaniSalah @timomeh @mcr431 I also had the same issue when using onChange event and using server-side rendering. my code is

class TaskForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
    this.handleInputChange = this.handleInputChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  };

  handleInputChange(event) {
    const target = event.target;
    const name   = target.name;

    this.setState({
      [name]: target.value
    });
    console.log(this.state);
  }
render() {
    console.log(this)
    console.log("up")
    return (
      <div id="modal" style={{display: 'block'}}>
        <div className="modal-body" style={{width: 'auto', overflow: 'hidden'}}>
          <form onSubmit={this.handleSubmit}>
            <p>
              <label htmlFor="task_title">Task name</label>
              <input
                autoComplete="off"
                className="span6 task_title"
                id="task_title_"
                onChange={this.handleInputChange}
                name="task[title]"
                size="30"
                style={{width: '98%'}}
                type="text" />
            </p>
        </form>
       </div>
     )
   }
}

I think the issue come with rendering the js response. I was rendering the response something like

$("#modal").html("<%= render :partial => 'form', :locals => {:task => @task} %>");

and in form I was writing the react-rails helper for rendering the component

<%= react_component('TaskForm', {title: 'Hello World'}, prerender: true) %>

The onChange event work when I directly renders a html page and use helper react_component but not with js response. Any idea why is it so?

punitcse avatar Oct 25 '18 09:10 punitcse

I solve that putting in call of component like this = react_component("FooterApp",prerender: true,trace:true, serverSide:false) with these keys and reload browser with cleaning cache

vNNi avatar Nov 27 '18 19:11 vNNi

As per the above conversation, we have a fix for this.

alkesh26 avatar Nov 10 '22 18:11 alkesh26