Set state of parent from child component - reactjs

I am working on this for hours now and I don't know what I have to do to make it work.
I have a parent component that has a child which is a modal dialogue. This dialogue has a date picker that has two attributes focused and onFocusChange.
The relevant parts of the parent component before the render() method:
class Terminkalender extends Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.setEventData = this.setEventData.bind(this);
this.selectElementPatient = this.selectElementPatient.bind(this);
this.handleDateChange = this.handleDateChange.bind(this);
this.toggleFocus = this.toggleFocus.bind(this);
moment.locale('de');
this.state = {
patient: null,
appointments: [{end: '2018-08-25T11:57:27.512Z', title: 'TEST', start: '2018-08-25T11:57:27.512Z'}],
time: '10:00',
inputValue: ''
}
}
componentDidMount() {
this.setState({
appointments: this.state.appointments.map(a => Object.assign({}, a, { end: new Date(a.end), start: new Date(a.start) })),
});
}
setEventData(event) {
this.setState({
timeStart: event.start.getHours() + ":" + event.start.getMinutes(),
timeEnd: event.end.getHours() + ":" + event.end.getMinutes(),
date: event,
disabled: true,
modal: !this.state.modal
});
}
toggle() {
this.setState({
modal: !this.state.modal
});
}
handleDateChange(newDate) {
this.setState({
date: newDate
});
}
toggleFocus(toggleFocused) {
this.setState({
focused: !this.state.focused
});
}
The child component in the parent component:
<AppointmentModal
focused={this.state.focused}
toggleFocus={this.toggleFocus}
patient={this.state.patient}
handleDateChange={this.handleDateChange}
date={this.state.date}
/>
Here is the date picker that uses focused and toggleFocus():
<SingleDatePicker
inputIconPosition="after"
block={false}
numberOfMonths={1}
date={this.props.date ? moment(this.props.date): null}
onDateChange={date => this.props.handleDateChange(date)}
focused={this.props.focused}
onFocusChange={this.props.toggleFocus}
/>
The problem I have now, is that the opening mechanism that is toggled by the function toggleFocus doesn't seem the work. When I click on the field of SingleDatePicker I can see in the console that focused is set two times to true.
What do I need to change so that if I click on the field and trigger the toggleFocus() function, I set focused to true and when I click out or click one date, it is set to false.
EDIT: The SingleDatePicker is from react-dates.
EDIT2, the render() method:
render() {
return (
<div className="animated">
<Card>
<CardBody style={{height: '50em'}}>
<Button type="submit" size="sm" color="success" className="mb-3 p-1"><i className="fa fa-plus"></i> Neuer Termin</Button>
<BigCalendar className="d-sm-down-none"
{...this.props}
selectable
events={this.state.appointments}
views={['month', 'week', 'day']}
step={15}
defaultDate={new Date(currYear, currMonth, 1)}
defaultView='week'
toolbar={true}
messages={{month:"Monat", week:"Woche", day:"Tag", today:"Heute", previous:"Zurück", next:"Weiter"}}
culture="de"
onSelectSlot={this.setEventData}
onSelectEvent={this.setEventData}
/>
<BigCalendar className="d-md-none"
{...this.props}
selectable
events={this.state.appointments}
views={['day']}
step={15}
defaultDate={new Date(currYear, currMonth, 1)}
defaultView='day'
toolbar={true}
messages={{day:"Tag"}}
culture="de"
/>
<AppointmentModal
timeStart={this.state.timeStart}
timeEnd={this.state.timeEnd}
modal={this.state.modal}
onChange={this.toggle}
selectElementPatient={this.selectElementPatient}
focused={this.state.focused}
toggleFocus={this.toggleFocus}
patient={this.state.patient}
handleDateChange={this.handleDateChange}
date={this.state.date}
/>
</CardBody>
</Card>
</div>
);
}
EDIT 3: The AppointmentModalcomponent:
class AppointmentModal extends Component {
constructor(props) {
super(props);
}
render() {
return (
<Modal isOpen={this.props.modal} toggle={this.props.onChange}>
<ModalHeader toggle={this.props.onChange}>Neuer Termin</ModalHeader>
<ModalBody>
<FormGroup row>
<Col md="3">
<Label htmlFor="anfang">Zeit:</Label>
</Col>
<Col xs="12" md="9">
<SingleDatePicker
inputIconPosition="after"
block={false}
numberOfMonths={1}
date={this.props.date ? moment(this.props.date): null}
onDateChange={date => this.props.handleDateChange(date)}
focused={this.props.focused}
onFocusChange={this.props.toggleFocus}
openDirection="down"
hideKeyboardShortcutsPanel={true}
placeholder="Tag"
displayFormat={() => moment.localeData().longDateFormat('L')}
/>
</Col>
</FormGroup>
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={this.props.onChange}>Speichern</Button>{' '}
<Button color="secondary" onClick={this.props.onChange}>Abbrechen</Button>
</ModalFooter>
</Modal>
);
}
}
export default AppointmentModal;

