反应:不正确的模态组件在 axios 调用后被调用

React: Incorrect Modal component gets rendered called after axios call

我从 react-semantic-ui 中获取了一个模态组件,并将其配置为具有 confirm/alert 之类的对话框或传统模态。

我这样做主要是为了练习制作更有用和可重用的组件,并间接练习管理状态并将其移动到商店,即 redux...

import React, { Component } from 'react'
import { Button, Modal } from 'semantic-ui-react'
import PropTypes from 'prop-types'

import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { logOutUser  } from '../../store/reducers/users/index'
import { modalStateOn, modalStateOff  } from '../../store/reducers/ui/index'


class MyModal extends Component {

 close = () => {
  const { modalStateOff } = this.props
  modalStateOff();
 }

 logOutUser = () => {
  const { logOutUser } = this.props
  logOutUser()
 }

 render() {
  const { modalActive } = this.props

   return (
    <>
      <Modal dimmer={'blurring'} centered={true} size={'mini'} open={modalActive} onClose={this.close}>
        <Modal.Header>
         <p>{this.props.message}</p>
        </Modal.Header>
        <Modal.Actions>
         {this.props.isAlertModal ?
         <Button
          color='black'
          onClick={this.close}
          content={this.props.affirmativeUsed}
         />
         :
        <>
         <Button
           color='black'
           onClick={this.close}
          >
           No
          </Button>
          <Button
           positive
           icon='checkmark'
           labelPosition='right'
           content={this.props.affirmativeUsed}
           onClick={() => { this.close(); this.logOutUser() }}
          />
        </>
         }
        </Modal.Actions>
      </Modal>
    </>
   )
 }
}

MyModal.propTypes = {
 message: PropTypes.string,
 affirmativeUsed: PropTypes.string
}

function mapStateToProps(state) {
 const { ui } = state
 const { modalActive } = ui

 return { modalActive }
}
const mapDispatchToProps = dispatch =>
 bindActionCreators({ logOutUser, modalStateOn, modalStateOff }, dispatch)

export default connect(mapStateToProps, mapDispatchToProps)(MyModal)

这对于我想用于注销的模式非常有效:

=======================================
home | profile | *dashboard | logout          /* You get a Modal to confirm your desire to log out */
=======================================

但是在我的个人资料页面上,我创建了一个 ImageUploader 组件来处理该页面的加载图像,正如您现在可能已经猜到的那样,我希望在 axios 请求成功,一个请求失败,给一些反馈...

