I have taken a modal component from the react-semantic-ui and configured it to either have a confirm/alert like dialog or a traditional Modal.
I did this mostly to practice making more useful & reusable components and indirectly to practice managing and moving state for it to a store ie 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)
This worked fantastically for a Modal I wanted to use for Logging out:
=======================================
home | profile | *dashboard | logout /* You get a Modal to confirm your desire to log out */
=======================================
However on my profile page I have created an ImageUploader
component which handles loading images for that page, As you might have guessed by now, I want a Modal to pop up as well when the axios
request is successful and one for a failure to give some feedback...
=======================================
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!!
-------------
This is the ImageUploader
component:
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)
Interestingly enough the Modal
for the logging out action get rendered instead of the one in the ImageUploader
???
AND
When I pass in the prop value to isAlertModal
it is ignored?!
<FooModal
isAlertModal={true}
open={true}
affirmativeUsed="Yes"
message="Your image has been uploaded succesfully"
/>
So I suppose maybe I have to unmount the Modal
in the nav to allow the image loader Modal
to propagate?
*Keep in mind in the uploadImage
function has e.stopPropagation();
called, but no dice!
Any help would be appreciated!
UPDATE
As Eneias suggested the rendering of this component (ImageModal) goes in the render function. Now this worked however that Modal is rendering for the logging out function.
Your FooModal
should be on render funciton instead of the callback function.
The render function is called every time your state or your props change. So, you should have a flag in your state isUploaded
, or something like that, and use it as condition to render your FooModal
.
On the callback function of your post method, you simply update the state. It will trigger the render function again with the new state.
So I discovered what the problem was:
Modal
component was incorrect!!! <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>
Had only one <Model.Header>
, this.props.message
and <Modal.Actions>
serving two out comes in the ternary:
It should be written like this:
<>
<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)
And this is the Redux file:
`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 }
}
Hope this makes sense!
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.