Bootstrap v4 弹出窗口不会在 React 中的状态更改时更新

Bootstrap v4 popover not updating on state change in React

我有一个 "Nightlife Coordination" 应用程序(来自 Free Code Camp 课程),允许用户按城市搜索并回复当天晚上的酒吧。该应用程序保留了一份名单,其中列出了谁收到了回复以及谁要去。它是用 React 和 Bootstrap v4(以及后端的 Node)构建的。

我在每个栏位置下都有文本,单击该文本后,用户可以回复或取消回复。还有一个按钮显示有多少人已回复,如果单击,将显示已回复的人员列表的 Bootstrap 弹出框。

如果用户回复(或未回复),我希望更新列表。 (目前,按钮上的 数字 会更新,但不会更新列表。)

下面两张图片说明了问题:

初始加载时,一切正常

当用户回复或取消回复时,按钮上的数字正确更新,但列表没有

这是我的代码。

正在渲染方法中第二个锚标记的 data-content 属性中生成列表。

有人可以帮忙吗?

另一个提示是,在我的 React 开发人员工具 Chrome 扩展中,它显示 data-content 属性在 RSVP 和 unRSVP 时正确更新。是不是 Bootstrap 在初始渲染时将 data-content 属性的内容保存在其 JS 文件中并且不更新它?

const React = require('react');

class Bar extends React.Component {  
  constructor(props) {
    super(props);
    this.state = {
      countMeIn: false, // coming from Mongo
      numberGoing: this.props.user_namesArr.length,
      user_id: this.props.twitter_id,
      user_name: this.props.user_name,
      yelp_id: this.props.yelp_id,
      user_namesArr: this.props.user_namesArr
    };
  }

  componentDidMount() { // need the same for DidMount and DidUpdate, in case user is signed in upon load (from previous session), or signs in after load
    if (this.state.user_namesArr.includes(this.props.user_name) && !this.state.countMeIn) {
      this.setState({
        countMeIn: true
      });
    }

  }

  componentDidUpdate(prevProps, prevState) { // Need both in case user logs in after initial page load
    console.log(this.state.user_namesArr);
    if (this.state.user_namesArr.includes(this.props.user_name) && !prevState.countMeIn) {
      this.setState({
        countMeIn: true
      });
    }
    $('[data-toggle="popover"]').popover();
  }


  rsvp() {
    let url = '/rsvp/?&yelp_id=' + this.props.yelp_id + '&user_id=' + this.props.twitter_id + '&user_name=' + this.props.user_name;
    fetch(url, { method: "POST" })
    .then((res) => res.json())
    .then((json) => {
      let newArr = this.state.user_namesArr;
      newArr.push(this.props.user_name);
      this.setState({
        numberGoing: this.state.numberGoing + 1,
        countMeIn: true,
        user_namesArr: newArr,
      });
    })
  }

  unrsvp() {
    let url = '/unrsvp/?&yelp_id=' + this.props.yelp_id + '&user_id=' + this.props.twitter_id + '&user_name=' + this.props.user_name;
    fetch(url, { method: "POST" })
    .then((res) => res.json())
    .then((json) => {
      let ind = this.state.user_namesArr.indexOf(this.props.user_name);
      let newArr = this.state.user_namesArr;
      newArr.splice(ind, 1);
      this.setState({
        numberGoing: this.state.numberGoing - 1,
        countMeIn: false,
        user_namesArr: newArr,
      });
    })
  }

  render() {
    return (
      <div className="col-lg-4 onecomponent">
        <a href={ this.props.bar_yelp_url } target="_blank">
        <div className="barname text-center">
          { this.props.name }
        </div>
        <div className="priceline">
          <img className="stars" src={ this.state.starsUrl } /> { this.props.review_count } reviews <span className="price">{ this.props.price }</span>
        </div>
        <div className="image">
          <img class="mainimg" src={ this.props.image_url } />
        </div>
        <div className="address text-center">
          { this.props.loc[0] }., { this.props.loc[1] }
        </div>
        </a>
        <hr/>
        <div className="text-center">
          <a tabindex="0" role="button" className="btn btn-success" data-toggle={ this.state.user_namesArr.length > 0 ? "popover" : "" } data-trigger="focus" title="Who's In?" data-content={ this.state.user_namesArr }>
            { this.state.numberGoing } going
          </a>
          {
            this.props.loggedIn ?
            this.state.countMeIn ?
            <span className="going" onClick={ () => this.unrsvp() }>You're going!</span> : // if logged in and already RSVP'd
            <span className="rsvpdetails" onClick={ () => this.rsvp() }>Count me in!</span> : // if logged in but not yet RSVP'd
            <span> Please log in </span> // if not logged in
          }
        </div>
      </div>
    )
  }

}

