Currently I have a single Component (Logbook.js) that has a table of logs, each with a unique key. When I click on the table row a modal displays the data of that row in a modal. From this modal, when I click update another modal appears and the data is neatly displayed in a form ready to update. I want to separate these modals into separate components, DisplayModal.js and UpdateModal.js. How can I do this so that the data contained in the row is carried to reach component?
This can give you an idea where to start:
const { Button, Modal, ModalHeader, ModalBody, ModalFooter } = Reactstrap;
class Modal1 extends React.Component {
render() {
return (
<React.Fragment>
<ModalHeader toggle={this.toggle}>{this.props.title}</ModalHeader>
{this.props.children}
</React.Fragment>
)
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
modal: false,
modalType: 1
};
this.toggle = this.toggle.bind(this);
this.changeModalType = this.changeModalType.bind(this);
}
changeModalType(type) {
this.setState({modalType: type});
}
toggle() {
this.setState(prevState => ({
modal: !prevState.modal
}));
}
renderDisplayModal() {
return(
<React.Fragment>
<ModalBody>
This is display modal
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={() => this.changeModalType(2)}>Open Update Modal</Button>{' '}
<Button color="secondary" onClick={this.toggle}>Cancel</Button>
</ModalFooter>
</React.Fragment>
);
}
renderUpdateModal() {
return(
<React.Fragment>
<ModalBody>
This is update modal
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={() => this.changeModalType(1)}>Open Display Modal</Button>{' '}
<Button color="secondary" onClick={this.toggle}>Cancel</Button>
</ModalFooter>
</React.Fragment>
);
}
render() {
return (
<div>
<Button color="danger" onClick={this.toggle}>Open</Button>
<Modal isOpen={this.state.modal} toggle={this.toggle}>
<Modal1 click={this.toggle} title={this.state.modalType === 1 ? 'Display title' : 'Update Modal'}>
{this.state.modalType === 1
? this.renderDisplayModal()
: this.renderUpdateModal()
}
</Modal1>
</Modal>
</div>
);
}
}
ReactDOM.render( < App / > ,
document.getElementById('root')
);
<script src="https://unpkg.com/react#16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js" crossorigin></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reactstrap/6.0.1/reactstrap.full.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/latest/css/bootstrap.min.css" crossorigin="anonymous">
<div id="root" />
Related
My app.js creates a widget layout, within it I have an handleWidgetSelection function which should take an id when an item is clicked in my modal component. When I console log in the handleWidgetSelection function I notice that the state is not updated and it remains null as I originally set it as. I am fairly new to react so any help would be great.
This is my app.js
class App extends Component {
constructor(props) {
super(props);
this.state={
selectedWidgetId: null, //initially set as null because no widget is selected
widgetOptions:[{name:"Data Table", comp:<DataTable/>},{name:"List", comp:<CheckboxList/>}],
widgets:[ //array for layout
{id:1, content: <DataTable/>},
{id:2, content: <CheckboxList/>},
{id:3, content: ""},
{id:4, content: ""}
],
isModalOpen: false
}
}
handleWidgetSelection=(id) => {
this.setState({selectedWidgetId: id})
console.log(this.state.selectedWidgetId); //trying to fix so this value does not always print null
}
.....
render() {
const { classes } = this.props;
return (
<div className={classes.root}>
//I am passing my values to the AddWidgetDialog component here
<AddWidgetDialog handleWidgetSelection={this.handleWidgetSelection} widgets={this.state.widgetOptions} isModalOpen={this.state.isModalOpen} onRequestClose={this.onRequestClose} />
<Grid container spacing={24}>
{
this.state.widgets.map((widget,index)=>{
return(
<Grid item xs={12} sm={6}>
<Paper className={classes.paper}><Swappable id={widget.id} content={widget.content} delete={this.deleteEvent.bind(this,index)} add={this.addEvent.bind(this,index)}/></Paper>
</Grid>
)
})
}
</Grid>
</div>
);
}
This is my AddWidgetDialog component
import React, { PropTypes } from 'react';
import Modal from 'react-modal';
const AddWidgetDialog = ({ handleWidgetSelection, widgets, isModalOpen, onRequestClose}) => {
const widgetItems = widgets.map((widget) => {
return (
<div className="list-group">
<a href="#" onClick={() => handleWidgetSelection(widget.name)} className="list-group-item">
<h6 className="list-group-item-heading">{widget.name}</h6>
</a>
</div>
);
});
return (
<Modal
className="Modal__Bootstrap modal-dialog"
isOpen={isModalOpen}>
<div className="modal-content">
<div className="modal-header">
<button type="button" className="close" onClick={onRequestClose}>
<span aria-hidden="true">×</span>
<span className="sr-only">Close</span>
</button>
<h4 className="modal-title">Add a widget</h4>
</div>
<div className="modal-body">
<h5>Pick a widget to add</h5>
{widgetItems}
</div>
<div className="modal-footer">
<button type="button" className="btn btn-default" onClick={onRequestClose}>Close</button>
</div>
</div>
</Modal>
);
};
export default AddWidgetDialog;
setState maybe asynchronous. See the docs Why is setState giving me the wrong value?
handleWidgetSelection=(id) => {
this.setState({selectedWidgetId: id})
console.log(this.state.selectedWidgetId); //no guarantee state is updated or not.
}
You should use console.log() in the callback as second argument to the setState
handleWidgetSelection=(id) => {
this.setState({selectedWidgetId: id},() => {
console.log(this.state.selectedWidgetId); //state is updated now.
})
}
I am having a Component which is a Modal alert. For animating the Modal, I am using the Transition from semantic-ui-react. But only a few animations(pulse,bounce, flash) are working and that too only while mounting of Component, and not working while I close the Modal. Also the duration property is also not working.
class Alert extends React.Component {
state = {
open: true
};
close = () => {
this.setState({
open: false
});
};
render() {
const { open } = this.state;
return (
<Transition animation="fade" duration={1000} visible={open}>
<Modal
size="mini"
closeOnDimmerClick={false}
closeOnEscape={false}
open={this.state.open}
onClose={this.close}>
<Modal.Content>
<p>
Hello user
</p>
</Modal.Content>
<Modal.Actions>
<Button color="blue" onClick={this.close}>
Ok
</Button>
</Modal.Actions>
</Modal>
</Transition>
);
}
}
Why such weird behavior? What am I doing wrong?
Modal is unmounting way before Transition can animate it, one way would be to let Transition unmount the Modal:
class Alert extends React.Component {
state = {
open: true
};
close = () => {
this.setState({
open: false
});
};
render() {
const { open } = this.state;
return (
<Transition
animation="fade"
duration={1000}
unmountOnHide={true}
visible={open}
>
<Modal
size="mini"
closeOnDimmerClick={false}
closeOnEscape={false}
open={true}
onClose={this.close}
>
<Modal.Content>
<p>Hello user</p>
</Modal.Content>
<Modal.Actions>
<Button color="blue" onClick={this.close}>
Ok
</Button>
</Modal.Actions>
</Modal>
</Transition>
);
}
}
I hope it helps!
class Posts extends Component {
constructor(){
super();
this.state = {
modalIsOpen:false
};
this.openModal = this.openModal.bind(this);
this.afterOpenModal = this.afterOpenModal.bind(this);
this.closeModal = this.closeModal.bind(this);
}
openModal() {
console.log("got here?")
this.setState({modalIsOpen: true});
}
afterOpenModal() {
// references are now sync'd and can be accessed.
this.subtitle.style.color = '#f00';
}
closeModal() {
this.setState({modalIsOpen: false});
}
render() {
const keys = generateKey(new Date().getTime())
var dictionary = this.props.posts
const postItemsArr = Object.keys(dictionary).map(post=>dictionary[post])
const number = 0
const postItems = postItemsArr.map(
post=>(
<Jumbotron key={generateKey(post.positiontitle) + generateKey(post.businessId)} >
<div className="position">{post.positiontitle}</div><br></br>
<BusinessName businessnameType={post.businessname} /><br></br>
<JobDescription jobDescription={post.description_sanitized} /><br></br>
<p>
<Modal isOpen={this.state.modalIsOpen}
onAfterOpen={this.afterOpenModal}
onRequestClose={this.closeModal}
style={customStyles}
contentLabel="Example"
>
<h2 ref={subtitle => this.subtitle = subtitle}>Hello</h2>
<button onClick={this.closeModal}>close</button>
<div>I am a modal</div>
<form>
<input />
<button>tab navigation</button>
<button>stays</button>
<button>inside</button>
<button>the modal</button>
</form>
</Modal>
<button onClick={this.openModal}>Open Modal</button>
</p>
</Jumbotron>
)
)
return (
<div>
<h1> Jobs Listings </h1>
{postItems }
</div>
);
}
}
Inside my Jumbotron, my modal doesn't seem to be opening, why?
It looks like it gets to the state openModal()
but doesn't actually open the modal when the user clicks on
<button onClick={this.openModal}>Open Modal</button>
Also; should I create a separate component called Modal; what would be best practices?
Overall I'm trying to trigger the modal object for a respective list item in the list.
I would create a new presentational component called modal, then use that when you need it, rather than do what you're trying to do now, which is pass an open/close state. The Modal can be a simple presentational component that just accepts props. By doing it this way, you create reusability.
function Modal(props) {
return (
<div className={props.css.awesomeLayout}>
{props.aProp}
</div>
)
}
ReactDOM is not working with refs.
I want to sting html <b>abcd</b> append to body of modal, but it's not working.
when I check console.log() of modal and modalBody, it return null.
My code in under:
class Header extends Component {
constructor(props) {
super(props);
this.state = {
this.state.modalSendResult: false
};
}
_open() {
this.setState({
modal: !this.state.modal
});
if (!this.state.modal) {
this.appendNode();
}
}
appendNode() {
let modal = ReactDOM.findDOMNode(this.refs.modal)
modalBody = ReactDOM.findDOMNode(this.refs.body);
ReactDOM.render(
<b>abcde</b>, modalBody
);
}
render() {
return (
<Button onClick={this._open.bind(this)}
className="btn btn-primary btn-primary-1 mr-1">
<i className="fa fa-paper-plane-o" aria-hidden="true"></i> Open
</Button>
<Modal ref="modal" isOpen={this.state.modal} toggle={this._open}
className={'modal-primary modal_customer'}>
<ModalHeader toggle={this._open}>
Danh sách kết quả gửi
</ModalHeader>
<ModalBody className="modal-send" ref="body">
</ModalBody>
</Modal>
);
}
}
Please help me!
Take a look at ReactDOM. You can avoid having to call findDOMNode by setting up your refs as specified here: Refs and the DOM.
I think in your case it would look something like
<Modal
ref={(modal) => { this.modal = modal; }}
isOpen={this.state.modal}
toggle={this._open}
className={'modal-primary modal_customer'}
>
<ModalHeader toggle={this._open}>
Danh sách kết quả gửi
</ModalHeader>
<ModalBody
className="modal-send"
ref={(body) => { this.body = body; }}
/>
</Modal>
Then you should be able to reference them with
this.modal
this.body
Alternatively, you could pass the content you want for your ModalBody component as a prop.
<ModalBody
className="modal-send"
content="abcde"
ref={(body) => { this.body = body; }}
/>
And render in your ModalBody component:
render() {
return (
<b>{this.props.content}</b>
);
In my EditRecipeForm component, when editing text in the modal, I get Uncaught TypeError: this.setState is not a function , because this refers to the modal window, and not the "app", the thing I don't understand is how to get this to be correct, so that editing the field value updates actually works.
(I removed code not directly related), full demo available here: http://codepen.io/pdewouters/pen/adLpvQ
const Button = ReactBootstrap.Button,
Accordion = ReactBootstrap.Accordion,
Panel = ReactBootstrap.Panel,
Modal = ReactBootstrap.Modal,
Input = ReactBootstrap.Input,
ButtonToolbar = ReactBootstrap.ButtonToolbar,
ButtonInput = ReactBootstrap.ButtonInput,
ListGroup = ReactBootstrap.ListGroup,
ListGroupItem = ReactBootstrap.ListGroupItem
class App extends React.Component {
constructor(props){
super(props)
this.state = {
showModal: false,
showEditModal: false,
recipes: [],
recipeName: '',
ingredients: '',
editRecipeNameInputVal: '',
editIngredientsInputVal: '',
recipeToEdit: {recipeName:'',ingredients:''}
}
}
closeEditModal(){
this.setState({ showEditModal: false })
}
openEditModal(recipe){
this.setState({showEditModal:true,recipeToEdit:recipe})
}
handleRecipeEditInputChange(value){
this.setState({editRecipeNameInputVal: value})
}
handleIngredientsEditInputChange(value){
this.setState({editIngredientsNameInputVal: value})
}
render(){
return(
<div className="container">
<div className="page-header">
<h1>Recipes</h1>
</div>
<div className="row">
<div className="col-md-12">
<EditRecipeForm
showModal={this.state.showEditModal}
closeModal={this.closeEditModal.bind(this)}
handleSubmit={this.handleEditFormSubmit.bind(this)}
handleRecipeEditInputChange={this.handleRecipeEditInputChange.bind(this)}
handleIngredientsEditInputChange={this.handleIngredientsEditInputChange.bind(this)}
recipe={this.state.recipeToEdit}
handleRecipeEditInputChange={this.handleRecipeEditInputChange}
handleIngredientsEditInputChange={this.handleIngredientsEditInputChange} />
<RecipeList recipes={this.state.recipes}
handleDeleteRecipe={this.handleDeleteRecipe.bind(this)}
handleOpenEditModal={this.openEditModal.bind(this)}
/>
</div>
</div>
</div>
)
}
}
class RecipeList extends React.Component {
handleOpenEditModal(recipe,event) {
this.props.handleOpenEditModal(recipe)
}
render(){
let recipes = this.props.recipes.map((recipe,index)=>{
return (
<Panel header={recipe.recipeName} eventKey={index}>
<IngredientsList ingredients={recipe.ingredients} />
<ButtonToolbar>
<Button bsStyle="danger" onClick={this.props.handleDeleteRecipe.bind(null,index)}>Delete</Button>
<Button bsStyle="primary" onClick={this.handleOpenEditModal.bind(this,recipe)}>Edit</Button>
</ButtonToolbar>
</Panel>
)
})
return (
<Accordion>
{recipes}
</Accordion>
)
}
}
class EditRecipeForm extends React.Component {
handleRecipeEditInputChange(e){
this.props.handleRecipeEditInputChange(e.target.value)
}
handleIngredientsEditInputChange(e){
this.props.handleIngredientsEditInputChange(e.target.value)
}
render(){
return(
<Modal show={this.props.showModal} onHide={this.props.closeModal}>
<Modal.Header closeButton>
<Modal.Title>Edit recipe</Modal.Title>
</Modal.Header>
<Modal.Body>
<form onSubmit={this.props.handleSubmit}>
<Input type="text"
label="Recipe"
placeholder="blueberry pancakes"
value={this.props.recipe.recipeName}
onChange={this.handleRecipeEditInputChange.bind(this)} />
<Input type="textarea"
label="Ingredients"
placeholder="milk,sugar,flour,butter,blueberries"
value={this.props.recipe.ingredients}
onChange={this.handleIngredientsEditInputChange.bind(this)} />
<ButtonInput type="submit" value="Save changes" />
</form>
</Modal.Body>
<Modal.Footer>
<Button onClick={this.props.closeModal}>Close</Button>
</Modal.Footer>
</Modal>
)
}
}
React.render(<App />, document.querySelector('.app'))
It looks like you're passing the handleRecipeEditInputChange and handleIngredientsEditInputChange prop callbacks twice and the second time you are not binding to the component instance.
<EditRecipeForm
showModal={this.state.showEditModal}
closeModal={this.closeEditModal.bind(this)}
handleSubmit={this.handleEditFormSubmit.bind(this)}
handleRecipeEditInputChange={this.handleRecipeEditInputChange.bind(this)}
handleIngredientsEditInputChange={this.handleIngredientsEditInputChange.bind(this)}
recipe={this.state.recipeToEdit}
handleRecipeEditInputChange={this.handleRecipeEditInputChange}
handleIngredientsEditInputChange={this.handleIngredientsEditInputChange} />
The second props will overwrite the first ones, so try deleting the second ones.