I have similar problem like this How to use Bootstrap Modal inside map function in React Js to render indivisual elements?
But answer is not working in my code or I didn't implement it properly. I know that in map to method is directed only last element of list but what I can do to transfer in toogleConfirmation proper data from map? Any hints?
Its my ModalComponent.jsx
export const ModalHeader = props => {
return <div className="modal-header">{props.children}</div>;
};
export const ModalBody = props => {
return <div className="modal-body">{props.children}</div>;
};
export const ModalFooter = props => {
return <div className="modal-footer">{props.children}</div>;
};
class ModalComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
modalShow: '',
display: 'none'
};
this.openModal = this.openModal.bind(this);
this.closeModal = this.closeModal.bind(this);
}
openModal() {
this.setState({
modalShow: 'show',
display: 'block'
});
}
closeModal() {
this.setState({
modalShow: '',
display: 'none'
});
}
componentDidMount() {
this.props.isOpen ? this.openModal() : this.closeModal();
}
componentDidUpdate(prevProps) {
if (prevProps.isOpen !== this.props.isOpen) {
this.props.isOpen ? this.openModal() : this.closeModal();
}
}
render() {
return (
<div
className={'modal fade ' + this.state.modalShow}
tabIndex="-1"
role="dialog"
aria-hidden="true"
style={{ display: this.state.display }}
>
<div className="modal-dialog" role="document">
<div className="modal-content">{this.props.children}</div>
</div>
</div>
);
}
}
export default ModalComponent;
and table in render method
class Datas extends React.Component {
constructor(props) {
super(props);
this.state = {
modal: false,
isConfirmed: false,
collections: [],
data: '',
datas: []
};
this.toggle = this.toggle.bind(this);
this.changeConfirmation = this.changeConfirmation.bind(this);
}
toggle() {
this.setState({ modal: !this.state.modal });
}
changeConfirmation(){
this.setState({
isConfirmed: !this.state.isConfirmed
});
}
toggleConfirmation(name, status) {
this.changeConfirmation();
this.toggle();
//next code with name and status
}
render() {
return (
<Table responsive>
<thead className="text-primary">
<tr>
<th>Name</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{this.state.collections.map((collection, i) => (
<tr key={i}>
<td>{collection.name}</td>
<td>{collection.status}</td>
<td>
<button
type="button"
className="btn btn-danger"
onClick={() => this.setState({
modal: true,
data: collection
})}
>
</button>
<Modal isOpen={this.state.modal}>
<ModalHeader>
<h5>Confirmation</h5>
<button
type="button"
className="close"
aria-label="Close"
onClick={this.toggle.bind(this)}
>
<span aria-hidden="true">×</span>
</button>
</ModalHeader>
<ModalBody>
<p>Are you sure to proceed with this action with {collection.name} ?</p>
</ModalBody>
<ModalFooter>
<button
type="button"
className="btn btn-secondary"
onClick={this.toggle.bind(this)}
>
Close
</button>
<button
type="button"
className="btn btn-primary"
onClick={this.toggleConfirmation.bind(this, collection.name, collection.status)}
>
Ok
</button>
</ModalFooter>
</Modal>
</td>
</tr>
))}
</tbody>
</Table>
)
}}
just pass collection as props to ModalBody , ModalHeader & ModalFooter in Data.js
<ModalBody collection={collection}/>
you can update ModalBody , ModalHeader & ModalFooter like this :
export const ModalBody = props => {
return (<div className="modal-body">
<p>Are you sure to proceed with this action with {props.collection.name} </p>
</div>);
};
Related
I have a dropdown menu in my react app just like this:
and you see there is "Open modal" where I have problem with:
The issue is when I click on it, the modal content shows up inside of the the list which is another component. the code is like:
const App = () => {
return (
<CRow>
<CCol xs={12}>
<CNav>
<CNavItem>
</CNavItem>
<CDropdown variant="nav-item">
<CDropdownToggle color="secondary">Dropdown button</CDropdownToggle>
<CDropdownMenu>
<CDropdownItem><Modal parentSelector={() => document.querySelector('#root')}> </Modal> </CDropdownItem>
<CDropdownItem href="#">Another action</CDropdownItem>
<CDropdownItem href="#">Something else here</CDropdownItem>
</CDropdownMenu>
</CDropdown>
<CNavItem>
<CNavLink href="#">Link</CNavLink>
</CNavItem>
<CNavItem>
<CNavLink href="#" disabled>
Disabled
</CNavLink>
</CNavItem>
</CNav>
</CCol>
</CRow>
)
}
export default App
and here is my modal component:
const display = {
display: 'block'
};
const hide = {
display: 'none'
};
class Modal extends React.Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.state = {
toggle: false
}
}
toggle(event) {
this.setState((prevState) => ({
toggle: !prevState.toggle
}));
}
render() {
var modal = [];
modal.push(
<div className="modal" style={this.state.toggle ? display : hide}>
<div className="modal-content">
<h4>modal content</h4>
</div>
<div className="modal-footer">
<a className="btn-flat" onClick={this.toggle}>Agree</a>
</div>
</div>
);
return (
<div>
<a className="btn" onClick={this.toggle}>{this.state.toggle ? 'Close modal' : 'Open modal'}</a>
{modal}
</div>
);
}
}
export default Modal
I tried QuerySelector, but didn't work. How can I control which DIV or component, modal can show up?
I have created a repeater, so when user clicks on plus icon then a new row with two input tags is appended. Below is my code :
repeater.js
import React from "react"
const Details = (props) => {
return (
props.Desc !== '' ?
props.Desc.map((val, idx) => {
let desc = ` desc-${idx}`, file = `file-${idx}`
return (
<tr key={val.index}>
<td> Description</td>
<td >
<input type="text" defaultValue={val.desc} name="desc" data-id={idx} id={desc} className="form-control " />
</td>
<td className="mr-2"> Files</td>
<td>
<input type="file" defaultValue={file} name="file" id={file} data-id={idx} className="form-control " />
</td>
<td>
{
idx === 0 ? <button onClick={() => props.add()} type="button" className="btn btn-primary text-center"><i className="fa fa-plus-circle" aria-hidden="true"></i></button>
: <button className="btn btn-danger" onClick={(() => props.delete(val))} ><i className="fa fa-minus" aria-hidden="true"></i></button>
}
</td>
</tr >
)
})
: null
)
}
export default Details
Details.js
import React, { Fragment, Component } from 'react'
import Details from './repeater.js'
class CreateDetail extends Component {
constructor(props) {
super(props);
this.state = {
inputList: [{ index: Math.random(), desc: "", file: "" }],
}
}
onSubmit = (e) => {
console.log('data : ', this.state.inputList[0]);
}
handleChange = (e) => {
if (["desc", "file"].includes(e.target.name)) {
let Desc = [...this.state.inputList]
inputList[e.target.dataset.id][e.target.name] = e.target.value;
} else {
this.setState({ [e.target.name]: e.target.value })
}
}
addNewRow = (e) => {
this.setState((prevState) => ({
inputList: [...prevState.inputList, { index: Math.random(), desc: "", file: "" }],
}));
}
deteteRow = (index) => {
this.setState({
inputList: this.state.inputList.filter((s, sindex) => index !== sindex),
});
}
clickOnDelete(record) {
this.setState({
inputList: this.state.inputList.filter(r => r !== record)
});
}
render() {
let { inputList, flag } = this.state
return (
<Fragment>
<div className="container-fluid">
<div className="row">
<div className="col-sm-12">
<div className="card">
<div className="card-body">
<form className="needs-validation" onChange={this.handleChange}>
<div className="form-group row">
<label className="col-xl-3 col-md-3">Details</label>
<div className="col-9 col-md-9">
<table className="table">
<tbody>
<Details add={this.addNewRow} delete={this.clickOnDelete.bind(this)} Desc ={inputList} />
</tbody>
</table>
</div>
</div>
<div className="pull-right">
<button type="button" className="btn btn-primary" onClick={this.onSubmit}> save </button>
</div>
</form>
</div>
</div>
</div>
</div >
</div >
</Fragment >
)
}
}
export default CreateDetail
But I am facing one issue, when I use the input tag's type "file" and I upload an image then I am receiving the fakepath as shown below.
C:\fakepath\6t8Zh249QiFmVnkQdCCtHK.jpg
I am encountering this problem only in repeater. If I use an input tag with type "file", outside the repeater then I am receiving the correct path.
The fake path is the main issue because if I extract the file name and upload it to s3 then empty image is uploaded to s3.
How can I upload a file in repeater?
the browser will not allow getting the local path of the file.you can use the data as form data and store it in state and can send it to S3.
like
handleChange = (e) => {
let formData = new FormData
for (var i = 0; i < e.target.files.length; i++) {
formData.append(e.target.name, e.target.files[i])
}
.....
}
I'm having weird problem in React JS. I have two classes named as Notes.js and DataTables.js
I'm using DataTables in Note.js like this
<DataTables
keyField="id"
columns={columns}
url={this.state.url}
useCallBack={true}
onEdit={this.onEdit}
/>
Please Note that DataTables.js is my own custom created DataTable.js not react-datatable.
All the work like fetching data from URL and showing it in tabular form is in DataTables.js file.
Note.js Code:
import React, { Component } from "react";
import { Constant } from "../shared/Constants";
import DataTables from "../shared/DataTables";
import { Modal, Button } from "react-bootstrap";
import BreadCrumb from "../shared/BreadCrumb";
import "../Style.css";
const columns = Constant.notes;
export class Notes extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
url: "notes/get_notes",
showModal: false,
note: [],
};
this.onEdit = this.onEdit.bind(this);
this.onAdd = this.onAdd.bind(this);
this.onUpdate = this.onUpdate.bind(this);
this.saveNote = this.saveNote.bind(this);
}
onUpdate(key, value) {
let noteData = this.state.note;
noteData[key] = value;
this.setState({
note: noteData,
});
}
saveNote(e) {
e.preventDefault();
}
onEdit(n) {
this.setState({
note: n,
showModal: true,
});
}
onAdd() {
this.setState({
note: [],
showModal: true,
});
}
render() {
return (
<>
<Modal
show={this.state.showModal}
aria-labelledby="example-modal-sizes-title-lg"
onHide={() => this.setState({ showModal: false })}
>
<form method="post" onSubmit={this.saveNote}>
<Modal.Header>
<Modal.Title>My Note</Modal.Title>
</Modal.Header>
<Modal.Body>
<div className="row">
<div className="col-sm-12">
<div className="form-group">
<label className="text-muted">Note Title</label>
<input
type="text"
placeholder="Note Title"
className="form-control"
ref="title"
value={this.state.note.title}
onChange={(e) => this.onUpdate("title", e.target.value)}
/>
</div>
<div className="form-group">
<label className="text-muted">Content</label>
<textarea
onChange={(e) => this.onUpdate("content", e.target.value)}
className="form-control"
style={{ height: "250px" }}
placeholder="Content"
>
{this.state.note.content}
</textarea>
</div>
</div>
</div>
</Modal.Body>
<Modal.Footer>
<Button
variant="secondary"
onClick={() => this.setState({ showModal: false })}
>
Close
</Button>
<Button type="submit" variant="primary">
Save Note
</Button>
</Modal.Footer>
</form>
</Modal>
<BreadCrumb
title="My Notes"
useCallBack={true}
onAdd={this.onAdd}
active_link="Notes"
link=""
link_text="Add New"
/>
<div className="row">
<div className="col-sm-12">
<div className="card">
<div className="card-body">
<div className="card-title">Notes</div>
<DataTables
keyField="id"
columns={columns}
url={this.state.url}
useCallBack={true}
onEdit={this.onEdit}
/>
</div>
</div>
</div>
</div>
</>
);
}
}
export default Notes;
I'm having Problem in Note.js on onUpdate function
onUpdate(key, value) {
let noteData = this.state.note;
noteData[key] = value;
this.setState({
note: noteData,
});
}
Problem: When I update a field in Modal as you can see in my code, then my Table in DataTable.js automatically gets updated, I'don't why :/
Here is DataTables.js function where I'm sending data to onEdit function
const TableData = () => {
return (
<tbody>
{tableData.length === 0 ?
<tr>
<td className="text-center" colSpan="5"><strong>No Data Found</strong></td>
</tr>
:
tableData.map((tData) => (
<tr key={tData[this.props.keyField]}>
{this.props.columns.map((item, index) => (
<td key={index} className="table-content">
{index === 0 ?
[(useCallback === true ? <span key={"sub_"+index} className="link" onClick={() => this.props.onEdit(tData)}>{tData[item.dataField]}</span> :
<Link
to={
this.props.edit_link +
"/" +
tData[this.props.edit_key_first] + (this.props.edit_key_second ? "/" +
tData[this.props.edit_key_second] : '')
}
>
{tData[item.dataField]}
</Link>
)]
: (
tData[item.dataField]
)}
</td>
))}
</tr>
))}
</tbody>
);
};
Please check gif image below so you can understand it :P
You have an onChange function that is updating the content
onChange={(e) => this.onUpdate("content", e.target.value)}
If you don't want to change the content while typing then you will have to remove this.
The toaster component is made programatically and i am passing the message through another component.
my Toaster component Looks like this:-
export default class MyToaster extends React.Component {
constructor(props) {
super(props);
this.toaster = React.createRef();
this.state = {
message: [],
show: false
};
this.state.message = this.props.message;
}
handleClose() {
this.setState({show: false})
}
createtoaster() {
let toastmessage = [];
if ( this.state.show === true) {
for (let i = 0; i <= this.state.message.length; i++) {
let tmessage = <div className="col-md-3 offset-md-8">
<div className="card-header">
<h3 className="card-title">Toast</h3>
</div>
<div className="card-body">
{this.state.message[i]}
</div>
<div className="card-footer">
<button className="btn btn-primary" onClick={this.handleClose()}>x</button>
</div>
</div>
toastmessage.push(tmessage);
}
return (toastmessage);
}
}
render() {
return (
<div className="col-md-2 offset-md-9">
<button className="btn btn-primary" onClick={() => this.setState({show:true})}>show Toaster</button>
</div>
)
}
}
Also this is my PostCard.js page in which the toaster component is called and message is passed.
export default class MyCard extends React.Component {
constructor(props) {
super(props);
this.state = {
currentPage: this.props.pgNo,
details: [],
id: null,
index: 0
}
this.message = 'osihfosihfoi';
}
AddButton() {
return (
<Link to={`${this.props.url}/addnew${this.props.url}`}>
<button
style={{
float: "right"
}}
className="btn btn-primary"><Faplus/>
</button>
</Link>
);
}
closeAfter7 = () => toast("7 Kingdoms", {autoClose: 7000});
fetchMoreData = () => {
if(this.state.index<100){
this.setState({
index: this.state.index + 5
})
}}
componentDidMount() {
window.addEventListener('scroll', this.onScroll);
this.fetchMoreData();
}
onScroll = () => {
$(window).scroll(() => {
if ($(window).scrollTop() + $(window).height() == $(document).height()) {
this.fetchMoreData();
}
});
}
createCard = () => {
let cardBody = [];
for (let i = this.state.currentPage; i < this.state.index; i++) {
let card = <div className="content">
<div className="container-fluid">
<div className="col-md-6 offset-md-3">
<div className="card">
<div className="card-header">
<h3 className="card-title"></h3>
</div>
<div className="card-body">
<h5>
ID :
</h5>{this.props.data[i].id}<br/>
<h5>
User ID :
</h5>{this.props.data[i].userId}<br/>
<h5>
Title :
</h5>{this.props.data[i].title}<br/>
<h5>
Body :
</h5>{this.props.data[i].body}<br/>
</div>
<div className="card-footer clearfix"></div>
</div>
</div>
</div>
</div>
cardBody.push(card);
}
return (cardBody)
}
render() {
return (
<div className="row">
<div className="col-md-2 offset-md-10">{this.AddButton()}
</div>
<Toaster message={this.message}/>
{/* <div id="snackbar" style={{backgroundColor: "red"}}>Hogaya.</div> */}
<div>
{this.createCard()}
</div>
</div>
)
}
}
My UI renders the Show toaster button but not do anything when it is clicked. Also it dosent give any errors. Can't figure out the problem so if anyone can point out what i am doing wrong it ll be great. Also Please let me know if I am not using the correct method or logic.
TIA.
It was not rendering anything because I wasn't rendering my createtoaster component. the correct way is that in my toaster component i should render it like this
render() {
return (
<div className="col-md-2 offset-md-9">
<button className="btn btn-primary" onClick={this.handleOpen}></button>
{this.createtoaster()}
</div>
)
}
}
Building a modal component that opens up a bootstrap modal from any part of the app then sets custom states for that component outside of it. It works fine but i always just get this error once i open the modal and I cant seem to figure out why:
Warning: setState(...): Cannot update during an existing state transition (such as within render or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to componentWillMount.` Doesnt really break anything but error keeps showing up.
My code:
layout.js
import React from "react";
import {Link} from 'react-router';
import NotificationSystem from 'react-notification-system';
import AppHeader from "#/ui/header/AppHeader";
import AppFooter from "#/ui/footer/AppFooter";
import Modal from "#/ui/modals/modal/Modal";
import "#/main.scss";
import './layout.scss';
export default class Layout extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
app.notify.clear = this.refs.notificationSystem.clearNotifications;
app.notify = this.refs.notificationSystem.addNotification;
app.modal = this.refs.modal.updateProps;
}
render() {
return (
<div class="app">
<div class="header">
<AppHeader page={this.props.location.pathname.replace('/', '')}/>
</div>
<div class="body">
{this.props.children}
</div>
<div class="footer">
<AppFooter />
</div>
<NotificationSystem ref="notificationSystem" style={false} />
<Modal ref="modal" />
</div>
);
};
}
Modal.js
import React from "react";
import ReactDOM from 'react-dom';
import SVGInline from "react-svg-inline";
import {closeSvg} from '#/utils/Svg';
export default class Modal extends React.Component {
constructor(props) {
super(props);
this.state = {
showHeader: true,
showFooter: false,
title: "",
size: '',
className: '',
id: '',
footerContent: null,
showSubmitBtn: true,
showCancelBtn: true,
cancelBtnText: "Cancel",
successBtnText: "Save Changes",
onModalClose: () => {},
showModal: false,
html: () => {}
}
this.updateProps = this.updateProps.bind(this);
this.hideModal = this.hideModal.bind(this);
}
componentWillMount() {
var self = this;
var $modal = $(ReactDOM.findDOMNode(this));
}
componentDidUpdate(prevProps, prevState) {
if(this.state.showModal) {
$('body').addClass('modal-open');
} else {
$('body').removeClass('modal-open');
}
}
componentWillUnmount() {
// $('body').removeClass("modal-open");
}
componentWillReceiveProps(nextProps) {
console.log(nextProps);
}
updateProps(args) {
let merged = {...this.state, ...args};
this.setState(merged);
}
hideModal() {
this.setState({
showModal: false
});
this.state.onModalClose();
}
buildFooter() {
if(this.props.footerContent) {
return (
<div class="content">
{this.props.footerContent}
</div>
)
} else if(this.props.showCancelBtn && this.props.showSubmitBtn) {
return (
<div class="buttons">
<button type="button" class="btn btn-default" data-dismiss="modal" onClick={this.props.onModalClose}>{this.props.cancelBtnText}</button>
<button type="button" class="btn btn-success">{this.props.successBtnText}</button>
</div>
);
} else if(this.props.showCancelBtn) {
return (<button type="button" class="btn btn-default" data-dismiss="modal" onClick={this.props.onModalClose}>Close</button>);
} else if(this.props.showSubmitBtn) {
return (<button type="button" class="btn btn-success">Save changes</button>);
}
}
render() {
let {
id,
className,
onModalClose,
size,
showHeader,
title,
children,
showFooter,
showModal,
html
} = this.state;
return (
<div class={`modal-wrapper`} >
{
showModal ?
<div class={`modal fade in ${className}`} role="dialog">
<div class="bg" ></div>
<div class={`modal-dialog ${size}`}>
<div class="modal-content">
{ showHeader ?
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<SVGInline svg={closeSvg} />
</button>
<h4 class="modal-title">{ title }</h4>
</div> : '' }
<div class="modal-body" >
{html()}
</div>
{ showFooter ?
<div class="modal-footer">
{ this.buildFooter() }
</div> : ''
}
</div>
</div>
</div>
: ''
}
</div>
);
}
}
SelectDefaultImage.js
import React from "react";
import sass from "./selectdefaultimage.scss";
import FullScreenImageModal from "#/ui/modals/fullscreenimagemodal/FullScreenImageModal";
export default class SelectDefaultImage extends React.Component {
constructor() {
super();
this.state = {
showModal: false,
imgUrl: false,
}
}
showImageModal(image) {
this.setState({
showModal: true,
imgUrl: image
});
}
hideImageModal() {
this.setState({
showModal: false,
imgUrl: false
})
}
onSelectImageClick(e, image) {
$('.select-image-widget .active').removeClass('active');
$(e.target).parent().addClass('active');
// this.props.selectedImage(image)
}
render() {
let {listingManager, images, selectedImage} = this.props;
let {imgUrl} = this.state;
return (
<div class="content">
<div class="row">
<div class="col-sm-12">
<label class="control-label" for="description">Select an Image</label>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div class="select-image-widget">
{
images.map((image, idx) => {
return (
<div class="selecter" key={idx}>
<div class="img" style={{backgroundImage: `url(${listingManager.LISTINGS_PATH + image})` }} onClick={(e) => { this.onSelectImageClick(e, image) }}></div>
<i class="fa fa-search-plus" aria-hidden="true" onClick={()=> {this.showImageModal(image)}}></i>
</div>
)
})
}
</div>
</div>
</div>
{
this.state.showModal ?
app.modal({
showModal: true,
className: "fullscreen-image-modal",
size: "modal-lg",
html: () => {
return (<img src={listingManager.LISTINGS_PATH + imgUrl} />);
}
})
: ''
}
</div>
)
}
}
The reason for the error is most likely that in SelectDefaultImage, you call app.modal from within the render method, and app.modal is this.refs.modal.updateProps, which does a setState. If you put the app.modal call in showImageModal, I expect the error to go away. However, setting the state of a another component by means of refs and globals is a bit of a React antipattern, so I would recommend to do some refactoring and use props to pass the data.