React bootstrap modal not rendering updated data - reactjs

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.

Related

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}

GET request does not have latest entry inside

I made an Event Feed in React. Here is the structure (I've removed unrelated components:
App.js
|_EventFeed.js
|_EventCreate.js
My problem is this:
When I create an event in EventCreate, I hope for the EventFeed to update with the latest entry but it does not happen. It does update eventually after a few seconds or refreshes - and I think this delay is because I'm using free Heroku dynos on the back-end.
How can I make it update automatically?
Code here:
EventFeed.js
import React from 'react';
import {
Button,
Row,
Col,
CardBody,
CardSubtitle,
Container,
CardText,
CardTitle,
Progress,
} from 'reactstrap';
import axios from 'axios';
import Loader from '../images/loader.gif';
import { Link } from "react-router-dom";
import nightWorkout from "../images/nightWorkout.jpg";
class EventFeed extends React.Component {
constructor(props) {
super(props);
this.state = {
message: true,
eventsList: [],
isLoading: true,
eventsJoined:0
}
}
//retrieve all events via axios
componentDidMount() {
axios.get('https://final-project-healthy.herokuapp.com/api/v1/events/')
.then(response => {
let tempEventList = response.data;
this.setState({ eventsList: tempEventList, isLoading: false })
})
.catch(error => {
console.log('ERROR: ', error);
})
}
refreshFeed = () =>{
axios.get('https://final-project-healthy.herokuapp.com/api/v1/events/')
.then(response => {
let tempEventList = response.data;
this.setState({ eventsList: tempEventList, isLoading: false })
})
.catch(error => {
console.log('ERROR: ', error);
})
}
componentDidUpdate(){
this.refreshFeed();
}
handleSubmit = e =>{
//make an api call to guestlist and add or delete current user to guest list
let event_id = e.target.id
let JWT = localStorage.getItem('userToken')
axios.post('https://final-project-healthy.herokuapp.com/api/v1/guestlists/',
{event_id:event_id},
{
headers:{
Authorization: `Bearer ${JWT}`
}
}
).then(response => {
console.log(response);
this.refreshFeed();
}).catch(error => {
console.log("ERROR in request: ", error)
})
}
render() {
const { eventsList, isLoading } = this.state;
return (
<div style={{backgroundImage: `url(${nightWorkout})`, backgroundSize:'cover', backgroundAttachment:'fixed', height:'100wh'}}>
<div>
<h1 className="text-center text-light shadow">Events Near You</h1>
<h4 className="text-center text-light shadow">Don't see a fitness meet you like? How about <Link to={'/events/create'}>creating your own</Link>?</h4>
{isLoading ?
<Container>
<Row className="d-flex align-items-center justify-content-center">
<img src={Loader} alt="Loading event feed" />
</Row>
</Container>
:
eventsList.map((eventInList) => {
let guestlist=[]
if (eventInList.guests.includes(this.props.current_user.id)){
console.log('The user is here!')
}
return (
<Container key={eventInList.id} className="my-2 bg-none">
<Row md="10" className="d-flex align-items-center rounded bg-info" style={{opacity:'0.9'}}>
<Col md="4" className=" d-flex justify-content-center align-item-center rounded">
<img width="200px" height="200px" className="border border-white m-3 rounded justify-content-center" src={eventInList.image} alt="event image" />
</Col>
<Col md="8" className="rounded">
<CardBody className="p-1 text-left">
<CardTitle><h3 className='text-light'>{eventInList.name}</h3></CardTitle>
<CardSubtitle className='text-light'>{eventInList.time}</CardSubtitle>
<CardText className='text-light'>Host : {eventInList.host.username}</CardText>
<CardText className='text-light'>Location:{eventInList.location}<br/>What to expect:{eventInList.description}</CardText>
<CardText className="text-light">Capacity: {eventInList.guests.length}/{eventInList.max_number}</CardText>
<Row className="align-items-center">
<Col md="8">
<Progress color="success" value={Math.floor(eventInList.guests.length/eventInList.max_number*100)} />
</Col>
<Col md="4">
{eventInList.guests.includes(this.props.current_user.id)
?
<Button id={eventInList.id} color="danger" onClick={this.handleSubmit}>Leave</Button>
:
<Button id={eventInList.id} color="success" onClick={this.handleSubmit}>Join</Button>
}
</Col>
</Row>
</CardBody>
</Col>
</Row>
</Container>)
})
}
</div>
</div>
)
}
}
export default EventFeed;
EventCreate.js
import React from 'react';
import {
Card,
CardImg,
CardText,
Button,
Col,
Form,
FormText,
FormGroup,
Input,
Label,
Row,
} from "reactstrap"
import axios from 'axios';
import PlacesWithStandaloneSearchBox from "../components/PlacesWithStandaloneSearchBox";
import { Link } from "react-router-dom";
import Loader from "../images/loader.gif";
import morningRun from "../images/morning_run.jpg"
class EventCreate extends React.Component {
constructor(props) {
super(props);
this.imageUpload=React.createRef();
this.state = {
eventName: '',
description: '',
location: '',
time: '',
maxNumber: '',
isLoading:false,
previewImage:null,
imageFile:null,
message:''
}
}
handleChange = e => {
this.setState({ [e.target.id]: e.target.value }, () => {
//for debugging purposes, this ensures that the state is updated with the field values
for (const key in this.state) {
if (this.state.hasOwnProperty(key)) {
console.log(key + " -> " + this.state[key] + ` of data type ${typeof (this.state[key])}`);
}
}
})
console.clear()
}
liftMyLocationUp = locationFromGoogle => {
this.setState({ location: locationFromGoogle })
}
//handleSubmit lifts up field values
handleSubmit = (e) => {
let formData = new FormData()
formData.append("image_file", this.state.imageFile, this.state.imageFile.name);
formData.append("name", this.state.eventName);
formData.append("description", this.state.description);
formData.append("max_number", this.state.maxNumber);
formData.append("time", this.state.time);
formData.append("location", this.state.location);
console.log('handleSubmit called')
this.registerEvent(formData);
setTimeout(() => this.props.history.push('/events'), 2000);
}
handleFile = (e) =>{
console.log(e.target.files[0])
this.setState({
imageFile:e.target.files[0],
previewImage:URL.createObjectURL(e.target.files[0]),
})
}
//necessary to setState? why not just keep it as props or make ?
registerEvent = (formData) => {
let JWT = localStorage.getItem('userToken')
axios.post('https://final-project-healthy.herokuapp.com/api/v1/events/',
formData,
{
headers: {
'Authorization': `Bearer ${JWT}`
}
}
).then(response => {
if (response.data.success){
console.log(response.data.success)
}
}).catch(error => {
console.log("ERROR in request: ", error);
})
}
handleClick = () =>{
this.imageUpload.current.click();
}
render() {
const { event_name, description, location, max_number, time, isLoading, previewImage, imageFile, message } = this.state
return (
<div style={{backgroundImage: `url(${morningRun})`, backgroundSize:'cover', height:'100vh'}}>
<h3 className="text-center text-light">Create Your Event Here:</h3>
<Row className="d-flex justify-content-center">
<Col lg="4" md="5" sm="12" className="d-flex justify-content-center">
<Form className="d-flex justify-content-center">
<FormGroup >
<Label for="eventName" className="text-light">Event Name:</Label>
<Input id="eventName" type="text" value={event_name} onChange={this.handleChange} placeholder="Give me a name"></Input>
<Label for="description" className="text-light">Description:</Label>
<Input id="description" type="text" value={description} onChange={this.handleChange} placeholder="Share what I'm about"></Input>
<Label for="location" className="text-light">Location:</Label>
<PlacesWithStandaloneSearchBox liftMyLocationUp={this.liftMyLocationUp} />
{/* <Input id="location" type="text" value={location} onChange={this.handleChange} placeholder="Tell folks where to find me"></Input> */}
<Label for="time" className="text-light">Time:</Label>
<Input id="time" type="datetime-local" value={time} onChange={this.handleChange}></Input>
<Label for="maxNumber" className="text-light">Max Number:</Label>
<Input id="maxNumber" type="number" min="0" value={max_number} onChange={this.handleChange} placeholder="It's always better with company"></Input>
<div> </div>
</FormGroup>
</Form>
</Col>
<Col lg="4" md="5" sm="12" className="d-flex justify-content-center">
<Row>
<Form>
<FormGroup>
<Label className="d-block text-light">Upload your event image here:</Label>
<div id="image-preview" onClick={this.handleClick}>
<Card style={{width:"300px", height:"300px"}} className="d-flex justify-content-center align-items-center">
{/* Set loading icon when uploading picture. nested ternary operator. */}
{!isLoading?(!imageFile?(<h4 className="text-center">{message ? message : "Click to upload"}</h4>):(<CardImg src={previewImage} height="300px" width="300px"/>)):<Loader/>}
</Card>
</div>
<input id="upload" style={{display:"none"}} type="file" accept="image/*" multiple={false} name="image-file" ref={this.imageUpload} id="imageFile" onChange={this.handleFile}></input>
</FormGroup>
<FormGroup>
<div id="image-submit-form">
<Row >
<Col md={12} sm={12}>
</Col>
</Row>
</div>
</FormGroup>
</Form>
</Row>
</Col>
</Row>
<Row>
<Col className="d-flex justify-content-center">
<Button color="primary" className="text-light" onClick={this.handleSubmit}>Create Event</Button>
</Col>
</Row>
</div>
)
}
}
export default EventCreate;

Input control is not editable on Multi Modal of react-bootstrap

I have two Modal Components in my application and on click of edit button in a Modal another Modal will open, and in the second Modal I have input control and its not editable
The following are the files which I used in my application.
import bootstrap css in index.js file as below
import 'bootstrap/dist/css/bootstrap.css'
App.js
function App() {
return (
<div>
<MyButton />
</div>
);
}
MyButton.js
export default class MyButton extends Component {
constructor(props) {
super(props);
this.state = {
personalInfoModalShow: false
};
}
render() {
let handlePersonalInfoModalHide = () => this.setState({
personalInfoModalShow: false
});
return (
<div>
<button className="btn btn-primary" onClick={() => this.setState({
personalInfoModalShow: true
})}
>
Open Modal
</button>
<PersonalInfoModal
show={this.state.personalInfoModalShow}
handlePersonalInfoModalHide={handlePersonalInfoModalHide}
/>
</div>
);
}
}
PersonalInfoModal.js
import React, {Component} from 'react';
import Modal from "react-bootstrap/Modal";
import {Container, Table} from "reactstrap";
import {FaPencilAlt} from "react-icons/fa";
import EditModal from "./EditModal";
export default class PersonalInfoModal extends Component {
constructor(props) {
super(props);
this.state = {
id: 100,
name: 'Sam',
department: 'IT',
isEditClicked: false,
editModalShow: false
};
}
render() {
let handleEditModalHide = () => this.setState({
isEditClicked: false,
editModalShow: false
});
return (
<div>
<Modal
show={this.props.show}
onHide={this.props.handlePersonalInfoModalHide}
size="lg"
centered
>
<Modal.Header closeButton>
<Modal.Title id="personal-modal-title">
Personal Information
</Modal.Title>
</Modal.Header>
<Modal.Body>
<Container fluid>
<Table striped responsive size="sm" className="text-center mt-4">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Department</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td>{this.state.id}</td>
<td>{this.state.name}</td>
<td>{this.state.department}</td>
<td>
<div onClick={() => this.setState({
isEditClicked: true,
editModalShow: true
})}
>
<FaPencilAlt />
</div>
{this.state.isEditClicked &&
<EditModal
show={this.state.editModalShow}
handleEditModalHide={handleEditModalHide}
department={this.state.department}
/>
}
</td>
</tr>
</tbody>
</Table>
</Container>
</Modal.Body>
</Modal>
</div>
);
}
}
EditModal.js
import React, {Component} from 'react';
import {Button, Modal} from "react-bootstrap";
export default class EditModal extends Component {
constructor(props) {
super(props);
this.state = {
department: props.department
};
}
handleChange = (e) => {
const input = e.target;
const name = input.name;
const value = input.value;
this.setState({[name]: value});
};
render() {
return (
<div>
<Modal
show={this.props.show}
onHide={this.props.handleEditModalHide}
size="sm"
centered
>
<Modal.Header closeButton>
<Modal.Title id="edit-modal-title">
Edit
</Modal.Title>
</Modal.Header>
<Modal.Body>
<input style={{width: "35%"}} type="text" value={this.state.department} name="department" onChange={this.handleChange} />
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={this.props.handleEditModalHide}>
Close
</Button>
<Button variant="primary" onClick={() => alert("saved")}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
</div>
);
}
}

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.

Set state of parent from child component

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;

Resources