react state not being updated - reactjs

I am trying to toggle the info window in react-google-maps off and on through a custom component. The toggle method is being called as I checked if it's logged. Here's the code:
/**
* Created by.
*/
import * as React from 'react'
import {Col, Row, Card, CardHeader, CardBody, CardColumns, CardText, CardFooter} from 'reactstrap'
import {InfoWindow, Marker} from 'react-google-maps'
export default class home extends React.Component {
state = {
isOpen: false
}
toggleOpen = () => {
this.setState(({ isOpen }) => (
{
isOpen: !isOpen,
}
));
if(this.state.isOpen)
console.log("state is open")
else
console.log("state is not open")
}
render()
{
const { isOpen } = this.state;
return (
<Marker
position={this.props.position}
onClick={this.toggleOpen}>
<InfoWindow isOpen={isOpen}>
<Card className="hovercard">
<Row>
<CardColumns sm={6} lg={3}>a
<CardHeader>
{this.props.homestay}
</CardHeader>
<CardText className="avatar">
<img alt="profile img" src={this.props.profilePic}/>
</CardText>
<div className="info">
<CardText>{this.props.descrip}</CardText>
</div>
<CardFooter>
{this.props.price}
</CardFooter>
</CardColumns>
</Row>
</Card>
</InfoWindow>
</Marker>
)
}
}
The infowindow is not opening when I click it. Any ideas?
EDIT ----
I changed the toggle method as you mentioned but the toggle is still not responding. Here's my project in sandeditor:https://codesandbox.io/s/93258nn8m4

You will need to put state in constructor for first time initialization or to create instance of the class, However not necessary always but use it to keep best practice.
constructor() {
this.state = {
isOpen: false
}
}
Also, I don't know if it is the correct way to setState
change this
toggleOpen = () => {
this.setState(({ isOpen }) => (
{
isOpen: !isOpen,
}
));
if(this.state.isOpen)
console.log("state is open")
else
console.log("state is not open")
}
to this
toggleOpen = () => {
this.setState({isOpen: !this.state.isOpen},() => {
if(this.state.isOpen)
console.log("state is open")
else
console.log("state is not open")
}
);
}
P.S. Beware because React setState is asynchronous!
Update
Just noticed that if you don't define a constructor in the class that is ok. Because The constructor is dead.
However, Your transpiler still will generate it in a constructor form which is satisfying.
So that part of your question; setting state without a constructor is fine but to setState is definitely not.

This reset the isOpen value.Once you are setting to current value and in callback you are toggling it.
this.setState(({ isOpen }) => (
{
isOpen: !isOpen,
}
));
Correct way:
this.setState(prevState) => (
{
isOpen: !isOpen,
}
));

Related

Error: out of memory when trying to run create-react-app

When I run npm start in chrome (create-react-app) it takes a while to load and then it crashes. the Error displays "out of memory". I cleared all my tabs and cache, and since other apps run just fine I have been able to tell the error is in the code. When I removed the Reserve_Button Component the application ran just fine. I think there may be some sort of memory leak related to SetState but I'm relatively new to React so there is not much more I can tell.
import React from "react";
import "./reserve_button.css";
// import Reserve_Form from "./Reserve_Form";
// import { connect } from "react-redux";
// import { submitreservation } from "../../Actions/index";
import Modal from "./modal";
class Button extends React.Component {
state = {
firstname: "",
Lastname: "",
};
showmodal = {
open: false,
};
// onSubmit = (formValues) => {
// this.props.submitreservation(formValues);
// };
showModal() {
if (this.state.open) {
return (
<Modal
open={this.state.open}
onDismiss={() => this.setState({ open: false })}
title="Modal Title"
content="Modal Body"
actions={<div className="ui button">Button</div>}
/>
);
}
}
render() {
return (
<div className="body">
<Button
onClick={() => this.setState({ open: !this.state.open })}
className="neon-button"
>
Reserve session
</Button>
{this.showModal()}
</div>
);
}
}
// <Reserve_Form onSubmit={this.onSubmit} />;
// export default connect(null, { submitreservation })(Button);
export default Button;
this is the Modal:
import React from "react";
import ReactDOM from "react-dom";
const Modal = (props) => {
return ReactDOM.createPortal(
<div onClick={props.onDismiss}>
<div onClick={(e) => e.stopPropagation()}>
<i onClick={props.onDismiss}></i>
<div className="header">{props.title}</div>
<div className="content">{props.content}</div>
<div className="actions">{props.actions}</div>
</div>
</div>,
document.querySelector("section")
);
};
export default Modal;
there are no set class names in a CSS file for the modal but I don't think that CSS would be the source of the problem.
UPDATE: when I console log this.state.open it prints "false" like 1000 times a second
There is an optimization you can make to your code. Perhaps it will help fix the error:
Instead of
<Button
onClick={() => this.setState({ open: !this.state.open })}
className="neon-button"
>
when relying on the previous state of a react component use the previous state to update the value. It looks something like this:
<Button
onClick={() => this.setState((prevState) => { open: !prevState.open })}
className="neon-button"
>
By doing this, we are referencing the previous state of this.state.open. This is particularly important as not all state updates happen instantaneously and so there is a possibility of referencing an older value of this.state.open in your approach.

