React.js中的动态子导航(传播事件和属性)

时间:2014-09-12 17:58:58

标签: javascript reactjs

我仍然在React.js中挣扎的一件事是处理子事件和属性传播的正确方法。已经有很多这样的例子已经完成,但实现总是大不相同。当然,有一种正确的方式"这样做......

任务是创建一个" Nav"由" NavItem"组成的组件。组件。下面是我期望工作的代码,但它没有。这些评论解释了我遇到问题的地方。请告知最佳解决方案,让我知道我做错了什么。

Nav.jsx

var Nav = React.createClass({
    propTypes: {
        active:         React.PropTypes.string,
        onSelect:       React.PropTypes.func
    },
    getInitialState: function () {
        return {
            active: this.props.active
        };
    },
    render: function () {
        return this.transferPropsTo(
            <nav>
                {this.props.children.map(this.renderChild)}
            </nav>
        );
    },
    renderChild: function (child, i) {
        // Here I want to generate a unique 'key' property for each child as well
        // as define a custom onSelect method which will tell the "Nav" component which is
        // the active key.
        return React.addons.cloneWithProps(child, {
            // Here I want to use the 'active' property of the Nav component to drive the
            // the 'active' property of the child.
            active: this.state.active === i ? true, false,
            key: i,
            onSelect: this.handleSelect
        });
    },
    handleSelect: function (event, component) {
        // PROBLEM: Here I want to get the 'key' property of the child. But there seems to be
        // no way to do this.
        var _child_key_ = 'impossible'; 
        this.setState({active: _child_key_});
    }
});

NavItem.jsx

var NavItem = React.createClass({
    propTypes: {
        active:     React.PropTypes.string,
        onSelect:   React.PropTypes.func
    },
    getInitialState: function () {
        return {
            active: this.props.active
        };
    },
    render: function () {
        return this.transferPropsTo(
            <a className="{this.state.active ? 'active' : ''}" onClick={this.handleSelect}>
                {this.props.label}
            </a>
        );
    },
    handleSelect: function (event, component) {
        // Propagate the event up to the parent event property.
        if (this.props.handleSelect) this.props.handleSelect(event, component);
    }
});

MyApp.jsx

var App = React.createClass({
    render: function () {
        return (
            <Nav>
                <NavItem label="Home" />
                <NavItem label="Page1" />
                <NavItem label="Page2" />
            </Nav>
        );
    }
});

React.renderComponent(new App(), document.body)

1 个答案:

答案 0 :(得分:2)

我相信这是最理智的方式。

var Nav = React.createClass({
    propTypes: {
        active:         React.PropTypes.string,
        onSelect:       React.PropTypes.func
    },

    // no state
    //getInitialState: function () {},

    render: function () {
        // use React.Children.map because children is opaque
        return this.transferPropsTo(
            <nav>
                {React.Children.map(this.props.children, this.renderChild)}
            </nav>
        );
    },
    renderChild: function (child, i) {
        return React.addons.cloneWithProps(child, {
            // use the prop, not state
            active: this.props.active === i,
            key: i,

            // let the parent decide how to handle the data change
            // give it the clicked index
            onSelect: this.props.onSelect.bind(null, i)
        });
    }
});
var NavItem = React.createClass({
    propTypes: {
        // it's a boolean
        active:     React.PropTypes.bool,
        onSelect:   React.PropTypes.func
    },
    // again, no state
    //getInitialState: function () {},

    render: function () {
        // just pass the onSelect handler in directly
        // let the parent handle it
        return this.transferPropsTo(
            <a className={this.props.active ? 'active' : ''} 
               onClick={this.props.onSelect}>
                {this.props.label}
            </a>
        );
    }
});
var App = React.createClass({
    getInitialState: function(){ return {active: 0} },

    handleSelect: function(i){ this.setState({active: i}) },

    render: function () {
        return (
            <Nav onSelect={this.handleSelect} active={this.state.active}>
                <NavItem label="Home" />
                <NavItem label="Page1" />
                <NavItem label="Page2" />
            </Nav>
        );
    }
});