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

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>
)
}
}

Related

React, problem with passing state as props

So I am quite new to React world, and I have this problem I am trying to solve, but I don't quite understand why it is happening.
So I want to pass the state of component to parent component and from parent component to child component and everything look okay, and in console log the state goes trough, but nothing changes. I believe there is a way I need to listen for state change or something within child component so it works. If I put true in the parent component, child component also get's true, but if I toggle it on click, it goes trough but nothing changes in the child component.
Also I understand my code is little rough right now ill reafactor it later, but right now I am trying to understand why it does not work.
If anyone could help me I would be thankful for it.
This is component that controls the state.. So the state passes from TurnOnBtn to App and from App it goes to TodoList
import "./Todo.css";
class TurnOnBtn extends Component {
constructor(props) {
super(props);
this.state = { display: false };
this.handleState = this.handleState.bind(this);
}
handleState() {
this.setState({ display: !this.state.display });
this.props.checkDisplay(this.state.display);
}
render() {
return (
<button onClick={this.handleState} className="TurnOnBtn">
<i className="fa fa-power-off"></i>
</button>
);
}
}
export default TurnOnBtn;
parent component App
import TurnOnBtn from "./TurnOnBtn";
import TheMatrix from "./TheMatrxHasYou";
import TodoList from "./TodoList";
import { Component } from "react";
class App extends Component {
constructor(props) {
super(props);
this.state = { display: true };
this.checkDisplay = this.checkDisplay.bind(this);
}
checkDisplay(newDisplay) {
this.setState({
display: newDisplay,
});
console.log(this.state);
}
render() {
return (
<div className="App">
<TodoList display={this.state.display} />
<TheMatrix />
<TurnOnBtn checkDisplay={this.checkDisplay} />
</div>
);
}
}
export default App;
child component TodoList
import Todo from "./Todo";
import NewTodoForm from "./NewTodoForm";
import { v4 as uuid } from "uuid";
import "./Todo.css";
class TodoList extends Component {
constructor(props) {
super(props);
this.state = {
todos: [],
displayOn: this.props.display,
};
this.newTodo = this.newTodo.bind(this);
this.editTodo = this.editTodo.bind(this);
this.deleteTodo = this.deleteTodo.bind(this);
}
editTodo(id, updatedTask) {
const updatedTodo = this.state.todos.map((todo) => {
if (todo.id === id) {
return { ...todo, todo: updatedTask };
}
return todo;
});
this.setState({
todos: updatedTodo,
});
console.log(updatedTask);
}
deleteTodo(id) {
this.setState({
todos: this.state.todos.filter((todo) => todo.id !== id),
});
}
newTodo(newState) {
this.setState({
todos: [...this.state.todos, { ...newState }],
});
}
render() {
return (
<div
style={this.state.displayOn ? { opacity: 1 } : { opacity: 0 }}
className="Todo-screen"
>
{" "}
<div className="TodoList">
<div className="TodoList-todos">
{" "}
{this.state.todos.map((todo) => (
<Todo
key={uuid()}
id={todo.id}
active={todo.active}
editTodo={this.editTodo}
deleteTodo={this.deleteTodo}
todoItem={todo.todo}
/>
))}
</div>
</div>{" "}
<NewTodoForm newTodo={this.newTodo} />
</div>
);
}
}
export default TodoList;
The bug here is in these line of codes:
handleState() {
this.setState({ display: !this.state.display });
this.props.checkDisplay(this.state.display);
}
Remember setState is an async function, so by the time you set a new state using setState, the value for this.state is not guaranteed changed.
One way to fix this is using the setState callback, which will run after the state is changed:
handleState() {
this.setState({ display: !this.state.display }, function() {
this.props.checkDisplay(this.state.display);
});
}
But you don't need to use another state to keep display state in TurnOnBtn as you can pass the toggle callback from the parent:
App.js
class App extends Component {
constructor(props) {
super(props);
this.state = { display: true };
this.toggleDisplay = this.toggleDisplay.bind(this);
}
toggleDisplay() {
this.setState({
display: !this.state.display,
});
}
render() {
return (
<div className="App">
<TodoList display={this.state.display} />
<TheMatrix />
<TurnOnBtn toggleDisplay={this.toggleDisplay} />
</div>
);
}
}
TurnOnBtn.js
class TurnOnBtn extends Component {
constructor(props) {
super(props);
this.handleState = this.handleState.bind(this);
}
handleState() {
this.props.toggleDisplay();
}
render() {
return (
<button onClick={this.handleState} className="TurnOnBtn">
<i className="fa fa-power-off"></i>
</button>
);
}
}

