在ReactJS中,组件状态是绑定到该组件的一个实例还是它绑定到该组件的所有实例?

时间:2019-03-01 10:01:56

标签: javascript reactjs state

我正在尝试在页面上显示组件的多个实例。每个实例都应出现在Tab.Pane(语义UI功能)中。但是,在尝试实现这一点时,我遇到了一些奇怪的行为,如下所述。

我有一个组成部分; <MultistepFilterSection />

在此组件中,状态为:

this.state = {
    conjunction: "AND" // default
}

此状态由组件中的onClick处理函数修改;

handleConjunctionButtonClick = (conjunction, e) => {
    this.setState({conjunction: conjunction})
}

此onClick是通过单击两个按钮之一触发的:

<Button.Group vertical>
    <Button 
        onClick={(e) => this.handleConjunctionButtonClick("AND")}
        color={this.state.conjunction === "AND" ? "blue" : null}
    >   
        And
    </Button>
    <Button
        onClick={(e) => this.handleConjunctionButtonClick("OR")}
        color={this.state.conjunction === "OR" ? "blue" : null}
    >
        Or
    </Button>
</Button.Group>

我正在使用Semantic UI's Tab component在页面上呈现此组件的3个实例;

    const panes = [
        { 
            menuItem: "Panel Data", 
            render: () => 
                <Tab.Pane attached={false}>
                    <MultistepFilterSection getAudience={this.getAudience} />
                </Tab.Pane>
        },
        { 
            menuItem: "Geolocation Data", 
            render: () => 
                <Tab.Pane attached={false}>
                    <MultistepFilterSection getAudience={this.getAudience} />
                </Tab.Pane>
        },
        { 
            menuItem: "Combined Audience Selection", 
            render: () => 
                <Tab.Pane attached={false}>
                    <MultistepFilterSection getAudience={this.getAudience} />
                </Tab.Pane>
        }
    ]

在render方法中,我有:

                <Tab 
                    menu={{ secondary: true, pointing: true }} 
                    panes={panes} 
                />

在任意一个选项卡中触发onClick处理程序都会更改组件所有实例中的状态。这正常吗?我认为组件的一个实例中的组件状态是该实例专有的。

在进一步的调查中,我发现当我使用语义UI的Tab组件实例化<MultistepFilterSection />的多个实例时,便表现出这种行为。当单独渲染实例时,展览的预期行为。

<MultistepFilterSection />组件的完整代码:

import React from "react";
import { Grid, Button, Container, Icon, Segment } from "semantic-ui-react";
import { connect } from "react-redux";
import uuid from "uuid";
import _ from "lodash";

import RuleGroup from "../rules/RuleGroup";
import { filterGroupCreated, filterGroupDeleted, filterDeleted } from "../../actions/FiltersAction"

export class MultistepFilterSection extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            conjunction: "AND" // default
        }

        // create one default group
        this.props.filterGroupCreated({filterGroupId: uuid()})

        this.handleAddRuleGroupButtonClick = this.handleAddRuleGroupButtonClick.bind(this);
        this.renderRuleGroups = this.renderRuleGroups.bind(this);
        this.handleConjunctionButtonClick.bind(this)
    }

    handleAddRuleGroupButtonClick() {
        // console.log("called handleAddRuleGroupButtonClick()")
        const filterGroupId = uuid()
        let data = {}
        data.filterGroupId = filterGroupId

        this.props.filterGroupCreated(data)
    }

    handleConjunctionButtonClick = (conjunction, e) => {
        this.setState({conjunction: conjunction})
    }

    renderRuleGroups() {
        // console.log("called renderRuleGroups()")
        return Object.values(this.props.filterGroups).map(function(ruleGroup) {
            const key = ruleGroup.id;

            // incomplete
            let currentGenderSelected

            return (
                <React.Fragment key={key}>
                    <RuleGroup
                        id={key} 
                        key={key} 
                        deleteRuleGroup={this.deleteRuleGroup}
                        deleteFilter={this.deleteFilter}
                        currentGenderSelected={currentGenderSelected}
                    />
                </React.Fragment>

            )
        }.bind(this))
    }

    deleteRuleGroup = (id, e) => {
        // console.log("delete rule group called")

        this.props.filterGroupDeleted({filterGroupId: id})
    }

    deleteFilter = (filterGroupId, filterId, e) => {
        // console.log("deleteFilter() called")

        this.props.filterDeleted({
            filterGroupId: filterGroupId, 
            filterId: filterId
        })
    }

    render() {
        const isFilterGroupQuery = true
        return(
            <Grid  padded="vertically">
                <Grid.Row stretched>
                    <Grid.Column  verticalAlign={"middle"} width={2}>
                        {_.size(this.props.filterGroups) > 0 &&
                            <Segment basic>
                                <Button.Group vertical>
                                    <Button 
                                        onClick={(e) => this.handleConjunctionButtonClick("AND")}
                                        color={this.state.conjunction === "AND" ? "blue" : null}
                                    >   
                                        And
                                    </Button>
                                    <Button
                                        onClick={(e) => this.handleConjunctionButtonClick("OR")}
                                        color={this.state.conjunction === "OR" ? "blue" : null}
                                    >
                                        Or
                                    </Button>
                                </Button.Group>
                            </Segment>
                        }
                    </Grid.Column>
                    <Grid.Column width={14}>
                        {this.renderRuleGroups()}
                    </Grid.Column>
                </Grid.Row>
                <Grid.Row>
                    <Grid.Column width={2}></Grid.Column>
                    <Grid.Column width={3}>
                        <Button
                            color="grey" 
                            basic 
                            onClick={this.handleAddRuleGroupButtonClick}
                        >
                            <Icon name="plus" />Add Rule Group
                        </Button>
                    </Grid.Column>
                    <Grid.Column width={11}>
                        <Button 
                            color="blue"
                            onClick={
                                (isFilterGroupQuery) => this.props.getAudience(
                                    isFilterGroupQuery, this.state.conjunction
                                )
                            }
                            floated={"right"} 
                        >
                            Run query
                        </Button>
                    </Grid.Column>

                </Grid.Row>
            </Grid>

        )
    }
}

function mapStateToProps(state) {
    return {
        filterGroups: state.filters
    };
}

export default connect(
    mapStateToProps, 
    {
        filterGroupCreated,
        filterGroupDeleted,
        filterDeleted
    }
)(MultistepFilterSection);

1 个答案:

答案 0 :(得分:0)

经过大量挖掘,最后在posting an issue上进行了Semantic UI的GitHub回购,我了解到我需要在Tab组件上使用renderActiveOnly={false}道具。我还必须更改标记,以便窗格对象采用以下格式:

  const panes = [
        { 
            key: 1,
            menuItem: "Panel Data", 
            pane: <Tab.Pane><Simple key={1} /></Tab.Pane>
        },
        { 
            key: 2,
            menuItem: "Geolocation Data", 
            pane: <Tab.Pane><Simple key={2} /></Tab.Pane>
        }
    ]