1

First off I Know about this thread: React onClick event on component

Is there not a way to do this directly to a React Component? That didn't seem to get answered in the thread. I know how i can add the attacked function a layer deeper but that is just extra connection code that makes it more brittle IMO. any change that it make to the ListItem level i would have to make to the interconnect. I imagine this getting much worse if am an nTh level child component trying to get back up to the master State.

My Case

Parent->List->ListItem

I need to have a click event on a list element trigger a function on the parent. I want to be able to click a ListItem component and have that onClick event come back up to Parent and update the state to trigger a rerender of a of a sibling component to List I have cut out a lot of boilerplate.

Parent Component wrapping it all. this is where i am using state to update my render views.

var Parent = React.createClass({
    updateState:function(newCurrent){
        this.setState({current: newCurrent});
    },
    render: function() {
        return (
            <div className="Parent"  >
              <Current this.state.current />
              <List
                list={this.state.list} 
                updateCurrentVideo={this.updateCurrentVideo} />
            </div>
        );
    }
}); 

List here is the List Element described at the start, i would like to attach an onClick event to each ListItem ti pass its data back up to parent to update state.

Instead of attaching the event inside of the compoent and the having to pass the trigger like this. ListItem->List->Parent(). It would be nice to attach the listener while i .map through the children of List.

var List = React.createClass({
    render: function() {
        var list = this.props.list.map(function(item,index){
            return (
                <ListItem  
                    onClick={ () => this.props.updateState(item) } />
            )
        }.bind(this));

        return (
            <div className="List">
                {list}
            </div>
        );
    }
});

The onClick attached at the component level doesn't appear to actually do anything. However if i change it to be like this and pass a prop down to the ListItem with a callback function then it magically works. So i guess my greater question this.

var ListVideos = React.createClass({
    handleClick:function(data){
        this.props.updateCurrentVideo(data)
    },
    render: function() {
        var list = this.props.list.map(function(item,index){
            return (
                <ListItem  
                    handleClick={this.handleClick(item) } />
            )
        }.bind(this));

        return (
            <div className="List">
                {list}
            </div>
        );
    }
});

And then Attach the onClick to the Component wrapper in the ListItem

var ListItem = React.createClass({
    render: function() {
        return (
            <div className="ListItem" onClick={this.props.handleClick}> 
                // STUFF
            </div>
        );
    }
});
4
  • I don't exactly follow what you are asking, but AFAIK you have to pass functions all the way down from the parent, unless you use context which creates a global in React to allow you to skirt this requirement. Commented Mar 24, 2016 at 20:35
  • That is basically my question i suppose. Asking if there is a good way to not have to chain your props all the way down and back up. Commented Mar 24, 2016 at 22:10
  • ^ @BrianShotola using context should be used only in very specific cases and is a pretty experimental feature. It would probably be better to either consider if your app is complex enough for flux or simply pass functions via props. Commented Mar 25, 2016 at 4:29
  • @markthethomas I should have been more precise. I agree, I don't condone the use of context, but it does exist for the purpose I stated. I also agree with the rest of your comment, and flux/props would also be my recommendation. Commented Mar 25, 2016 at 14:20

2 Answers 2

2

You can have ListItem pass all of its props down, allowing event listeners to fall in automatically.

var ListItem = React.createClass({
    render: function() {
        return (
            <div className="ListItem" {...this.props}> 
                Stuff
            </div>
        );
    }
});

Or with es6, you can pass only the ones you don't use, which is preferred.

var ListItem = React.createClass({
    render: function() {
        var {text, ...props} = this.props;
        return (
            <div className="ListItem" {...props}> 
                {text}
            </div>
        );
    }
});
Sign up to request clarification or add additional context in comments.

1 Comment

So if i pass the props down through the components in this way. When i call a prop method on my Nth child does it call it on the grand(or great great great grandparent) ? or do i still have to wire up all the internal logic on every substep component?
0

If I understand what you're asking - can you invoke a grandparent's method from a grandchild without daisy chaining through the parent? - then yes, you can do it.

Your problem might be because in the first example, when you create the List, you give it a property pointing to updateCurrentVideo, which doesn't exist, and then when you create the ListItem, you give it a property pointing to this.props.UpdateState which also doesn't exist.

It doesn't really explain why your other examples work, but I am guessing there were some typographical problems (what's the difference between List and ListVideos?) or you haven't given us the exact names. Incorrectly named handlers can be a source of silent failures with React.

In any case, the general answer is that something like this will work:

var Grandparent = React.createClass() {
    handleChange: function(update) {
        this.setState({data: update});
    },
    render: function() {
        return (
            <Parent handleChange={this.handleChange} />
        );
    }
}

var Parent = React.createClass() {
    render: function() {
        return (
            <Child handleChange={() => this.props.handleChange(changeValue)} />
        );
    }
}

var Child = React.createClass() {
    render: function() {
        return (
            <div onClick={this.props.handleChange}>Go</div>
        );
    }
}

You can also do something more vanilla without using an anonymous function in the cases where you need to handle onClick in the Child before invoking the callback.

2 Comments

I cut out a lot of code from teh actual components taht were doing other things. both of those calls that you mentioned don't exists were simply code i missed when cutting out the excess that wasn't relevent to the question. This answer is essentially my current solution. it just gets really annoying once you start getting deeper into a component tree and need to talk back up to your app store.
Although i guess this is what actual stores are meant for to not have to deal with this kind of componenet transversal.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.