Calling an onclick event from a different Component

I am desperatly trying to make my code works between 2 components by using the onclick event of my modal component to my Avaibalities component but nothing happens.
How can i make the value of my state ShowModal works?
Avaibalities Component
import React from 'react';
import Calendar from 'react-calendar';
import Modal from '../pages/Modal';
class Avaibalities extends React.Component {
state = {
date: new Date(),
showDate: false,
showModal: false,
};
onChange = (date) => {
this.setState({ date });
};
validation = () => {
this.setState({
showDate: true,
});
};
togglePop = () => {
this.setState({
showModal: true
});
};
render() {
return (
<div>
<div className="home">
<div className="calendarContent">
<div>
<Calendar
onChange={this.onChange}
value={this.state.date}
locale="en-GB"
/>
<>
<button className={'button'}>Validate</button>
<div>
{this.state.showModal ? (
<Modal toggle={this.togglePop} />
) : null}
</>
{this.state.showDate ? (
<div>
<p>
From : {this.state.date[0].toLocaleDateString()} to :{' '}
{this.state.date[1].toLocaleDateString()}
</p>
</div>
) : null}
</div>
</div>
</div>
);
}
}
export default Avaibalities;
Modal Component
import React from 'react';
class Modal extends React.Component {
handleClick = () => {
this.props.toggle();
};
render() {
return (
<div className="modal">
<div className="modal_content">
<span className="close" onClick={this.handleClick}>×</span>
<p>I'm A Pop Up!!!</p>
</div>
</div>
);
}
}
export default Modal;
Looking for someone to help me,
Thank you very much
You do not render your modal in this code because I see that showModal initially false and to set it as true you call tooglePop function as a props in Modal. But render of Modal component depends on showModal state so it never renders because initially false
You can't expect something to happen here.
To display your modal, you need this.state.showModal = true. In your modal, you are setting showModal to true but it's not different than te previous state, so nothing happens.
Change your method as follow :
togglePop = () => {
this.setState(prevState => ({
...prevState,
showModal: !prevState.showModal
}));
};

no longer show popup if user has subscribed in React (LocalStorage)

