Почему состояние родительского компонента не изменяется, когда дочерний элемент вызывает родительскую функцию? - PullRequest
1 голос
/ 23 апреля 2019

Я использую модал начальной загрузки и при нажатии на элемент, который был открыт, но не могу закрыть его, когда я нажимаю «x» в правом углу модальной области. Проблема в том, что мне удается передать состояние с помощью реквизита от родителя к потомку, но когда я вызываю функцию «lgClose» внутри потомка, она переходит к родительскому компоненту, но фактически не меняет положение состояния на «ложь». Я вижу, как он идет внутри функции, потому что я поставил "console.log ('im in the function')", и я могу это увидеть. Почему состояние не изменилось на false в родительском компоненте?

import React, { Component } from 'react';
import { Link } from 'react-router-dom'
import { connect } from 'react-redux';
import { getAllUsers,getMessages } from './../actions';
import { bindActionCreators} from 'redux';
import { Button, Modal } from 'react-bootstrap';
import  ContainerModal  from './../components/popup_modal';

// parent component
class MainContainer extends Component {

  constructor(props, context) {
    super(props, context);

    this.state = {
      lgShow: false
    };
  }

  componentWillMount(){
    this.props.getAllUsers();
  }


  renderList = ({list}) => {
    if(list){
      return list.map((item)=>{
        return(
          <div key={item._id} className="item-list" onClick={() => this.setState({lgShow: true})}>
                            
              
              <div className="title">{item.firstName} {item.lastName}</div>
              <div className="body">{item.age}</div>

              <div>
                    
                <ContainerModal lgShow={this.state.lgShow} lgClose={this.lgClose}/>                                    

              </div>
              
          </div>
        )
      })
    }
  }

  lgClose = () => {
    this.setState({lgShow:false});
    console.log('im in the function');
  } 


  render() {
    console.log(this.state);

    return (
        <div className="App">
          <div className="top">
            <h3>Messages</h3>
            <Link to="/form">Add</Link>
          </div>
          <div className="messages_container">
            {this.renderList(this.props.users)}
          </div>           
              
        </div>
    );
  }
}

function mapStateToProps(state) {
  return {
      messages:state.messages,
      users:state.users
  }
}

function mapDispatchToProps (dispatch) { 
  return bindActionCreators({getAllUsers,getMessages},dispatch);
}


export default connect(mapStateToProps,mapDispatchToProps)(MainContainer);



import React from 'react';
import { Button, Modal } from 'react-bootstrap';

// child component
const ContainerModal = (props) => {
    
    return (
        <div>
            <Modal                  
                  size="lg"
                  show={props.lgShow}
                  onHide={ props.lgClose }
                  aria-labelledby="example-modal-sizes-title-lg"
            >

                  <Modal.Header closeButton>
                    <Modal.Title id="example-modal-sizes-title-lg">
                      Large Modal
                    </Modal.Title>
                  </Modal.Header>

                  <Modal.Body>item</Modal.Body>

            </Modal>

        </div>        
       
    )
}

export default ContainerModal;

Ответы [ 3 ]

0 голосов
/ 23 апреля 2019

В вашем ContainerModal попробуйте это:

import React from 'react';
import { Button, Modal } from 'react-bootstrap';

// child component
const ContainerModal = (props) => {

    return (
        <div>
            <Modal                  
                  {...props}
                  size="lg"
                  aria-labelledby="example-modal-sizes-title-lg"
            >

                  <Modal.Header closeButton>
                    <Modal.Title id="example-modal-sizes-title-lg">
                      Large Modal
                    </Modal.Title>
                  </Modal.Header>

                  <Modal.Body>item</Modal.Body>

            </Modal>

        </div>        

    )
}

export default ContainerModal;

А в renderList сделайте это:

 <ContainerModal show={this.state.lgShow} onHide={this.lgClose} />
0 голосов
/ 23 апреля 2019

Вот причина, по которой ваше lgShow остается верным.

Это объяснение будет немного длиннее.Поэтому, пожалуйста, потерпите меня.

Вот что вы сделали.

Вы добавили ContainerModal в качестве дочернего элемента div (className ="item-list") внутри renderList метода, к которому прикреплен слушатель onClick.Когда вы нажимаете на элемент списка, он работает нормально, и появляется модальный, но когда вы нажимаете на кнопку закрытия на модальных вещах, они становятся подозрительными.