React checkbox local storage

I am building a To-Do List web app with React as my first project.
I want to implement local storage which works fine only that,I am unable to handle check and uncheck of the checkbox prefectly.
Here is a link to the deployed website so you can understand the problem I am having.
https://rapture-todo.netlify.app/
When you add a todo, and mark it complete.
on reload, the checkbox of the todo is unchecked but the todo is marked complete.
Here is my source code[github link- https://github.com/coolpythoncodes/React-ToDo-List].
For App.js
import React, { Component } from 'react';
import Header from './component/Header';
import Info from './component/Info';
import AddToDo from './component/AddToDo';
import TodoListItem from './component/TodoListItem';
import './sass/main.scss';
class App extends Component{
constructor(props){
super(props);
this.state= {
value: '',
list: [],
show: true,
};
this.handleChange= this.handleChange.bind(this);
this.handleSubmit= this.handleSubmit.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
this.deleteTask = this.deleteTask.bind(this);
}
componentDidMount() {
const list = window.localStorage.getItem('userTodo') ? JSON.parse(localStorage.getItem('userTodo')) : [];
this.setState({ list })
}
handleChange(e) {
this.setState({value:e.target.value})
}
// Handle submission of user todo item
handleSubmit(e) {
e.preventDefault();
const newTask = {
id: Date.now(),
userTodo: this.state.value,
isCompleted: false,
checked: false,
}
// Validate form so user doesn't add an empty to do
if (this.state.value.length > 0) {
this.setState({
list: [newTask, ...this.state.list],
value: '', // Clear input field
show: true, // Success message
}, ()=>{
window.localStorage.setItem('userTodo', JSON.stringify(this.state.list));
})
}
}
// Handles checkbox
handleInputChange(id) {
this.setState({list: this.state.list.map(item => {
if (item.id === id) {
item.isCompleted = !item.isCompleted;
item.checked = !this.state.checked;
}return item
})}, ()=>{
window.localStorage.setItem('userTodo', JSON.stringify(this.state.list));
})
}
// Delete a task
deleteTask(id){
this.setState({list: this.state.list.filter(item => item.id !== id )},()=>{
window.localStorage.setItem('userTodo', JSON.stringify(this.state.list))
})
console.log(this.state.list)
}
render(){
return(
<div>
<Header />
<Info />
<AddToDo onChange={this.handleChange} value={this.state.value} onSubmit={this.handleSubmit} />
<TodoListItem deleteTask={this.deleteTask} onChange={this.handleInputChange} list={this.state.list} defaultChecked={this.state.checked} />
</div>
)
}
}
export default App;
For TodoListItem.js
import React, { Component } from 'react';
import ToDoItem from './ToDoItem';
import '../sass/main.scss';
class ToDoListItem extends Component{
render(){
const {list, onChange, deleteTask, defaultChecked} = this.props;
return(
<div>
{list.map((todo)=>{
return (
<ToDoItem
key={todo.id}
userTodo={todo.userTodo}
isCompleted={todo.isCompleted}
onChange={onChange}
id={todo.id}
deleteTask={deleteTask}
defaultChecked={defaultChecked}
/>
)
})}
</div>
)
}
}
export default ToDoListItem;
For TodoItem.js
import React, { Component } from 'react';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faTrashAlt } from '#fortawesome/free-solid-svg-icons'
import '../sass/main.scss';
class ToDoItem extends Component{
render(){
const {userTodo, isCompleted, onChange, id, deleteTask, defaultChecked} = this.props;
const checkStyle = isCompleted ? 'completed-todo' : 'not-completed-todo';
return(
<div className={`container ${checkStyle}`}>
<input type="checkbox" onChange={onChange.bind(this, id)} defaultChecked={defaultChecked}/>
<div >
<p className='title'>{userTodo}</p>
</div>
{/* Delete button */}
<button onClick={deleteTask.bind(this, id)}><FontAwesomeIcon className='remove-icon' icon={faTrashAlt} /></button>
</div>
)
}
}
export default ToDoItem;
Please note: I have gone through other questions similar to the problem I am having but I could not solve this problem.
If I did not state the question well, please let me know.
In the below code in App.js,
<TodoListItem deleteTask={this.deleteTask} onChange={this.handleInputChange} list={this.state.list} defaultChecked={this.state.checked} />
You are setting, defaultChecked={this.state.checked} Why do you do that? There is nothing called checked in the state.
In fact, there is no need to pass the defaultValue.
Make the following changes,
In App.js, remove defaultValue prop for TodoListItem
<TodoListItem deleteTask={this.deleteTask} onChange={this.handleInputChange} list={this.state.list}/>
In TodoListItem.js, remove defaultChecked={defaultChecked}
<ToDoItem
key={todo.id}
userTodo={todo.userTodo}
isCompleted={todo.isCompleted}
onChange={onChange}
id={todo.id}
deleteTask={deleteTask}
defaultChecked={defaultChecked} // Remove this.
/>
In ToDoItem.js,
<input type="checkbox"onChange={onChange.bind(this, id)}
defaultChecked={isCompleted} // Replace defaultValue with isCompleted
/>