Related

is there any way to disable send button until message got typed in the message field without onchange method in reactjs

I'm new to react, My objective is to disable send button until message got typed in the message field or to show some popup without writing anything in textfield and clicking on send button. For Disabling - I've tried by giving onChange method but due to onchange method input field is getting laggy. That's why i'm using Id. Can anyone suggest me any way of disabling the button ?
Here is the code:
class Data extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [
{ isSender: true, content: "hello" },
{ isSender: false, content: "hello1" },
{ isSender: true, content: "hello2" },
{ isSender: false, content: "hello3" },
{ isSender: true, content: "hello4" },
{ isSender: false, content: "hello5" },
{ isSender: true, content: "hello6" },
{ isSender: false, content: "hello7" }
],
msg: ""
};
}
componentDidMount() {
this.scrollToBottom();
}
scrollToBottom = () => {
this.messagesEnd.scrollIntoView({ behavior: "smooth" });
};
componentDidUpdate() {
this.scrollToBottom();
}
handleSubmit = e => {
e.preventDefault();
let text = document.getElementById("text").value;
console.log(text);
};
render() {
const { classes } = this.props;
return (
<Card>
<CardHeader
avatar={<Avatar aria-label="recipe">S</Avatar>}
title={
<>
<InputBase placeholder="Search Google Maps" margin="normal" />
<IconButton type="submit" aria-label="search">
<SearchIcon />
</IconButton>
</>
}
/>
<Divider />
<CardContent
style={{ overflow: "scroll" }}
className={classes.contentHeight}
id="chatList"
>
<div>
<Message isSender content="Hello" />
{this.state.data.map(item => {
if (item.isSender) {
return <Message isSender content={item.content} />;
}
return <Message content={item.content} />;
})}
</div>
<div
style={{ float: "left", clear: "both" }}
ref={el => {
this.messagesEnd = el;
}}
/>
</CardContent>
<Divider />
<CardActions>
<Paper className={classes.contentPaper}>
<Input
margin="dense"
className={classes.input}
placeholder="Enter a Message"
disableUnderline
name="msg"
id="text"
/>
<Button onClick={this.handleSubmit}>Send</Button>
</Paper>
</CardActions>
</Card>
);
}
}
Either the send button needs to disable or while clicking on the send button without typing anything in input field - it should show snackbar at the top of the textfield as "Enter something".
Can anyone please help me in this?
Here is the whole code:
You should update state on onChange for input
<Input
margin="dense"
className={classes.input}
placeholder="Enter a Message"
disableUnderline
name="msg"
id="text"
onChange={e => this.setState({ msg: e.target.value })}
/>
And disable attribute to Button
<Button onClick={this.handleSubmit} disabled={!this.state.msg.length}>
Send
</Button>
Try to avoid document.getElementById("text").value in react, it's better to use state or ref

How to delete an item on the front end from a list of items in ReactJS

I'm trying to delete an item from a list of items that I get dynamically from a REST API. For some reason, my onDelete function is being called when I press the Search button, instead of individually when I want to delete a specific list item. Not sure why.
class Users extends Component {
constructor(props) {
super(props);
this.state = {
searchValue: '',
users: []
};
}
handleOnChange = event => {
this.setState({ searchValue: event.target.value });
};
handleSearch = () => {
this.makeApiCall(this.state.searchValue);
};
onDelete(e) {
console.log('why is this being called?');
}
makeApiCall = async searchInput => {
let res = await axios(
`https://zuul-stage.whatifops.com/v1/user/phone/${searchInput}`
);
this.setState({ users: res.data });
};
render() {
return (
<div>
<input
name='text'
type='text'
placeholder='Search'
onChange={event => this.handleOnChange(event)}
value={this.state.searchValue}
/>
<button onClick={this.handleSearch}>Search</button>{' '}
{this.state.users ? (
<div>
{this.state.users.map((user, index) => (
<div key={user.id}>
<Row>
<Col lg={2} style={{ maxWidth: '9.7%' }}>
<Button
color='danger'
style={{ paddingTop: 12, paddingBottom: 12 }}
onClick={this.onDelete()}
>
<i className='fa fa-trash'></i> Delete
</Button>
</Col>
<Col lg={10}>
<ListGroup>
<ListGroupItem>
<strong>Email:</strong> {user.email}
<strong>Phone:</strong> {user.phone}
</ListGroupItem>
</ListGroup>
</Col>
</Row>
</div>
))}
</div>
) : (
<p>Try searching for a user</p>
)}
</div>
);
}
}
export default Users;
The onDelete function I was using was
onDelete(e){
let id = e.target.id;
let updatedUsers = this.users.filter(user=>user.id!=id)
this.setState({users:updatedUsers })
}
but I was getting an error about the users being undefined, and it was not being called individually onClick. Not sure what I am doing wrong, I thought this would be a simple thing to build but I'm struggling!
The issue is that the onDelete is being called (will get called automatically unless the following is changed)
change:
{this.onDelete()}
to:
{() => this.onDelete()}
or to (once onDelete is bounded correctly):
{this.onDelete}

React bootstrap modal not rendering updated data

When I'm running the below code and clicking the "Read" button, data is not updating in modal window even though I have few different components rendered.
I'm new to react, I have read similar posts that it has something to do with changing state but don't really know how to apply it in this case?
import React, { Component } from "react";
import Moment from "react-moment";
import Card from "react-bootstrap/Card";
import Button from "react-bootstrap/Button";
import ButtonToolbar from "react-bootstrap/ButtonToolbar";
import Row from "react-bootstrap/Row";
import Col from "react-bootstrap/Col";
import Container from "react-bootstrap/Container";
import CardDeck from "react-bootstrap/CardDeck";
import Modal from "react-bootstrap/Modal";
import "./posts.css";
const config = require("../../config").config();
class MyVerticallyCenteredModal extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<Modal
{...this.props}
size="lg"
aria-labelledby="contained-modal-title-vcenter"
centered
>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-vcenter">
{this.props.title}
</Modal.Title>
</Modal.Header>
<Modal.Body>
<pre>{this.props.title}</pre>
</Modal.Body>
<Modal.Footer>
<Button onClick={this.props.onHide}>Close</Button>
</Modal.Footer>
</Modal>
);
}
}
This is my parent component where I'm having button opening modal, post.title and post.body is not being updated on modal.
class Posts extends Component {
constructor() {
super();
this.state = {
posts: [],
modalShow: false
};
}
componentDidMount() {
fetch(config.localhostPrefix + "/api/stories")
.then(res => res.json())
.then(posts => this.setState({ posts }));
}
render() {
let modalClose = () => this.setState({ modalShow: false });
return (
<div className="Posts">
<h2>Posts</h2>
<CardDeck>
{this.state.posts.map(post => (
<Card key={post._id}>
<Card.Body>
<Card.Title>"{post.title}"</Card.Title>
<Card.Text>
{post.body}
<ButtonToolbar>
<Button
variant="primary"
onClick={() => this.setState({ modalShow: true })}
>
Read
</Button>
<MyVerticallyCenteredModal
show={this.state.modalShow}
title={post.title}
body={post.body}
onHide={modalClose}
/>
</ButtonToolbar>
</Card.Text>
</Card.Body>
<Card.Footer>
<Container>
<Row>
<Col>
<small className="text-muted">
Created:
<Moment format=" YYYY/MM/DD hh:mm">
{post.createdAt}
</Moment>{" "}
</small>
</Col>
</Row>
<Row>
<Col>
<small className="text-muted">
Updated:
<Moment format=" YYYY/MM/DD hh:mm">
{post.updatedAt}
</Moment>
</small>
</Col>
</Row>
<Row>
<Col>
<small className="text-muted">
Author: {post.author}{" "}
</small>
</Col>
</Row>
</Container>
</Card.Footer>
</Card>
))}
</CardDeck>
</div>
);
}
}
export default Posts;
Thanks for creating the sandbox. I was able to fix the issue you're having. I slightly modified your Posts component. You were close but had one or two things you missed out. Please see my changes below:
class Posts extends Component {
constructor() {
super();
this.state = {
posts: [
{ _id: 1, title: "title1", body: "body1", author: "author1" },
{ _id: 2, title: "title2", body: "body2", author: "author2" },
{ _id: 3, title: "title3", body: "body3", author: "author3" }
],
postId: null,
modalShow: false
};
}
modalClose = id => {
this.setState({ modalShow: !this.state.modalShow, postId: id });
};
renderModal = () => {
const { modalShow, postId, posts } = this.state;
const post = posts.find(post => (post._id === postId));
return (
<MyVerticallyCenteredModal
show={modalShow}
title={post.title}
body={post.body}
onHide={this.modalClose}
/>
);
};
render() {
return (
<div className="Posts">
<h2>Posts</h2>
<CardDeck>
{this.state.posts.map(post => (
<Card key={post._id + post.title}>
<Card.Body>
<Card.Title>"{post.title}"</Card.Title>
<Card.Text>
{post.body}
<ButtonToolbar>
<Button
variant="primary"
onClick={() => this.modalClose(post._id)}
>
Read
</Button>
</ButtonToolbar>
</Card.Text>
</Card.Body>
<Card.Footer>
<Container>
<Row>
<Col>
<small className="text-muted">
Author: {post.author}{" "}
</small>
</Col>
</Row>
</Container>
</Card.Footer>
</Card>
))}
</CardDeck>
{this.state.modalShow && this.renderModal()}
</div>
);
}
}
I hope this helps you.
It is not perfect but something to help you figure out what was wrong initially.

