如何在嵌套的React组件中传递状态和处理程序

时间:2016-12-02 21:54:15

标签: javascript reactjs ecmascript-6

我在通过我的React组件传递处理程序时遇到了问题。

我已尝试按照React文档的Lifting State Up部分中的说明进行操作。

我们的想法是拥有一个带有选项卡式导航的页面,每个选项卡都会呈现一些子页面的显示。我的整个页面有一个组件Page.js,这就是我存储activeTab状态的位置,我认为我应该定义处理状态更改的函数。然后我将activeTab状态和处理程序作为道具传递给TabMenu.js组件,后者又将其传递给TabItem.js组件。

文件:

Page.js

import React, { Component } from 'react';

import TabMenu from './TabMenu';
import FooPage from './FooPage';
import BarPage from './BarPage';

class Page extends Component {

    constructor(props) {
        super(props);
        this.state = {
            activeTab: 'foo'
        };

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

    getVisiblePage() {
        switch(this.state.activeTab) {
            case 'bar':
                return (
                    <FooPage />
                );
            case 'foo':
            default:
                return (
                    <BarPage />
                );
        }
    }

    setActiveTab(e, tab) {
        this.setState({
            activeTab: tab
        });
    }

    render() {
        var visiblePage = this.getVisiblePage();

        return (
            <section>
                <TabMenu
                    activeTab={ this.state.activeTab }
                    changeTabHandler={ this.setActiveTab }
                />
                { visiblePage }
            </section>
        );
    }
}

export default Page;

TabMenu.js:

import React, { PropTypes } from 'react';

import TabItem from './TabItem';

const TabMenu = ({ activeTab, changeTabHandler }) => {

    const tabs = [
        {
            key: 'foo',
            text: 'Foo Page',
        },
        {
            key: 'bar',
            text: 'Bar Page'
        },
    ];

    const tabItems = tabs.map((item) => (
        <TabItem
            key={ item.key } 
            item={ item }
            isActive={ item.key === activeTab }
            changeTabHandler={ changeTabHandler }
        />
    ));

    return (
        <nav id="TabMenu">
            <ul className="tab-items">
                { tabItems }
            </ul>
        </nav>
    );
};

TabMenu.displayName = 'TabMenu';

TabMenu.propTypes = {
    activeTab: PropTypes.string,
    changeTabHandler: PropTypes.func,
};

export default TabMenu;

TabItem.js

import React, { PropTypes } from 'react';

const TabItem = ({ item, changeTabHandler }) => {
    return (
        <li onClick={ changeTabHandler(item.key) }>
            { item.text }
        </li>
    );
};

TabItem.displayName = 'TabItem';

TabItem.propTypes = {
    item: PropTypes.object,
    changeTabHandler: PropTypes.func,
};

export default TabItem;

最终结果是我的控制台溢出了以下错误的1000份副本:

  

warning.js:36警告:setState(...):无法在现有状态转换期间更新(例如在render或其他组件的构造函数中)。渲染方法应该是道具和状态的纯函数;构造函数副作用是反模式,但可以移动到componentWillMount

我做错了什么?

2 个答案:

答案 0 :(得分:3)

无限循环是因为你在父组件的渲染函数中有一些东西,它会调用setState或触发对另一个组件的更新,这会影响原始或父组件的状态,然后调用它们再次渲染。

在你的情况下,因为在TabItem.js中,

<li onClick={ changeTabHandler(item.key) }>
            { item.text }
        </li>

实际上立即调用changeTabHandler将在Page中执行setState,然后TabItem将呈现并再次调用changeTabHandler

将其更改为

<li onClick={() => changeTabHandler(item.key) }>
            { item.text }
        </li>

答案 1 :(得分:1)

您的changeTabHandler()会立即被调用,每次渲染<li>一次。改变这个:

<li onClick={ changeTabHandler(item.key) }>

到此:

<li onClick={ () => changeTabHandler(item.key) }>