=======================================
home | *profile | dashboard | logout          /* You get a Modal to confirm your desire to log out */
=======================================

        -------------
       | choose file |                       /* AND!!! Get a Modal to confirm with a success OR failure!!
        -------------

这是 ImageUploader 组件:

import React, { Component } from 'react';
import './ImageUploader.css';
import FooModal from '../Modal/MyModal' 
import axios from 'axios';

import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { loadAvatar } from '../../store/reducers/users/index'
import { modalStateOn, modalStateOff } from '../../store/reducers/ui/index'

class ImageUploader extends Component {
 constructor(props) {
  super(props);
    this.uploadImage = this.uploadImage.bind(this);
 }

 componentDidUpdate(previousProps, previousState) {
  if (previousProps.userAvatar !== this.props.userAvatar) {
  console.log("this.props.userAvatar in componentDidUpdate", this.props.userAvatar);
   loadAvatarImage(this.props.userAvatar)
  }
 }

 setDefaultImage(){
  var defaultImage =  '../../static/profile-avatars/assets/default-img.jpg';
  this.loadAvatarImage(defaultImage)
 }

 loadAvatarImage(img) {
  var { loadAvatar } = this.props;
  loadAvatar(img)
 }

 uploadImage(e, method) {

  const { modalStateOn } = this.props
  console.log('this.props in ImageUploader uploadImageFunction', this.props)

  if (method === "multer") {

   let imageFormObj = new FormData();

   imageFormObj.append("imageName", "multer-image-" + Date.now());
   imageFormObj.append("imageData", e.target.files[0]);

   this.loadAvatarImage(window.URL.createObjectURL(e.target.files[0]))

   var config = { headers: { 'content-type': 'multipart/form-data' }}
   axios.post(`http://localhost:8016/images/uploadmulter`, imageFormObj, config )
    .then((data) => {
     if (data.data.success) {
      console.log("data ", data);
      modalStateOn();
     return (
       <FooModal
       isAlertModal={true}
       open={true}
       affirmativeUsed="Yes"
       message="Your image has been uploaded succesfully"
      />
     )
     }
    })
    .catch((err) => {
     alert("Error while uploading image using multer");
      this.setDefaultImage();
    });
  }
    e.stopPropagation();

 }

 render() {
  var {  userAvatar } = this.props
  return (
   <>
    <div className="main-container">
     <h3 className="main-heading">Image Upload App</h3>

     <div className="image-container">
      <div className="process">
       <h4 className="process__heading">Process: Using Multer</h4>
       <p className="process__details">Upload image to a node server, connected to a MongoDB database, with the help of multer</p>
       <form action="/uploadmulter" method="post" encType="multipart/form-data" >
        <input type="file" name="avatar" className="process__upload-btn"
         onChange={(e) => {
          this.uploadImage(e, "multer");
        }} />
        <img src={userAvatar} alt="upload-image" className="process__image" />
       </form>
      </div>
     </div>
    </div>
  </>
  );
 }
}

function mapStateToProps(state) {
 const { ui, users } = state
 const { userAvatar } = users
 const { modalActive } = ui

 return { userAvatar, modalActive }
}

const mapDispatchToProps = dispatch =>
 bindActionCreators({ loadAvatar, modalStateOn }, dispatch)

export default connect(mapStateToProps, mapDispatchToProps)(ImageUploader)

有趣的是,注销操作的 Modal 被呈现,而不是 ImageUploader???

中的呈现

AND

当我将 prop 值传递给 isAlertModal 时,它被忽略了?!

  <FooModal
   isAlertModal={true}
   open={true}
   affirmativeUsed="Yes"
   message="Your image has been uploaded succesfully"
  />

所以我想也许我必须卸载导航中的 Modal 以允许图像加载器 Modal 传播?

*请记住,uploadImage 函数调用了 e.stopPropagation();,但没有骰子!

如有任何帮助,我们将不胜感激!

更新

正如 Eneias 所建议的,此组件 (ImageModal) 的渲染在渲染函数中进行。现在这行得通了,但是 Modal 正在为注销功能呈现。

您的 FooModal 应该在渲染函数而不是回调函数上。

每次您的状态或道具发生变化时都会调用渲染函数。所以,你应该在你的状态 isUploaded 中有一个标志,或者类似的东西,并将它用作呈现你的 FooModal.

的条件

在 post 方法的回调函数中,您只需更新状态即可。它将以新的状态再次触发渲染函数。

所以我发现了问题所在:

1。我分解 Modal 组件的方式 不正确!!!

 <Modal dimmer={'blurring'} centered={true} size={'mini'} open={modalActive} onClose={this.close}>
  <Modal.Header>
   <p>{this.props.message}</p>
  </Modal.Header>
    <Modal.Actions>
     {this.props.isAlertModal ?
      <Button
       color='black'
       onClick={this.close}
       content={this.props.affirmativeUsed}
      />
     :
     <>
      <Button
       color='black'
       onClick={this.close}
      >
       No
      </Button>
      <Button
       positive
       icon='checkmark'
       labelPosition='right'
       content={this.props.affirmativeUsed}
       onClick={() => { this.close(); this.logOutUser() }}
      />
     </>
    }
   </Modal.Actions>
 </Modal>

只有一个 <Model.Header>this.props.message<Modal.Actions> 服务两个出在三元中:

应该这样写:

    <>
     <Modal dimmer={'blurring'} centered={true} size={'mini'} open={open} onClose={this.close}>
      {avatarModalActive === true ?
       <>
        <Modal.Header>
         <p>{this.props.message}</p>
        </Modal.Header>
         <Modal.Actions>
         <Button
          color='black'
          onClick={()=> {this.close()}}
          content={this.props.affirmativeUsed}
         />
         </Modal.Actions>
       </>
         :
        <>
        <Modal.Header>
         <p>{this.props.message}</p>
        </Modal.Header>
        <Modal.Actions>
         <Button
          color='black'
          onClick={this.close}
         >
           No
         </Button>
         <Button
          positive
          icon='checkmark'
          labelPosition='right'
          content={this.props.affirmativeUsed}
          onClick={() => {this.close(); this.logOutUser()}}
         />
         </Modal.Actions>
        </>
         }
      </Modal>
    </>

 And I would up realizing I had one piece of `state` serving both Modals.

    const { avatarModalActive, modalActive } = this.props /* Now there are two pieces of state managing each modal */
      var open;
    
      if (avatarModalActive) open = avatarModalActive 
      else open = modalActive 