React-bootstrap Modal component opens/closes all modals when I map through a list

New to programming so I'm sorry if I'm not wording this correctly. I'm using a .map to render and list every single item on an array. For each item, I want the modal to open/close only the specific modal corresponding to each item in the array. However, when I click on the button to open the modal, every single one opens and closes. I believe this is because the modals are all set to an on/off button together. How can I set it it (with the .map value.id or something) so that only the specific modal opens and closes?
class DrinkMenu extends Component {
constructor(props, context) {
super(props, context);
this.state = {
show: false
};
this.handleHide = this.handleHide.bind(this);
}
handleHide() {
this.setState({ show: false });
}
async componentDidMount() {
let res = await axios.get('/getAllDrinks')
this.props.updateDrinkMenu(res.data)
}
async addToCart(drinkObj) {
let res = await axios.post('/addToCart', drinkObj)
console.log(`Added ${drinkObj.name} to order.`)
}
render() {
let drinkList = this.props.drinkMenu.map((drink) => {
return (
<div key={drink.id}>
<h5>{drink.name}</h5>
<h6>${drink.price}</h6>
<span
onClick={() => this.setState({ show: true })}
>
<strong>More Info</strong>
<br />
<button onClick={() => this.addToCart(drink)}>Add to Order</button>
</span>
<Modal
show={this.state.show}
onHide={this.handleHide}
container={this}
aria-labelledby="contained-modal-title"
>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title">
{drink.name}
</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>{drink.sub_category} | ABV {drink.abv}% | {drink.origin}</p>
<Col xs={6} md={4}>
<Image className="drink-logo" src={drink.logo} thumbnail />
</Col>
<p className="drink-description"><strong>Description</strong><br />{drink.description}</p>
<p href={drink.website}>Website</p>
</Modal.Body>
<Modal.Footer>
<Button onClick={this.handleHide}>Close</Button>
</Modal.Footer>
</Modal>
</div>
)
})
return (
<div>
<h2>Drink Menu</h2>
<div>
{drinkList}
</div>
</div>
)
}
}
From the code you have shared, I see that you are handling all the Model with the same state value, i.e. show. This is causing all the state for all the Models to be true hence all of them as shown.
To solve this, you can extract your whole component in a new React class which has just the functionality to show Modal as per the independent state. So your new React component will look something like this:
class DrinkComponent extends React.Component {
constructor(props) {
super(props);
this.handleHide = this.handleHide.bind(this);
this.state = {
show: false,
}
}
handleHide() {
this.setState({ show: false });
}
render() {
const { drink } = this.props;
return (<div key={drink.id}>
<h5>{drink.name}</h5>
<h6>${drink.price}</h6>
<span
onClick={() => this.setState({ show: true })}
>
<strong>More Info</strong>
<br />
<button onClick={() => this.props.addToCart(drink)}>Add to Order</button>
</span>
<Modal
show={this.state.show}
onHide={this.handleHide}
container={this}
aria-labelledby="contained-modal-title"
>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title">
{drink.name}
</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>{drink.sub_category} | ABV {drink.abv}% | {drink.origin}</p>
<Col xs={6} md={4}>
<Image className="drink-logo" src={drink.logo} thumbnail />
</Col>
<p className="drink-description"><strong>Description</strong><br />{drink.description}</p>
<p href={drink.website}>Website</p>
</Modal.Body>
<Modal.Footer>
<Button onClick={this.handleHide}>Close</Button>
</Modal.Footer>
</Modal>
</div>);
}
}
In this case, each DrinkComponent will have its independent state of showing and hiding of the model. Now we have to just modify your existing render function in DrinkMenu, to display DrinkComponent. So your render function will look something like this:
render() {
let drinkList = this.props.drinkMenu.map((drink) => (<DrinkComponent drink={drink} addToCart={this.addToCart}/>));
return (
<div>
<h2>Drink Menu</h2>
<div>
{drinkList}
</div>
</div>
)
}
Also you can remove the show state from DrinkMenu as it wont be needed there.
Hope it helps.

