Reactjs setState被回调执行阻止
我正在做一个React项目,并制作了自己的手风琴组件。在应用程序的一个页面上,我需要呈现一个手风琴列表,当点击打开标题时,必须从API获取每个手风琴的内容。目前,我在accordion中有一个名为open的状态,我允许组件的用户传入两个回调:onClickOpen和onClickClose。有一个handleClick函数,用于设置状态,然后在setState回调中调用回调 我的问题是,似乎从未调用set state,因为当我控制台记录this.state.open的值时,它总是false。我假设回调发生了什么,但我不确定是什么 手风琴组件称为节:Reactjs setState被回调执行阻止,reactjs,Reactjs,我正在做一个React项目,并制作了自己的手风琴组件。在应用程序的一个页面上,我需要呈现一个手风琴列表,当点击打开标题时,必须从API获取每个手风琴的内容。目前,我在accordion中有一个名为open的状态,我允许组件的用户传入两个回调:onClickOpen和onClickClose。有一个handleClick函数,用于设置状态,然后在setState回调中调用回调 我的问题是,似乎从未调用set state,因为当我控制台记录this.state.open的值时,它总是false。我假
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import shortid from 'shortid';
import {
SectionContainer,
ToggleButton,
HeaderContainer,
Heading,
BodyContainer,
Body,
Icon,
Button,
} from './sectionStyles';
class Section extends Component {
static propTypes = {
titles: PropTypes.array.isRequired,
children: PropTypes.node,
data: PropTypes.object,
noExpand: PropTypes.bool,
showSecondButton: PropTypes.bool,
onSecondButtonClick: PropTypes.func,
color: PropTypes.string,
widths: PropTypes.array,
fontSize: PropTypes.number,
fontWeight: PropTypes.number,
secondIconName: PropTypes.string,
secondIconColor: PropTypes.string,
onClickOpen: PropTypes.func,
onClickClose: PropTypes.func,
};
static defaultProps = {
children: null,
noExpand: false,
showSecondButton: false,
onSecondButtonClick: () => {},
data: {},
onClickOpen: () => {},
onClickClose: () => {},
};
state = {
open: false,
};
handleClick = () => {
if (this.props.noExpand) return;
if (this.state.open) {
this.setState({ open: false }, () => {
if (this.props.onClickClose) this.props.onClickClose();
});
} else {
this.setState({ open: true }, () => {
if (this.props.onClickOpen) this.props.onClickOpen();
});
}
}
renderHeadings() {
return this.props.titles.map((title, i) => {
return (
<Heading width={this.props.widths ? this.props.widths[i] : null} fontSize={this.props.fontSize} fontWeight={this.props.fontWeight} key={shortid.generate()}>
{
this.props.showSecondButton &&
(
<Button onClick={() => this.props.onSecondButtonClick(this.props.data)}>
<Icon className="material-icons md-32" color={this.props.secondIconColor}>{this.props.secondIconName}</Icon>
</Button>
)
}
{title}
</Heading>
);
});
}
render() {
return (
<SectionContainer>
{ !this.props.noExpand && <ToggleButton color={this.props.color} open={this.state.open} />}
<HeaderContainer open={this.state.open} onClick={() => this.handleClick()}>
{this.renderHeadings()}
</HeaderContainer>
<BodyContainer open={this.state.open}>
<Body>
{this.props.children}
</Body>
</BodyContainer>
</SectionContainer>
);
}
}
export default Section;
我使用的截面组件如下所示:
class MEPlotAccordion extends Component {
static propTypes = {
row: PropTypes.object.isRequired,
clearSelectedNode: PropTypes.func.isRequired,
fetchSelectedNode: PropTypes.func.isRequired,
selectedNode: PropTypes.object,
isFetchingSelectedNode: PropTypes.bool.isRequired,
};
static defaultProps = {
selectedNode: {},
};
onClickOpen = (nodeId) => {
// the callback that is called from the Section handleClick
this.props.fetchSelectedNode({ type: nodeTypes.MODEL_ELEMENT, id: nodeId });
};
onClickClose = () => {
this.props.clearSelectedNode();
};
renderMetaDataPage() {
if (this.props.isFetchingSelectedNode) {
return 'Loading...';
}
if (this.props.selectedNode) {
// this component requires data from API to render
return (
<ModelElementMetaDataPage
modelElement={this.props.selectedNode}
/>
);
}
return null;
}
renderSeries() {
return this.props.row.series.map((series) => {
return (
<Section
key={series.id}
titles={[`${series.name} - ${series.tagName}`]}
onClickOpen={() => this.onClickOpen(series.id)}
onClickClose={() => this.onClickClose()}
fontSize={18}
fontWeight={500}
>
{this.renderMetaDataPage()}
</Section>
);
});
}
render() {
return (
<AccordionContainer>
{this.renderSeries()}
</AccordionContainer>
);
}
}
const mapStateToProps = state => ({
state,
selectedNode: selectors.selectedNode(state),
isFetchingSelectedNode: selectors.isFetchingSelectedNode(state),
});
const mapDispatchToProps = dispatch => ({
clearSelectedNode: () => dispatch(actions.clearSelectedNode()),
fetchSelectedNode: (nodeType, id) => dispatch(actions.fetchSelectedNode(nodeType, id)),
});
export default connect(mapStateToProps, mapDispatchToProps)(MEPlotAccordion);
因此,在与问题作者讨论之后,问题是每个渲染上都有一个父组件正在卸载。卸载的原因是,组件在每次渲染时都被赋予了由shortid.generate生成的不同密钥。series.id从何而来?它的值是否总是在每次渲染时更改?它是series对象的一个属性。它不会更改每个渲染。您是否检查了在执行单击操作时是否正在卸载节组件?嘿,您说得对!正在卸载!为什么要卸载呢?我的假设是,您为每个渲染部分提供了不同的键。或者,节本身的父级正在卸载,因此它的所有子级也正在卸载。你得检查一下。您可以尝试检查是否正在卸载父级。如果不是,则检查您提供给节组件的键。
class MEPlotAccordion extends Component {
static propTypes = {
row: PropTypes.object.isRequired,
clearSelectedNode: PropTypes.func.isRequired,
fetchSelectedNode: PropTypes.func.isRequired,
selectedNode: PropTypes.object,
isFetchingSelectedNode: PropTypes.bool.isRequired,
};
static defaultProps = {
selectedNode: {},
};
onClickOpen = (nodeId) => {
// the callback that is called from the Section handleClick
this.props.fetchSelectedNode({ type: nodeTypes.MODEL_ELEMENT, id: nodeId });
};
onClickClose = () => {
this.props.clearSelectedNode();
};
renderMetaDataPage() {
if (this.props.isFetchingSelectedNode) {
return 'Loading...';
}
if (this.props.selectedNode) {
// this component requires data from API to render
return (
<ModelElementMetaDataPage
modelElement={this.props.selectedNode}
/>
);
}
return null;
}
renderSeries() {
return this.props.row.series.map((series) => {
return (
<Section
key={series.id}
titles={[`${series.name} - ${series.tagName}`]}
onClickOpen={() => this.onClickOpen(series.id)}
onClickClose={() => this.onClickClose()}
fontSize={18}
fontWeight={500}
>
{this.renderMetaDataPage()}
</Section>
);
});
}
render() {
return (
<AccordionContainer>
{this.renderSeries()}
</AccordionContainer>
);
}
}
const mapStateToProps = state => ({
state,
selectedNode: selectors.selectedNode(state),
isFetchingSelectedNode: selectors.isFetchingSelectedNode(state),
});
const mapDispatchToProps = dispatch => ({
clearSelectedNode: () => dispatch(actions.clearSelectedNode()),
fetchSelectedNode: (nodeType, id) => dispatch(actions.fetchSelectedNode(nodeType, id)),
});
export default connect(mapStateToProps, mapDispatchToProps)(MEPlotAccordion);