Calling an onclick event from a different Component - reactjs

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

Related

Change the state of parent component on click of a child component with a specific key in React JS

I am making a Todo App in React JS, i'm fairly new to React JS.
I have made two components named AddComponent and TodoItem component. The AddComponent has an input and button to add the todos and it will render TodoItem as a child component whenever a todo is added using the AddComponent.
I also have to remove the child component TodoItem when a button inside it is clicked according to it's key, so it would remove that item from the state of the parent component AddComponent.
The problem i'm facing is i can't change the state anywhere else than the render() function, because there is where i'm dynamically generating the TodoItem components using the state.
And changing the state inside the render() function would result in an infinite loop. Please help how to resolve this problem.
I'm using Bootstrap 4 using a CDN for styling.
AddComponent.js
import React from 'react'
import shortid from 'shortid'
import TodoItem from './TodoItem';
class AddComponent extends React.Component {
constructor() {
super();
this.state = {
todoText: '',
todoList: []
}
this.handleChange = this.handleChange.bind(this);
this.handleClick = this.handleClick.bind(this);
this.handleTodoClick = this.handleTodoClick.bind(this);
}
handleChange(e) {
e.persist();
this.setState((prevState) => {
return {
todoText: e.target.value,
todoList: prevState.todoList
}
})
}
handleClick() {
this.setState((prevState) => {
return {
todoText: prevState.todoText,
todoList: [{
text: prevState.todoText,
id: shortid.generate()
}, ...prevState.todoList]
}
})
}
handleTodoClick(id) {
const newState = this.state.todoList.filter(todoL => todoL.id === id);
this.setState({ todoList: newState });
}
render() {
const todos = this.state.todoList.map((todo) => {
return (
<TodoItem key={todo.id} value={todo.text} click={this.handleTodoClick(todo.id)}>{todo.text}</TodoItem>
)
})
return (
<div>
<div className="row">
<div className="col-sm-9">
<div className="form-group">
<input
type="text"
className="form-control"
placeholder="Add a Todo"
onChange={this.handleChange}
/>
</div>
</div>
<div className="col-sm-3">
<button
type="button"
className="btn btn-primary btn-block"
onClick={this.handleClick}
>Add
</button>
</div>
</div>
<div>
{todos}
</div>
</div>
)
}
}
export default AddComponent
TodoItem.js
import React from 'react'
class TodoItem extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="alert alert-secondary alert-dismissible fade show">
<button
type="button"
className="close"
onClick={this.props.click}
data-dismiss="alert">×</button>
{this.props.value}
</div>
)
}
}
export default TodoItem
Try : https://codesandbox.io/s/hopeful-moon-duqfe
There were two problems; 1st one is filter and the 2nd one is handler callback function that is set to click prop.
1st problem : Filter function should filter that does not match your id so you should use NOT equal :
handleTodoClick(id) {
const newState = this.state.todoList.filter(todoL => todoL.id !== id);
this.setState({ todoList: newState });
}
2nd problem : When you set click prop as a handler callback function, you should set reference of the function itself without calling it. Not like this : click={this.handleTodoClick(todo.id). This will call the function and set the click prop to result of it, which is undefined since it does not return to anything.
So i have changed your click props as click={this.handleTodoClick}
const todos = this.state.todoList.map(todo => {
return (
<TodoItem key={todo.id} id={todo.id} value={todo.text} click={this.handleTodoClick}>
{todo.text}
</TodoItem>
);
});
On child component, onClick is similary set to function itself onClick={() => this.props.click(this.props.id)} NOT like onClick={this.props.click(this.props.id)}.
import React from "react";
class TodoItem extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="alert alert-secondary alert-dismissible fade show">
<button
type="button"
className="close"
onClick={() => this.props.click(this.props.id)}
data-dismiss="alert"
>
×
</button>
{this.props.value}
</div>
);
}
}
Fix:
<TodoItem key={todo.id} todoId={todo.id} value={todo.text} click={this.handleTodoClick(todo.id)}>{todo.text}</TodoItem>
We can't use key as id. The key property is used by React under the hood, and is not exposed to us.
import React from 'react'
class TodoItem extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="alert alert-secondary alert-dismissible fade show">
<button
type="button"
className="close"
// look here
onClick={e => {this.props.click(this.props.todoId)}}
data-dismiss="alert">×</button>
{this.props.value}
</div>
)
}
}
export default TodoItem
Try the following by updating the handle for click in AddComponent then creating a handler within TodoItem that calls props click with the respective todo item id value. I'd recommend to just pass the entire todo so that you can access both the unique id and value in TodoItem:
AddComponent:
const todos = this.state.todoList.map((todo) => {
return (
<TodoItem key={todo.id} todo={todo} click={this.handleTodoClick}>{todo.text}</TodoItem>
)
})
TodoItem:
import React from 'react'
class TodoItem extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.props.click(this.props.todo.id);
}
render() {
return (
<div className="alert alert-secondary alert-dismissible fade show">
<button
type="button"
className="close"
onClick={this.handleClick}
data-dismiss="alert">×</button>
{this.props.todo.value}
</div>
)
}
}
export default TodoItem;

duplicates being created in todo list app

