【问题标题】:How to pass down state and handlers in nested React components如何在嵌套的 React 组件中传递状态和处理程序
【发布时间】:2016-12-02 21:54:15
【问题描述】:

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

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

这个想法是有一个带有标签导航的页面,每个标签都会呈现一些子页面的显示。我的整个页面有一个组件Page.js,这就是我存储 activeTab 状态的地方,我认为我应该定义处理状态更改的函数。然后,我将 activeTab 状态和处理程序作为 props 传递给 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 或其他组件的构造函数中)。渲染方法应该是 props 和 state 的纯函数;构造函数副作用是一种反模式,但可以移至componentWillMount

我做错了什么?

【问题讨论】:

    标签: javascript reactjs ecmascript-6


    【解决方案1】:

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

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

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

    实际上立即调用changeTabHandler会在Page中做setState,然后TabItem会渲染并再次调用changeTabHandler

    改成

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

    【讨论】:

    • 请记住,在渲染函数中创建大量闭包可能会导致性能略有下降,具体取决于您是否使用较旧的手机。
    【解决方案2】:

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

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

    到这里:

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

    【讨论】:

    • 与上述@kossel 的答案相同,但也谢谢您!
    猜你喜欢
    • 1970-01-01
    • 2016-08-31
    • 2017-01-08
    • 1970-01-01
    • 1970-01-01
    • 2020-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多