This is the complete Modal File:

import React, { PureComponent, Component } from 'react'
import { Button, Modal } from 'semantic-ui-react'
import PropTypes from 'prop-types'

import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { logOutUser  } from '../../store/reducers/users/index'
import { avatarModalStateOff, modalStateOff  } from '../../store/reducers/ui/index'


class MyModal extends PureComponent {
 constructor(props) {
  super(props);
  this.state = {
   isAlertModal: this.props.isAlertModal
  }
 }

 componentDidMount() {
  const { isAlertModal } = this.props;
  this.setState({ isAlertModal: isAlertModal})
 }

 close = () => {
  if (this.props.avatarModalActive){
   this.props.avatarModalStateOff();
  }
  this.props.modalStateOff();
 }

 logOutUser = () => {
  const { logOutUser } = this.props
  logOutUser()
 }

 render() {
  const { avatarModalActive, modalActive } = this.props
  var open;

  if (avatarModalActive) open = avatarModalActive
  else open = modalActive

   return (
    <>
     <Modal dimmer={'blurring'} centered={true} size={'mini'} open={open} onClose={this.close}>
      {avatarModalActive === true ?
       <>
        <Modal.Header>
         <p>{this.props.message}</p>
        </Modal.Header>
         <Modal.Actions>
         <Button
          color='black'
          onClick={()=> {this.close()}}
          content={this.props.affirmativeUsed}
         />
         </Modal.Actions>
       </>
         :
        <>
        <Modal.Header>
         <p>{this.props.message}</p>
        </Modal.Header>
        <Modal.Actions>
         <Button
          color='black'
          onClick={this.close}
         >
           No
         </Button>
         <Button
          positive
          icon='checkmark'
          labelPosition='right'
          content={this.props.affirmativeUsed}
          onClick={() => {this.close(); this.logOutUser()}}
         />
         </Modal.Actions>
        </>
         }
      </Modal>
    </>
   )
 }
}

MyModal.propTypes = {
 message: PropTypes.string,
 affirmativeUsed: PropTypes.string
}

function mapStateToProps(state) {
 const { ui } = state
 const { modalActive, avatarModalActive } = ui

 return { modalActive, avatarModalActive }
}
const mapDispatchToProps = dispatch =>
 bindActionCreators({ logOutUser, avatarModalStateOff, modalStateOff }, dispatch)

export default connect(mapStateToProps, mapDispatchToProps)(MyModal)

这是 Redux 文件:

`ui.js`

/* initial state */
export var uiStartState = { modalActive: false, avatarModalActive: false, }

/* action types */
export const actionTypes = {
    MODAL_ACTIVE: 'MODAL_ACTIVE',
    MODAL_INACTIVE: 'MODAL_INACTIVE',
    AVATAR_MODAL_ACTIVE: 'AVATAR_MODAL_ACTIVE',
    AVATAR_MODAL_INACTIVE: 'AVATAR_MODAL_INACTIVE',
}

/* reducer(s) */
export default function ui(state = uiStartState, action) {
    switch (action.type) {

        case actionTypes.MODAL_ACTIVE:
            return Object.assign({}, state, { modalActive: true });

        case actionTypes.MODAL_INACTIVE:
            return Object.assign({}, state, { modalActive: false });

        case actionTypes.AVATAR_MODAL_ACTIVE:
            return Object.assign({}, state, { avatarModalActive: true });

        case actionTypes.AVATAR_MODAL_INACTIVE:
            return Object.assign({}, state, { avatarModalActive: false });

        default:
            return state
    }
};

/* actions */
export const modalStateOn = () => {
    return { type: actionTypes.MODAL_ACTIVE }
}

export const modalStateOff = () => {
    return { type: actionTypes.MODAL_INACTIVE }
}

export const avatarModalStateOn = () => {
    return { type: actionTypes.AVATAR_MODAL_ACTIVE }
}

export const avatarModalStateOff = () => {
    return { type: actionTypes.AVATAR_MODAL_INACTIVE }
}

希望这是有道理的!