В ReactJS состояние компонента привязано к экземпляру компонента или оно привязано ко всем экземплярам компонента?

Я пытаюсь отобразить несколько экземпляров компонента на странице. Каждый экземпляр должен отображаться в Tab.Pane (функция семантического пользовательского интерфейса). Однако в моей попытке добиться этого я получаю странное поведение, как более подробно описано ниже.

У меня есть компонент; <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>

Я использую Компонент Tab семантического пользовательского интерфейса для отображения 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>
        }
    ]

В методе рендеринга у меня есть:

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

Запуск обработчика onClick на любой из вкладок изменяет состояние во всех экземплярах компонента. Это нормально? Я думал, что состояние компонента в одном экземпляре компонента является эксклюзивным для этого экземпляра.

При дальнейшем расследовании я обнаружил, что поведение проявляется, когда я создаю несколько экземпляров <MultistepFilterSection /> с помощью компонента Tab семантического пользовательского интерфейса. При самостоятельном рендеринге экземпляры демонстрируют ожидаемое поведение.

Полный код компонента <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.info("called handleAddRuleGroupButtonClick()")
        const filterGroupId = uuid()
        let data = {}
        data.filterGroupId = filterGroupId

        this.props.filterGroupCreated(data)
    }

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

    renderRuleGroups() {
        // console.info("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.info("delete rule group called")

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

    deleteFilter = (filterGroupId, filterId, e) => {
        // console.info("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);

Это определенно ненормально, state предназначен для экземпляра компонента и не должен использоваться совместно. Возможно, вам следует поделиться своим кодом, если вам нужна дополнительная помощь.

sjahan 01.03.2019 11:04

Вам необходимо хранить и управлять состоянием в родительском компоненте, в котором находится MultistepFilterSection. Оттуда вы передаете функцию нажатия кнопки в каждый компонент. Идея заключается в том, что если какие-либо компоненты MultistepFilterSection вызываются при щелчке, то состояние обновляется только в родительском компоненте. Вы должны взглянуть на документы React, поскольку они превосходны. Следующая ссылка описывает именно то, что вы хотите сделать. reactjs.org/docs/lifting-state-up.html

Chris Adams 01.03.2019 11:09

это ненормально, состояние должно обновляться только для компонента, на который нажали, пожалуйста, поделитесь своим кодом, это поможет нам предложить исправление.

Praveen Rao Chavan.G 01.03.2019 11:26

Где вы связываете обработчик onClick handleConjunctionButtonClick() , укажите этот код.

Praveen Rao Chavan.G 01.03.2019 11:28

Поделитесь полным кодом компонента MultiStepFilterSection.

hazardous 01.03.2019 12:26
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
5
129
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

После долгих поисков и, в конце концов, размещение проблемы в репозитории Semantic UI на GitHub, я узнал, что мне нужно использовать renderActiveOnly = {false} prop для компонента Tab. Мне также пришлось изменить свою разметку, чтобы объекты панелей были в следующем формате:

  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>
        }
    ]

Другие вопросы по теме