The popup show up after 1 sec. But I need to show it only to user who doesn't subscribe yet, by using localStorage. I did try use local storage like this code below, but then all I've got is a blank white page when it's time to show/not show popup. Is the localStorage I coded was totally wrong? Please help
import React from 'react'
import styled from 'react-emotion'
import Rodal from 'rodal'
import '../styles/rodal.css'
import Delayed from '../components/Delayed'
const Signup = () => (
<Containers>
<SubsribtionForm
action="https://subscribe/post?/....."
method="post"
>
<SubscribeInput type="email" name="EMAIL" placeholder="Subscribe to Updates!" required />
<Button type="submit" onClick={this.submit}>Add Me</Button>
</SubsribtionForm>
</Containers>
)
export default class Popup extends React.Component {
constructor(props) {
super(props)
this.state = { visible: true }
if (localStorage.getItem('submit')) {
this.setState({ visible: false })
}
this.submits = this.submits.bind(this)
}
submits() {
const newsub = this.state.submit
localStorage.setItem('submit', newsub)
}
show() {
this.setState({ visible: true })
}
hide() {
this.setState({ visible: false })
}
render() {
return (
<div>
<Container>
<Delayed waitBeforeShow={1000}>
<Rodal
visible={this.state.visible}
onClose={this.hide.bind(this)}
width={500}
height="100%"
customStyles={customStyles}
>
<Box>
<Banner />
<ContainerContent>
<Header>Subscribe to our mailing list</Header>
<Words>
We will organize and send regular updates Stay informed!
</Words>
</ContainerContent>
<ContainerForm>
<Signup />
</ContainerForm>
</Box>
</Rodal>
</Delayed>
</Container>
</div>
)
}
}
constructor(props) {
super(props)
this.state = {
visible: !(localStorage.getItem('submit') !== '' && localStorage.getItem('submit') !== null),
}
this.submits = this.submits.bind(this)
}
Just check if submit is not empty, like above.
Another approach would be to do the following in componentDidMount life cycle
componentDidMount() {
if (localStorage.getItem('submit')) {
this.setState({ visible: false })
}
}
You are calling this.setState inside the class constructor, you can use a simple conditional in this.state to assign the value directly and please use the bind in the constructor :D. I use the length because if the string is '' the length is 0 then that value in the conditional is false
import React from 'react'
import styled from 'react-emotion'
import Rodal from 'rodal'
import '../styles/rodal.css'
import Delayed from '../components/Delayed'
const Signup = () => (
<Containers>
<SubsribtionForm
action="https://subscribe/post?/....."
method="post"
>
<SubscribeInput type="email" name="EMAIL" placeholder="Subscribe to Updates!" required />
<Button type="submit" onClick={this.submit}>Add Me</Button>
</SubsribtionForm>
</Containers>
)
export default class Popup extends React.Component {
constructor(props) {
super(props)
const submit = localStorage.getItem('submit')
this.state = { visible: !submit && !submit.length }
this.submits = this.submits.bind(this)
this.show = this.show.bind(this)
this.hide = this.hide.bind(this)
}
submits() {
const newsub = this.state.submit
localStorage.setItem('submit', newsub)
}
show() {
this.setState({ visible: true })
}
hide() {
this.setState({ visible: false })
}
render() {
return (
<div>
<Container>
<Delayed waitBeforeShow={1000}>
<Rodal
visible={this.state.visible}
onClose={this.hide}
width={500}
height="100%"
customStyles={customStyles}
>
<Box>
<Banner />
<ContainerContent>
<Header>Subscribe to our mailing list</Header>
<Words>
We will organize and send regular updates Stay informed!
</Words>
</ContainerContent>
<ContainerForm>
<Signup />
</ContainerForm>
</Box>
</Rodal>
</Delayed>
</Container>
</div>
)
}
}

setState won't work in handleClick

