简体   繁体   中英

React, Adding onClick event directly to Components instead of render element inside said component.

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>
        );
    }
});

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>
        );
    }
});

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM