反应:不正确的模态组件在 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 }
}
希望这是有道理的!
我从 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 }
}
希望这是有道理的!