Reset State on Props Change

So, I have three components(Search, Pages, HanddlesApi) plus App.js and I'm passing props around via functions to other child components with no problem.
The Search component passes it's state of userInput to HandleApi component and updates the api using componentDidUpdate. (this works great, np here).
I added the Pages component to update the api's page number so that the user can cycle through pages of content. This works, but with issues. The user can do a search and cycle though the pages, but if they enter a new query, they will land  on the same page number of the new query. For example, If
I searched "ducks" and clicked to the next page(2). Then did a search for "dogs" they would land on page two of "dogs"
So my question is how do I reset state for my Pages component only when a user enters a new query?
I saw that componentWillReceiveProps is being deprecated, so I can't use that.
getDerivedStateFromProps  seemed like it might be a good idea, but from what I read it should only be used in rare cases.
So, the two most likely options seemed to be, use componentDidUpdate in a way I don't understand or use key?
Overall I'm just confused on what to do
In my HanddlesApi Component I'm passing the follwoing into the API:
q: this.props.inputValue ? this.props.inputValue : 'news',
page: this.props.pageNum ? this.props.pageNum: 0
then..
componentDidMount() {
this.fetchNews()
}
componentDidUpdate(prevProps, prevState) {
if (this.props.inputValue !== prevProps.inputValue || this.props.pageNum !== prevProps.pageNum) {
this.setState({
news: []
}, this.fetchNews);
}
}
Then in my Pages Component, I have
import React, { Component } from 'react'
class Pages extends Component {
constructor(props) {
super(props)
this.state = {
nextPage: 1,
prevPage: 0
}
}
handleNextClick = () => {
this.setState({
nextPage: this.state.nextPage + 1,
})
}
handlePrevClick = () => {
this.setState({
prevPage: this.state.prevPage - 1,
})
}
render() {
return (
<div className='pageNav'>
<button className="PrevButton" onClick={() => {
this.handlePrevClick()
this.props.onNextButtonClick(this.state.prevPage)
}}>Previous </button>
<button className="nextButton" onClick={() => {
this.handleNextClick()
this.props.onNextButtonClick(this.state.nextPage)
}}>Next </button>
</div>
)
}
}
export default Pages
Search Component
import React, { Component } from 'react';
class SearchBar extends Component {
constructor(props) {
super(props)
this.state = {
inputValue: ""
}
}
handleChange = (e) => {
this.setState({
inputValue: e.target.value
})
}
handleSubmit = (e) => {
e.preventDefault()
this.props.onSubmittedSearch(this.state.inputValue)
}
render() {
//{this.props.onSubmittedSearch(this.state.inputValue)}
return (
<section>
<form onSubmit={this.handleSubmit}>
<label htmlFor="searching"></label>
<input type="text" placeholder="Search Something" value={this.state.inputValue} onChange={this.handleChange} />
<button type="submit">Search </button>
</form>
</section>
)
}
}
export default SearchBar
App.js
class App extends Component {
constructor(props) {
super(props)
this.state = {
inputValue: null,
pageNum: 1
}
}
// used to pass props from SearchBar to NewsList
onSubmittedSearch = (inputValue) => {
this.setState({
inputValue: inputValue
})
}
onNextButtonClick = (pageNum) => {
this.setState({
pageNum: pageNum
})
}
render() {
return (
<main>
<SearchBar onSubmittedSearch={this.onSubmittedSearch} />
<NewsList inputValue={this.state.inputValue} pageNum={this.state.pageNum} />
<Pages onNextButtonClick={this.onNextButtonClick} />
<Footer />
</main>
)
}
}
export default App;
You should let App in charge of changing and holding the current page number. So you can reset it each time your search component submit. Here is a working exemple:
class Pages extends React.Component {
render() {
return (<div className='pageNav'>
<button disabled={this.props.page <= 1} className="PrevButton" onClick={this.props.onPrevButtonClick}>Previous
</button>
<span>{this.props.page}</span>
<button className="nextButton" onClick={this.props.onNextButtonClick}>Next
</button>
</div>)
}
}
class SearchBar extends React.Component {
constructor(props) {
super(props)
this.state = {
inputValue: ""
}
}
handleChange = (e) => {
this.setState({inputValue: e.target.value})
}
handleSubmit = (e) => {
e.preventDefault()
this.props.onSubmittedSearch(this.state.inputValue)
}
render() {
//{this.props.onSubmittedSearch(this.state.inputValue)}
return (<section>
<form onSubmit={this.handleSubmit}>
<label htmlFor="searching"></label>
<input type="text" placeholder="Search Something" value={this.state.inputValue} onChange={this.handleChange}/>
<button type="submit">Search
</button>
</form>
</section>)
}
}
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
inputValue: null,
pageNum: 1
}
}
// used to pass props from SearchBar to NewsList
onSubmittedSearch = (inputValue) => {
this.setState({inputValue: inputValue, pageNum: 1})
}
onNextButtonClick = () => {
this.setState(state => ({
pageNum: state.pageNum + 1
}))
}
onPrevButtonClick = (pageNum) => {
this.setState(state => ({
pageNum: Math.max(state.pageNum - 1, 1)
}))
}
render() {
return (<main>
<SearchBar onSubmittedSearch={this.onSubmittedSearch}/>
<Pages onNextButtonClick={this.onNextButtonClick} onPrevButtonClick={this.onPrevButtonClick} page={this.state.pageNum}/>
</main>)
}
}
ReactDOM.render(<App/>, document.getElementById('root'));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>