I am creating a todo list where when the user clicks the checkbox "complete" that is next to the todo item, it appears in the complete component however there is a duplicate of that item that is being added as well and i am also having an issue trying to have the checkbox not appear in the completed component...
When a user creates a new todo it appears in the active component first and it has a checkbox next to it called completed and when the user clicks the checkbox it appears in the completed component
import React from 'react';
import Active from './Components/Active';
import Completed from './Components/Completed';
import Todoform from './Components/Todoform';
import './App.css';
class App extends React.Component {
state = {
items: [],
task: '',
id: 0,
completedItems: []
}
handleInput = (event) => {
this.setState({
task: event.target.value
})
}
handleSubmit = (event) => {
event.preventDefault()
const newTask = {
id: this.state.id,
title: this.state.task
}
const updatedItems = [...this.state.items, newTask]
this.setState({
items: updatedItems,
task: '',
id: this.state.id + 1
})
}
handleComplete = (newTask) => {
this.setState({completedItems: [...this.state.items, newTask]})
//console.log(this.state.items)
}
render() {
return (
<div id="main-content">
<h1>Task Lister</h1>
<Todoform
handleChange={this.handleInput}
handleSubmit={this.handleSubmit}
task={this.state.task}
/>
<Active
items={this.state.items}
handleComplete={this.handleComplete}
/>
<Completed
completedItems={this.state.completedItems}
/>
</div>
)
}
}
export default App;
import React from 'react'
class Todo extends React.Component{
state = {
checked: false
}
handleCheck = () => {
this.setState({
checked: !this.state.checked
})
}
handleClick = () => {
this.props.handlecompletedList(this.props.title)
}
render(){
const { title } = this.props
return (
<div className="ui checked checkbox">
<input type="checkbox" checked={this.state.checked} onChange={this.handleCheck}
onClick={this.handleClick}/>
<label>Completed {title}</label>
</div>
)
}
}
export default Todo;
import React from 'react'
import Todo from './Todo'
const Active = (props) => {
const { items, handleComplete } = props
return(
<div id="activeList">
<h2 className="position">Active</h2>
<ul id="tasks">
{
items.map(item => {
return(
<Todo key={item.id} handlecompletedList={handleComplete} title={item.title}/>
)
})
}
</ul>
</div>
)
}
export default Active;
import React from 'react'
import Todo from './Todo'
const Completed = (props) => {
const { completedItems } = props
return(
<div id="completedList">
<h2 className="position">Completed</h2>
<ul id="tasks">
{
completedItems.map(item => {
return(
<Todo key={item.id} title={item.title}/>
)
})
}
</ul>
</div>
)
}
export default Completed
import React from 'react';
class Todoform extends React.Component {
render(){
const {task, handleChange, handleSubmit} = this.props;
return(
<form onSubmit={handleSubmit}>
<label>Task description:</label>
<input type="text" name="name" placeholder="description" value={task} onChange={handleChange}/>
<button>Create New Task</button>
</form>
)
}
}
export default Todoform;
To hide the checkbox next to completed items you need to use Conditional Rendering. An example would be to add a prop IsCompleted to your component and use it when rendering html like this:
{this.props.isCompleted &&
<input
type="checkbox"
checked={this.state.checked}
onChange={this.handleCheck}
onClick={this.handleClick}/>
}
The duplicate item issue is probably because you use this.state.items in your handleComplete method instead of using this.state.completedItems if this is not the issue, would you mind sharing the code for the Todoform component as well?
EDIT: The item duplicates because when the handleComplete is called it copies this.state.items to the list and adds the one that you clicked on.
You should use this.state.completedItems in the handleComplete, also you are currently only sending and appending the title in the handleComplete method, you should be appending an object that has a title. The solution would be to update your handleClick method to this and update handleComplete to use this.state.completedItems:
handleClick = () => {
this.props.handlecompletedList({
title: this.props.title
});
};

React get codemirror value onClick

I am trying to get the following code below to display the CodeMirror onChange value when the button is clicked and then display that value inside the "output" div.
I am fairly new to react so not sure if it's best to pass the value through state or if there's an easier method.
Here is my code so far:
import React, { Component } from "react";
import { UnControlled as CodeMirror } from "react-codemirror2";
import "codemirror/mode/javascript/javascript";
import "codemirror/lib/codemirror.css";
export default class Code extends Component {
render() {
return (
<div>
<CodeMirror
value='console.log("Hello World")'
options={{
mode: "javascript",
lineNumbers: true
}}
onChange={(editor, data, value) => {
console.log(value);
}}
/>
<button onClick={}>run code</button>
<div className="Output">
<p>// Value should go here</p>
</div>
</div>
);
}
}
You can make use of state to maintain your values, and show output from your state,
class Code extends Component {
constructor(props) {
super(props)
this.state = {
runCode: false,
outputText: 'console.log("Hello World")',
}
}
runCode = () => {
this.setState({runCode: true})
}
render() {
return (
<div>
<CodeMirror
value={this.state.outputText}
options={{
mode: 'javascript',
lineNumbers: true,
}}
onChange={(editor, data, value) => {
this.setState({
runCode: false,
outputText: value,
})
}}
/>
<button onClick={this.runCode}>run code</button>
<div className="Output">
<pre>{this.state.runCode && this.state.outputText}</pre>
</div>
</div>
)
}
}
Demo - Output appended on click of button.
Demo1 - Outout appended as you type in.
You need to add a state in such situations:
export default class Code extends Component {
state={
value: ''
}
render() {
return (
<div>
<CodeMirror
value='console.log("Hello World")'
options={{
mode: "javascript",
lineNumbers: true
}}
onChange={(editor, data, value) => {
this.setState({value});
}}
/>
<button onClick={}>run code</button>
<div className="Output">
<p>{this.state.value}</p>
</div>
</div>
);
}
}

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

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