Ad

React Bootstrap State Does Not Persists When Opening/closing Popover

- 1 answer

I'm using React Bootstrap and I'm running into any issue while using OverlayTrigger/Popover. When I close the popover and reopen it the state of <ComponentWithState /> is discarded.

Live Example of the issue. To replicate: Click button, click link, notice how text changed from "State: Off" to "State: On". Close popover (click beside or re-click button), then re-click button to open popover. Notice how text says "State: Off". The state did not persist (does not say "State: On").

Idea how I can make the state persists between closing/reopening popover?

'use strict';

var React = require('react');
var Popover = require('react-bootstrap/lib/Popover');
var Button = require('react-bootstrap/lib/Button');
var OverlayTrigger = require('react-bootstrap/lib/OverlayTrigger');

var ComponentWithState = React.createClass({
  getInitialState: function() {
    return {
      value: false
    };
  },

  changeToTrue: function(e) {
    e.preventDefault();
    this.setState({value: true});
  },

  render: function() {
    return (
      <div>
        <div><a target="_blank" rel="nofollow noreferrer" href="#" onClick={this.changeToTrue}>Change state to On</a></div>
        <div>State: {this.state.value ? 'On' : 'Off'}</div>
      </div>
    );
  }
});

var App = React.createClass({
  render: function() {
    var children = (
      <Popover title="Something">
        <ComponentWithState />
      </Popover>
    );

    return (
      <OverlayTrigger trigger="click" rootClose overlay={children}>
        <Button>Options</Button>
      </OverlayTrigger>
    );
  }
});

React.render(
  <App />,
  document.getElementById('container')
);
Ad

Answer

If I understand your issue correctly, then I think the problem may lie where you're keeping your state.

getInitialState is run when the component first is created and only once. So every time you click the button again, your <ComponentWithState /> is umounted and state is lost. Then getInitialState is run again fresh when we click on the button again (defaulting to false).

What you probably want to do is move the state to the app component so that it can persist regardless of if the Overlay is opened or not. Then we just pass the click handler and state as props to the ComponentWithState and do the same logic with the ternary.

I wasn't able to test this, so let me know if you have any issues.

'use strict';

  var React = require('react');
  var Popover = require('react-bootstrap/lib/Popover');
  var Button = require('react-bootstrap/lib/Button');
  var OverlayTrigger = require('react-bootstrap/lib/OverlayTrigger');

  var ComponentWithState = React.createClass({

    propTypes: {
      stateValue: React.PropTypes.boolean,
      onLinkClick: React.PropTypes.func
    },

    render: function() {
      var stateText = (this.props.stateValue) ? 'On' : 'Off';

      return (
        <div>
          <div><a target="_blank" rel="nofollow noreferrer" href="#" onClick={this.props.onLinkClick}>Change state to On</a></div>
          <div>State: {stateText}</div>
        </div>
      );
    }
  });

  var App = React.createClass({

    getInitialState: function() {
      return {
        value: false
      };
    },

    render: function() {
      var children = (
        <Popover title="Something">
          <ComponentWithState onLinkClick={this.handleLinkClick} stateValue={this.state.value}/>
        </Popover>
      );

      return (
        <OverlayTrigger trigger="click" rootClose overlay={children}>
          <Button>Options</Button>
        </OverlayTrigger>
      );
    },

    handleLinkClick: function (event) {
      event.preventDefault();

      this.setState({value: !this.state.value});
    }
  });

  React.render(
    <App />,
    document.getElementById('container')
  );
Ad
source: stackoverflow.com
Ad