Это происходит из-за того, что прослушиватель onClick div (className = "item-list") вызывается после того, как прослушиватель щелчков ContainerModal вызывается, устанавливая lgShow в true снова.Это поведение называется всплывающим событием , когда и родитель, и потомок получают событие снизу вверх (то есть, потомок, получающий первое событие, за которым следует родитель).Чтобы проверить это, добавьте console.log в прослушиватель onClick элемента div.

Вот ссылка на вопрос github для объяснения всплывающих событий

Решение

Первым и главным предложением было бы вывести ContainerModal из метода renderList в корневой div.Это потому, что сейчас для каждого вашего элемента списка создается ContainerModal, который не нужен.Например, если у вас есть 10 пользователей в списке, вы создаете 10 ContainerModal (Чтобы убедиться в этом, увеличьте количество пользователей, и вы можете увидеть, как оттенок модала становится темнее.)

Решение указанной выше проблемыВозьмите ContainerModal на корневой уровень, где он onClick не будет совпадать с onClick div (className = "item-list") в renderList, и ваша проблема будет решена.

Вот модифицированное решение.

import React, { Component } from 'react';
import { Link } from 'react-router-dom'
import { connect } from 'react-redux';
import { getAllUsers,getMessages } from './../actions';
import { bindActionCreators} from 'redux';
import { Button, Modal } from 'react-bootstrap';
import  ContainerModal  from './../components/popup_modal';

// parent component
class MainContainer extends Component {

  constructor(props, context) {
    super(props, context);

    this.state = {
      lgShow: false
    };
  }

  componentWillMount(){
    this.props.getAllUsers();
  }


  renderList = ({list}) => {
    if(list){
      return list.map((item)=>{
        return(
          <div key={item._id} className="item-list" onClick={() => this.setState({lgShow: true})}>
                            
              
              <div className="title">{item.firstName} {item.lastName}</div>
              <div className="body">{item.age}</div>

              <div>
                    
                {/* <ContainerModal lgShow={this.state.lgShow} lgClose={this.lgClose}/> */}                                

              </div>
              
          </div>
        )
      })
    }
  }

  lgClose = () => {
    this.setState({lgShow:false});
    console.log('im in the function');
  } 


  render() {
    console.log(this.state);

    return (
        <div className="App">
          <div className="top">
            <h3>Messages</h3>
            <Link to="/form">Add</Link>
          </div>
          <div className="messages_container">
            {this.renderList(this.props.users)}
          </div>           
          <ContainerModal lgShow={this.state.lgShow} lgClose={this.lgClose}/>
        </div>
    );
  }
}

function mapStateToProps(state) {
  return {
      messages:state.messages,
      users:state.users
  }
}

function mapDispatchToProps (dispatch) { 
  return bindActionCreators({getAllUsers,getMessages},dispatch);
}


export default connect(mapStateToProps,mapDispatchToProps)(MainContainer);



import React from 'react';
import { Button, Modal } from 'react-bootstrap';

// child component
const ContainerModal = (props) => {
    
    return (
        <div>
            <Modal                  
                  size="lg"
                  show={props.lgShow}
                  onHide={ props.lgClose }
                  aria-labelledby="example-modal-sizes-title-lg"
            >

                  <Modal.Header closeButton>
                    <Modal.Title id="example-modal-sizes-title-lg">
                      Large Modal
                    </Modal.Title>
                  </Modal.Header>

                  <Modal.Body>item</Modal.Body>

            </Modal>

        </div>        
       
    )
}

export default ContainerModal;

Обратите внимание на измененную позицию модального контейнера.Остальная часть кода в порядке.

Я также загрузил рабочее решение в изолированную программную среду кода.Посмотрите на это.

Edit xlxp61vy5q

Надеюсь, это поможет.

0 голосов
/ 23 апреля 2019

Кажется, что this в функции lgClose не указывает на MainContainer.Если это так, то один из способов решения этой проблемы - привязать функцию lgClose к MainContainer в конструкторе:

constructor(props, context) {
    super(props, context);

    this.state = {
      lgShow: false
    };

    this.lgClose = this.lgClose.bind(this); // Add this line
}

Надеюсь, это поможет:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...