Passing props from grandchildren to parent

2020-06-22 03:24发布

问题:

I have following React.js application structure:

<App />
  <BreadcrumbList>
    <BreadcrumbItem />
  <BreadcrumbList/>
<App /> 

The problem is, when I click on <BreadcrumbItem /> , I want to change a state in <App />

I used callback to pass props to <BreadcrumbList/> but that`s how far I got. Is there any pattaren how to easily pass props up to compenent tree ?

How can I pass prop to <App />, without doing any callback chaining ?

回答1:

If you are doing something simple then its often just better to pass the change in state up through the component hierarchy rather than create a store specifically for that purpose (whatever it may be). I would do the following:

BreadcrumbItem

var React = require('react/addons');
var BreadcrumbItem = React.createClass({

embiggenMenu: function() {
    this.props.embiggenToggle();
},

render: function() {
    return (
            <div id="embiggen-sidemenu" onClick={this.embiggenMenu} />
    );
}
});

module.exports = BreadcrumbItem ;

THEN pass it up to the parent through the BreadcrumbList component.....

 <BreadcrumbItem embiggenToggle={this.props.embiggenToggle}>

... and UP to App, then use it to set the state....

var React = require('react/addons');

var App = React.createClass({

embiggenMenu: function() {
    this.setState({
        menuBig: !this.state.menuBig
    });
},

render: function() {
    return (

        <div>

            <BreadcrumbList embiggenToggle={this.embiggenMenu} />

        </div>

        )
    }
});
module.exports = BreadcrumbItem;

This example toggles a simple boolean however you can pass up anything you like. I hope this helps.

I have not tested this but it was (quickly) ripped from a live working example.


EDIT: As it was requested i'll expand upon the vague: "you can pass up anything".

If you were making a navigation menu based on an array and needed to pass up the selected item to a parent then you would do the following

var React = require('react/addons');

var ChildMenu = React.createClass({


getDefaultProps: function () {
    return {
        widgets : [
            ["MenuItem1"],
            ["MenuItem2"],
            ["MenuItem3"],
            ["MenuItem4"],
            ["MenuItem5"],
            ["MenuItem6"],
            ["MenuItem7"]
        ]
    }
},

handleClick: function(i) {
    console.log('You clicked: ' + this.props.widgets[i]);
    this.props.onClick(this.props.widgets[i]);
},

render: function() {
    return (
        <nav>
            <ul>
                {this.props.widgets.map(function(item, i) {
                    var Label = item[0];

                    return (
                        <li
                            onClick={this.handleClick.bind(this, i)}
                            key={i}>

                            {Label}
                        </li>
                    );
                }, this)}
            </ul>
        </nav>
    );
}
});

module.exports = ChildMenu;

You would then do the following in the parent:

var React = require('react/addons');

var ChildMenuBar = require('./app/top-bar.jsx');

var ParentApp = React.createClass({

widgetSelectedClick: function(selection) {
    //LOGGING
    //console.log('THE APP LOGS: ' + selection);

    //VARIABLE SETTING
    var widgetName = selection[0];

    //YOU CAN THEN USE THIS "selection"
    //THIS SETS THE APP STATE
    this.setState({
        currentWidget: widgetName
    });
},

render: function() {
        return (
                <ChildMenu onClick={this.widgetSelectedClick} />
        );
    }
});

module.exports = ParentApp;

I hope this helps. Thanks for the upvote.



回答2:

If you use Flux pattern, you can have a AppStore which listen a BREADCRUMB_CLICK event. So when you click on a BreadCrumbItem, you can execute an action which dispatch BREADCRUMB_CLICK event. When AppStore handle the event, he inform App component which update your state.

For more informations:

Flux architecture