使用子组件作为实际组件,而不是无状态子对象

时间:2017-04-05 16:10:20

标签: reactjs

某些组件包含Tabs Tab s:

class App {
  render() {
    <div>
      <p>Tabs here:</p>
      <Tabs>
        <Tab name="Page 1"> .. content here .. </Tab>
        <Tab name="Page 2"> .. content here .. </Tab>
        <Tab name="Page 3"> .. content here .. </Tab>
      </Tabs>
    </div>
  }
}

Tabs负责大部分标记:

class Tabs {
  click() {
    // I want the Tab component, because I want to tab.setState()
    // `this` is not a `Tab` object
  }
  render() {
    return (
      ( print the tab labels: )
      <ul>
        { this.props.children.map(tab =>
          // >>> `tab` is not a `Tab` object <<<
          <li><a onClick={ this.click.bind(tab) } href="#">{ tab.props.name }</a></li>
        ) }
      </ul>

      ( print the tab content: )
      { this.props.children }
    );
  }
}

Tab本身很少,也许什么都没有:

const Tab = ({children}) => <div>{ children }</div>;

Tabs.click如何知道点击了哪个Tab。如果我click.bind(tab)它是子对象,而不是Tab对象。如果我click.bind(this)它是Tabs对象。

这可能是一种非常冗长的方式,可以解释为什么props.children不包含Component对象,而是一种代理(?)子对象。它确实有道具,但没有方法,状态等。

编辑1:
在这个例子中,我想让Tab决定如何呈现自己:一次作为标签链接,一次作为标签内容。标签将负责调用渲染方法,但Tab会知道如何。正如您在Tabs.render()中看到的,有2个渲染。如果可以做到这一点,那就太好了:

      <ul>
        { this.props.children.map(tab => tab.renderLink()) }
      </ul>

      { this.props.children.map(tab => tab.renderContent()) }
      or just
      { this.props.children }
      because content is the normal render

但是Tabs无法做到这一点,因为Tab没有children个对象......为什么?

1 个答案:

答案 0 :(得分:0)

嗯,有几种方法可以实现这一目标。首先,你是对的 - 因为你在map中使用了ES6箭头功能,this并没有反弹 - 它继承了&#34;继承& #34;从封闭的范围(我认为,从技术上讲,它不那么继承而且更多只是保持不变 - 没有改变)。

首先,不要在render方法中绑定,而是在构造函数中绑定click

constructor(props) {
    super(props);

    this.click = this.click.bind(this);
}

或使用ES6箭头功能

click = (event) => {...}

虽然这并不能直接解决您的问题,但它会清除因使用绑定回调而产生的上下文混乱。

从那里,您可以使用event.target

获取点击的标签

您也可以使用partials - bind方法接受附加参数,这些参数前缀为绑定函数的参数。这看起来像这样:

render (
    <div>
    { this.props.children.map((tab) => {
        <a <li><a onClick={ this.click.bind(null, tab) } href="#">{ tab.props.name }</a></li>
    }) }
);

您还需要调整click方法以接受标签:click(tab, event)

我也不认为这是明智的,甚至建议从其他组件中弄乱组件的状态。为什么不使用Tab组件?传递Tabs onClick作为道具并处理此类点击事件......

class Tab extends React.Component {
    onClick = (event) => {
        this.setState({...}); // Tab onClick

        if (this.props.onClick) this.props.onClick(); // Tabs onClick
    }
}

编辑:工作示例

Tabs上课

class Tabs extends React.Component {
    onTabClick = (tab) => {
        tab.setState({ test: true });
    }

    render() {
        return (
            <div>
                { this.props.children.map((tab) => {
                    return React.cloneElement(tab, {
                        onClick: this.onTabClick
                    });
                }) }
            </div>
        );
    }
}

Tab上课

class Tab extends React.Component {
    constructor(props) {
        super(props);
        this.state = {};
    }

    onClick = (event) => {
        console.log(event);
        event.preventDefault();

        if (this.props.onClick) {
            this.props.onClick(this);
        }
    }
    render() {
        return <div>{ !this.state.test && <a href={ this.props.url } onClick={ this.onClick }>{ this.props.label }</a> }</div>;
    }
}

示例标签

<Tabs>
    <Tab url="http://www.google.com" label="Google" />
    <Tab url="http://www.google.com" label="Google" />
</Tabs>

虽然它再次引发了这个问题 - 为什么不在Tab类中设置状态呢?为什么需要在父母中设置孩子的状态? Can that state not be maintained by the parent (using this.setState(...) and passed into the child as a prop?