Я пытаюсь отобразить несколько экземпляров компонента на странице. Каждый экземпляр должен отображаться в 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);
Вам необходимо хранить и управлять состоянием в родительском компоненте, в котором находится MultistepFilterSection. Оттуда вы передаете функцию нажатия кнопки в каждый компонент. Идея заключается в том, что если какие-либо компоненты MultistepFilterSection вызываются при щелчке, то состояние обновляется только в родительском компоненте. Вы должны взглянуть на документы React, поскольку они превосходны. Следующая ссылка описывает именно то, что вы хотите сделать. reactjs.org/docs/lifting-state-up.html
это ненормально, состояние должно обновляться только для компонента, на который нажали, пожалуйста, поделитесь своим кодом, это поможет нам предложить исправление.
Где вы связываете обработчик onClick handleConjunctionButtonClick() , укажите этот код.
Поделитесь полным кодом компонента MultiStepFilterSection.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


После долгих поисков и, в конце концов, размещение проблемы в репозитории 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>
}
]
Это определенно ненормально,
stateпредназначен для экземпляра компонента и не должен использоваться совместно. Возможно, вам следует поделиться своим кодом, если вам нужна дополнительная помощь.