Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/reactjs/26.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Reactjs setState被回调执行阻止_Reactjs - Fatal编程技术网

Reactjs setState被回调执行阻止

Reactjs setState被回调执行阻止,reactjs,Reactjs,我正在做一个React项目,并制作了自己的手风琴组件。在应用程序的一个页面上,我需要呈现一个手风琴列表,当点击打开标题时,必须从API获取每个手风琴的内容。目前,我在accordion中有一个名为open的状态,我允许组件的用户传入两个回调:onClickOpen和onClickClose。有一个handleClick函数,用于设置状态,然后在setState回调中调用回调 我的问题是,似乎从未调用set state,因为当我控制台记录this.state.open的值时,它总是false。我假

我正在做一个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);