How to toggle class of a div element by clicking on button in react js?

I want to toggleclass name of one element by clicking on another element. Both elements are in separate component files. I don't know how to get the state of an element and pass it to another element. Please help me solving the problem.
file1.js
<Button onClick={this.toggleFunction}>Button</Button>
file2.js
<div class="wrapper"></div>
I want to toggle class active on wrapper div when the button is clicked.
Thanks
class MyComponent extends Component {
constructor(props) {
super(props);
this.addActiveClass= this.addActiveClass.bind(this);
this.state = {
active: false,
};
}
toggleClass() {
const currentState = this.state.active;
this.setState({ active: !currentState });
};
render() {
return (
<div
className={this.state.active ? 'your_className': null}
onClick={this.toggleClass}
>
<p>{this.props.text}</p>
</div>
)
}
}
Parent Component
import React from "react";
import ButtonComponent from "./buttonComponent";
import "./demo.css";
//Parent Component
class Demo extends React.Component {
constructor(props) {
super(props);
this.state = {
active: false
};
}
updateValue = value => {
this.setState({
active: value
});
};
render() {
return (
<div>
<ButtonComponent updateParent={this.updateValue} />
<div
className={
this.state.active ? "dropdownbutton1" : "dropdownbutton1Active"
}
>
<label>First</label>
<br />
<select>
<option value="yes">yes</option>
<option value="no">no</option>
</select>
</div>
</div>
);
}
}
export default Demo;
Child Component
import React from "react";
import ToggleButton from "react-toggle-button";
import "./demo.css";
class ButtonComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
active: false,
defaultValue: 1
};
}
togglebutton = () => {
this.props.updateParent(this.state.active);
this.setState({ active: !this.state.active });
if (this.state.active) {
this.setState({ defaultValue: 1 });
} else {
this.setState({ defaultValue: -1 });
}
};
render() {
return (
<div>
<div className="ToggleButton">
<ToggleButton onClick={this.togglebutton} value={this.state.active} />
</div>
</div>
);
}
}
export default ButtonComponent;
Link :https://codesandbox.io/s/m4py2y97zp

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