module.exports = Bar;

也许使用 ref 会有所帮助...但为什么不使用 reactstrap 更重要的是为什么不使用 react-popper ...?众所周知 (https://github.com/FezVrasta/popper.js/#react-vuejs-angular-angularjs-emberjs-etc-integration) 许多库不能很好地与 React 或任何其他(虚拟)DOM 管理器一起工作。

你真的需要jQuery吗?

使用 React 门户,您可以删除所有这些依赖项。

它适用于 Reactstrap。我只是将 reactstrap 添加到我的 package.json 文件中,并使用了 Reactstrap 代码。

const React = require('react');
import { Button, Popover, PopoverHeader, PopoverBody } from 'reactstrap';

class Bar extends React.Component {  
  constructor(props) {
    super(props);
    this.state = {
      countMeIn: false, // coming from Mongo
      numberGoing: this.props.user_namesArr.length,
      user_id: this.props.twitter_id,
      user_name: this.props.user_name,
      yelp_id: this.props.yelp_id,
      user_namesArr: this.props.user_namesArr,
      popover: false
    };
    this.toggle = this.toggle.bind(this);
  }

  componentDidMount() { // need the same for DidMount and DidUpdate, in case user is signed in upon load (from previous session), or signs in after load
    if (this.state.user_namesArr.includes(this.props.user_name) && !this.state.countMeIn) {
      this.setState({
        countMeIn: true
      });
    }        
  }

  componentDidUpdate(prevProps, prevState) {
    if (this.state.user_namesArr.includes(this.props.user_name) && !prevState.countMeIn) {
      this.setState({
        countMeIn: true
      });
    }
  }


  rsvp() {
    let url = '/rsvp/?&yelp_id=' + this.props.yelp_id + '&user_id=' + this.props.twitter_id + '&user_name=' + this.props.user_name;
    fetch(url, { method: "POST" })
    .then((res) => res.json())
    .then((json) => {
      let newArr = this.state.user_namesArr;
      newArr.push(this.props.user_name);
      this.setState({
        user_namesArr: newArr,
        numberGoing: this.state.numberGoing + 1,
        countMeIn: true
      });
    })
  }

  unrsvp() {
    let url = '/unrsvp/?&yelp_id=' + this.props.yelp_id + '&user_id=' + this.props.twitter_id + '&user_name=' + this.props.user_name;
    fetch(url, { method: "POST" })
    .then((res) => res.json())
    .then((json) => {
      let ind = this.state.user_namesArr.indexOf(this.props.user_name);
      let newArr = this.state.user_namesArr;
      newArr.splice(ind, 1);
      this.setState({
        user_namesArr: newArr,
        numberGoing: this.state.numberGoing - 1,
        countMeIn: false
      });
    })
  }

  toggle() {
    this.setState({
      popover: !this.state.popover
    });
  }

  render() {
    return (
      <div className="col-lg-4 onecomponent">
        <a href={ this.props.bar_yelp_url } target="_blank">
        <div className="barname text-center">
          { this.props.name }
        </div>
        <div className="priceline">
          <img className="stars" src={ this.state.starsUrl } /> { this.props.review_count } reviews <span className="price">{ this.props.price }</span>
        </div>
        <div className="image">
          <img class="mainimg" src={ this.props.image_url } />
        </div>
        <div className="address text-center">
          { this.props.loc[0] }., { this.props.loc[1] }
        </div>
        </a>
        <hr/>
        <div className="text-center">
          { /* For this to work, id must have leading letters, otherwise throws massive errors. See here:  */ }
          <Button id={ "abc" + this.props.yelp_id } className="btn btn-success" onClick={ this.toggle }>{ this.state.numberGoing } going</Button>
          <Popover placement="right" isOpen={ this.state.popover } target={ "abc" + this.props.yelp_id } toggle={ this.toggle }>
            <PopoverHeader>Who's In?</PopoverHeader>
            <PopoverBody>{ this.state.user_namesArr }</PopoverBody>
          </Popover>
          {
            this.props.loggedIn ?
            this.state.countMeIn ?
            <span className="going" onClick={ () => this.unrsvp() }>You're going!</span> : // if logged in and already RSVP'd
            <span className="rsvpdetails" onClick={ () => this.rsvp() }>Count me in!</span> : // if logged in but not yet RSVP'd
            <span> Please log in </span> // if not logged in
          }
        </div>
      </div>
    )
  }

}

module.exports = Bar;