I cannot close the modal and dimmer in semantic - using react

I'm trying to get a modal to close in react, using semantic modal. For some reason, I can get the form to close, but the dimmer remains. I need help.
I have tried $('.ui.modal').modal('hide dimmer') and many number of other things.
Modal is here:
export default class AddCamerModal extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<Modal
id="add-camera-form"
trigger={<Button id="color-0093ee border-color-0093ee"
basic
icon="video-camera"
size="large"></Button>}
>
<Header icon='cube' content='New Object' />
<Modal.Content>
<AddCameraForm />
</Modal.Content>
</Modal>
)
}
Form is here:
export default class AddCameraForm extends React.Component {
constructor(props) {
super(props);
}
closeModal() {
$('.modal').modal('hide');
}
render() {
return (
<Form size="large">
<Form.Group widths="equal">
<Form.Field label='Name' control='input' placeholder='Name' name="name" id="name" required />
</Form.Group>
<Form.Group>
<Button type='submit' className="submit" onClick={this.handleSave}>Save</Button>
<Button type='deny' className="deny" onClick={this.closeModal}>Cancel</Button>
</Form.Group>
</Form>
)
}
}
You should pass 'open' prop false to your modal. You can do it via state or via props. For example:
export default class AddCamerModal extends React.Component {
constructor(props) {
super(props);
this.closeModal=this.closeModal.bind(this)
state={ isOpen: true }
}
closeModal() {
this.setState({isOpen: !this.state.isOpen});
}
render() {
return (
<Modal
open={this.props.open}
id="add-camera-form"
trigger={<Button id="color-0093ee border-color-0093ee"
basic
icon="video-camera"
size="large"></Button>}
>
<Header icon='cube' content='New Object' />
<Modal.Content>
<AddCameraForm closeModal={this.closeModal} />
</Modal.Content>
</Modal>
)
}
}
export default class AddCameraForm extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<Form size="large">
<Form.Group widths="equal">
<Form.Field label='Name' control='input' placeholder='Name' name="name" id="name" required />
</Form.Group>
<Form.Group>
<Button type='submit' className="submit" onClick={this.handleSave}>Save</Button>
<Button type='deny' className="deny" onClick={this.props.closeModal}>Cancel</Button>
</Form.Group>
</Form>
</div>
)
}
}
Maybe try this...
export default class AddCamerModal extends React.Component {
constructor(props) {
super(props);
this.state = {
open: false
};
}
render() {
return (
<Modal
id="add-camera-form"
open={this.state.open}
onClose={e => this.setState({ open: false })}
trigger={
<Button
id="color-0093ee border-color-0093ee"
basic
icon="video-camera"
size="large"
onClick={e => this.setState({ open: true })}
/>
}
>
<Header icon="cube" content="New Object" />
<Modal.Content>
<AddCameraForm />
</Modal.Content>
</Modal>
);
}
}
Okay, I want to post this just in case someone gets stuck in the same rabbit hole I was in. I had to set the open attribute in the modal, then pass that down to my AddCameraForm through the closeModal prop.
Here's the AddCameraModal:
export default class AddCameraModal extends React.Component {
constructor(props) {
super(props);
this.openModal=this.openModal.bind(this);
this.closeModal=this.closeModal.bind(this);
this.state = { isOpen: false }
}
openModal() {
this.setState({isOpen: true})
}
closeModal() {
this.setState({isOpen: false});
}
render() {
return (
<Modal
open={this.state.isOpen}
id="add-camera-form"
trigger={
<Button id="color-0093ee border-color-0093ee"
basic
icon="video-camera"
size="large" onClick={this.openModal}>
</Button>
}
>
<Header icon='cube' content='New Object' />
<Modal.Content>
<AddCameraForm closeModal={this.closeModal} openModal={this.openModal} />
</Modal.Content>
</Modal>
)
}
}
And here's the button's code from my AddCameraForm class:
<Button type='deny' className="cancel" onClick={this.props.closeModal}>Cancel</Button>

Resources