title: setState being blocked by callback execution
body: <p>I am working on a React project and have made my own accordion component. On one page in the app, I need to render a list of accordions and the content of each one must be fetched from an API when the header is clicked open. Currently, I have a piece of state called open in my accordion and I allow the user of the component to pass in two callbacks: onClickOpen and onClickClose. THere is a handleClick function that sets the state and then calls the callback in the setState callback. </p>
  
  <p>My issue is that, it seems like set state is never called because when I console log the value of this.state.open it is always false. I'm assuming there is something happening with the callback but I'm not sure what.  </p>
  
  <p>Accordion Component (called Section): </p>
  
  <pre><code>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: () =&gt; {},
      data: {},
      onClickOpen: () =&gt; {},
      onClickClose: () =&gt; {},
    };
  
    state = {
      open: false,
    };
  
    handleClick = () =&gt; {
      if (this.props.noExpand) return;
  
      if (this.state.open) {
        this.setState({ open: false }, () =&gt; {
          if (this.props.onClickClose) this.props.onClickClose();
        });
      } else {
        this.setState({ open: true }, () =&gt; {
          if (this.props.onClickOpen) this.props.onClickOpen();
        });
      }
    }
  
    renderHeadings() {
      return this.props.titles.map((title, i) =&gt; {
        return (
          &lt;Heading width={this.props.widths ? this.props.widths[i] : null} fontSize={this.props.fontSize} fontWeight={this.props.fontWeight} key={shortid.generate()}&gt;
            {
              this.props.showSecondButton &amp;&amp;
              (
                &lt;Button onClick={() =&gt; this.props.onSecondButtonClick(this.props.data)}&gt;
                  &lt;Icon className="material-icons md-32" color={this.props.secondIconColor}&gt;{this.props.secondIconName}&lt;/Icon&gt;
                &lt;/Button&gt;
              )
            }
            {title}
          &lt;/Heading&gt;
        );
      });
    }
  
    render() {
      return (
        &lt;SectionContainer&gt;
          { !this.props.noExpand &amp;&amp; &lt;ToggleButton color={this.props.color} open={this.state.open} /&gt;}
          &lt;HeaderContainer open={this.state.open} onClick={() =&gt; this.handleClick()}&gt;
            {this.renderHeadings()}
          &lt;/HeaderContainer&gt;
          &lt;BodyContainer open={this.state.open}&gt;
            &lt;Body&gt;
              {this.props.children}
            &lt;/Body&gt;
          &lt;/BodyContainer&gt;
        &lt;/SectionContainer&gt;
      );
    }
  }
  
  export default Section;
  
  </code></pre>
  
  <p>Styles for styled components: </p>
  
  <pre><code>import styled from 'styled-components';
  
  import { colors } from '../../../theme/vars';
  
  import { themes as themeTypes } from '../../../types';
  
  export const SectionContainer = styled.div(props =&gt; ({
    boxSizing: 'border-box',
    position: 'relative',
    width: '100%',
    borderBottom: `1px solid ${colors.SLATE_BLUE_20}`,
    borderLeft: props.open === true ? `1px solid ${colors.MOSS_GREEN_FOCUS_13}` : 'none',
    borderRight: props.open === true ? `1px solid ${colors.MOSS_GREEN_FOCUS_13}` : 'none',
  }));
  
  export const ToggleButton = styled.button`
    box-sizing: border-box;
    position: absolute;
    right: 0;
    margin: 0;
    padding: 0;
    height: 3em;
    width: 3em;
    outline: 0;
    border: 0;
    background: none;
    text-indent: -9999%;
    pointer-events: none;
    font-size: 14px;
    line-height: 18px;
    &amp;:before {
        content: '';
        display: block;
        position: absolute;
        height: 12px;
        width: 4px;
        border-radius: .3em;
        background: ${props =&gt; props.color || colors.MOSS_GREEN_100};
        transform-origin: 50%;
        top: 50%;
        left: 50%;
        transition: all .25s ease-in-out;
        transform: ${props =&gt; props.open === true ? `translate(0%, -50%) rotate(-45deg)` : `translate(75%, -50%) rotate(45deg)` };
      }
    &amp;:after {
        content: '';
        display: block;
        position: absolute;
        height: 12px;
        width: 4px;
        border-radius: .3em;
        background: ${props =&gt; props.color || colors.MOSS_GREEN_100};
        transform-origin: 50%;
        top: 50%;
        left: 50%;
        transition: all .25s ease-in-out;
        transform: ${props =&gt; props.open === true ? `translate(0%, -50%) rotate(45deg)` : `translate(-75%, -50%) rotate(-45deg)`};
      }
  `;
  
  export const HeaderContainer = styled.div`
    box-sizing: border-box;
    display: flex;
    justify-content: flex-start;
    align-items: center;
    flex-direction: row;
    flex-wrap: nowrap;
    padding-left: 16px;
    height: 40px;
    width: 100%;
    overflow: hidden;
    cursor: pointer;
    white-space: nowrap;
    text-overflow: ellipsis;
    color: ${colors.SLATE_BLUE_100};
    background: ${props =&gt; props.open === true ? colors.MOSS_GREEN_FOCUS_13 : colors.WHITE};
    border-bottom: 1px solid ${colors.SLATE_BLUE_20};
  `;
  
  export const Heading = styled.div(props =&gt; ({
    boxSizing: 'border-box',
    display: 'flex',
    flexDirection: 'row',
    justifyContent: 'space-between',
    verticalAlign: 'center',
    width: props.width || '25%',
    fontSize: `${props.fontSize || 14}px`,
    fontWeight: props.fontWeight || 400,
    lineHeight: '32px',
    color: colors.SLATE_BLUE_100,
  }));
  
  
  export const BodyContainer = styled.div(props =&gt; ({
    boxSizing: 'border-box',
    overflow: 'hidden',
    height: props.open === true ? 'auto' : '0px',  // how to transition this...
    transition: 'all .2s ease-in',
    borderLeft: props.open === true ? `1px solid ${colors.MOSS_GREEN_FOCUS_13}` : 'none',
    borderRight: props.open === true ? `1px solid ${colors.MOSS_GREEN_FOCUS_13}` : 'none',
    borderBottom: props.open === true ? `2px solid ${colors.MOSS_GREEN_100}` : 'none',
    background: colors.WHITE,
  }));
  
  export const Body = styled.div(props =&gt; ({
    boxSizing: 'border-box',
    padding: '1em',
    color: '#333',
    lineHeight: '1.3',
  }));
  
  export const Icon = styled.i`
    color: ${props =&gt; props.color};
    font-size: '32px';
    margin-top: auto;
  `;
  
  export const Button = styled.button`
    display: flex;
    flex-direction: row;
    align-self: center;
    margin-right: 10px;
    background: none;
    border: none;
  `;
  
  </code></pre>
  
  <p>I use the Section component like this: </p>
  
  <pre><code>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) =&gt; {
    // the callback that is called from the Section handleClick
      this.props.fetchSelectedNode({ type: nodeTypes.MODEL_ELEMENT, id: nodeId });
    };
  
    onClickClose = () =&gt; {
      this.props.clearSelectedNode();
    };
  
    renderMetaDataPage() {
      if (this.props.isFetchingSelectedNode) {
        return 'Loading...';
      }
      if (this.props.selectedNode) {
        // this component requires data from API to render
        return (
          &lt;ModelElementMetaDataPage
            modelElement={this.props.selectedNode}
          /&gt;
        );
      }
      return null;
    }
  
  
    renderSeries() {
      return this.props.row.series.map((series) =&gt; {
        return (
          &lt;Section
            key={series.id}
            titles={[`${series.name} - ${series.tagName}`]}
            onClickOpen={() =&gt; this.onClickOpen(series.id)}
            onClickClose={() =&gt; this.onClickClose()}
            fontSize={18}
            fontWeight={500}
          &gt;
            {this.renderMetaDataPage()}
          &lt;/Section&gt;
        );
      });
    }
  
    render() {
      return (
        &lt;AccordionContainer&gt;
          {this.renderSeries()}
        &lt;/AccordionContainer&gt;
      );
    }
  }
  
  const mapStateToProps = state =&gt; ({
    state,
    selectedNode: selectors.selectedNode(state),
    isFetchingSelectedNode: selectors.isFetchingSelectedNode(state),
  });
  
  const mapDispatchToProps = dispatch =&gt; ({
    clearSelectedNode: () =&gt; dispatch(actions.clearSelectedNode()),
    fetchSelectedNode: (nodeType, id) =&gt; dispatch(actions.fetchSelectedNode(nodeType, id)),
  });
  
  export default connect(mapStateToProps, mapDispatchToProps)(MEPlotAccordion);
  </code></pre>