my setState doesn't chance the state in the handleClick event handler.
I'm sure the handleClick works because it logs the param.
I'm kind of new to React so I must be overlooking something.
Does this mean there is something wrong with my handleClick function?
Any advice would be really appreciated!
import React from 'react';
import './Projects.css';
import Footer from '../../Components/Footer/Footer.js';
import ProjectPage from
'../../Components/ProjectPage/ProjectPage.js';
import { Redirect, Link } from 'react-router-dom';
class Projects extends React.Component {
constructor(props) {
super(props);
this.state= {
title: "kaufmann house",
content: "charles",
}
this.getImages = this.getImages.bind(this);
}
getImages() {
var VisibilitySensor = require('react-visibility-sensor');
return this.props.projectList.map((post,index) =>
<div>
<div className="projects">
<VisibilitySensor onChange={isVisible =>
this._onChange(isVisible, post.title)}>
<img key={post.id} src={post.featureImage}
className='projectImage' alt='projectImage' onClick= .
{this.handleClick.bind(this, post.content)}/>
</VisibilitySensor>
</div>
</div>
)
}
_onChange = (isVisible, param) => {
isVisible && this.setState({title: param});
};
handleClick = (param) => {
console.log(param);
this.setState({content: param});
};
render() {
return (
<div>
<Link to={{pathname: `/ProjectPage/${this.state.title}`,
state: {
info: `${this.state.content}`}
}}>{this.getImages()}</Link>
<Link to={{pathname: `/ProjectPage/${this.state.title}`,
state: {
info: `${this.state.content}`}
}}>
<Footer title={this.state.title}/>
</Link>
</div>
)
}
}
export default Projects;
this.state= {
title: "kaufmann house",
content: "charles",
}
Your state contains title and content. You have to setState like below. Otherwise, your new state will not update correctly because you replaced the whole state object.
_onChange = (isVisible, param) => {
isVisible && this.setState({
...this.state,
title: param
});
};
handleClick = (param) => {
console.log(param);
this.setState({
...this.state,
content: param
});
};
I would suggest the following changes:
1) Move var VisibilitySensor = require('react-visibility-sensor');
to the top of your file to keep your component clean
import React from 'react';
import './Projects.css';
import Footer from '../../Components/Footer/Footer.js';
import ProjectPage from
'../../Components/ProjectPage/ProjectPage.js';
import { Redirect, Link } from 'react-router-dom';
import VisibilitySensor from 'react-visibility-sensor';
2) Regarding your click handler, it is a bad practice to create handler functions using bind, because this may cause a performance issue since a new function will be created on each render. you can use an arrow function and set data-[attribute]
to add data to your component
getImages() {
//var VisibilitySensor = require('react-visibility-sensor'); remove this line
return this.props.projectList.map((post,index) => (
<div key={post.id}>
<div className="projects">
<VisibilitySensor onChange={isVisible =>
this._onChange(isVisible, post.title)}>
<img src={post.featureImage}
data-content={post.content}
className='projectImage'
alt='projectImage'
onClick={this.handleClick}/>
</VisibilitySensor>
</div>
</div>
))
}
handleClick = (e) => {
var content = e.target.dataset.content;
this.setState((state) => ({
...state,
content
}))
}

How can I render the same modal component into a list array itens in React?

I need to render a modal/lightbox component dynamic into a list array component, but it only renders the last modal content.
How can I turn this modal component dynamic to call it from the main component and populate it with correct data from an object array?
My List component is:
import React, { Component } from 'react';
import LightBox from './LightBox';
class ListPrice extends Component {
constructor(props) {
super(props);
this.state = { isOpen: false };
}
toggleModal = () => {
this.setState({
isOpen: !this.state.isOpen
});
}
render() {
return (
<div>
{this.props.products.map(product => {
return(
<div>
<a key={product.id} onClick={this.toggleModal}>
<h3>{product.title}</h3>
<p>{product.description}</p>
</a>
<LightBox key={product.id} show={this.state.isOpen}
onClose={this.toggleModal}>
{product.modalContent}
</LightBox>
</div>
);
})}
</div>
);
}
}
export default ListPrice;
And my LightBox component is (I removed styles to display short code here):
import React from 'react';
import PropTypes from 'prop-types';
class LightBox extends React.Component {
render() {
if(!this.props.show) {
return null;
}
return (
<div>
<div>
{this.props.children}
<div>
<button onClick={this.props.onClose}>
Close
</button>
</div>
</div>
</div>
);
}
}
LightBox.propTypes = {
onClose: PropTypes.func.isRequired,
show: PropTypes.bool,
children: PropTypes.node
};
export default LightBox;
Thank you for any advice :)
With show={this.state.isOpen} you always display all the modals - only the last one is visible as other modals are displayed behind it.
In order to fix that you must show only the selected dialog. You can store opened dialog in state with construct like this.setState({ openedDialog: product.id }).
Then you can query if the dialog is open by using this.state.openedDialog === product.id. That should do the job.
openModal = (id) = () => {
this.setState({
openedDialog: id
});
}
closeModal = () => {
this.setState({
openedDialog: null
});
}
show={this.state.openedDialog === product.id}
onClick={this.openModal(product.id)}
onClose={this.closeModal}

Resources