Trying to render a Pop up outside of render: React Js - javascript

I am trying to bring popup based on clicking of the respective links. Here I could see onClick is being triggered along with the state change But no modal is appearing.
Can some one tell me what is it that I am doing wrong. I am using semantic-ui-react modal for the same puropse
Sandbox: https://codesandbox.io/s/semantic-ui-example-seg91?file=/Modal.js
import React from "react";
import Modal from "./Modal";
class LoaderExampleText extends React.Component {
constructor(props) {
super(props);
this.state = {
isModalOpen: false
};
}
setModal = (e, value) => {
console.log(value);
e.stopPropagation();
this.setState({ isModalOpen: true });
return (
<Modal
modalOpen={this.state.isModalOpen}
handleClose={() => {
this.setState({ isModalOpen: false });
}}
items={value}
/>
);
};
render() {
return (
<>
<a onClick={e => this.setModal(e, "first item")}>Modal A</a>
<a onClick={e => this.setModal(e, "second Item")}>Modal B</a>
</>
);
}
}
export default LoaderExampleText;
import * as React from "react";
import { Modal } from "semantic-ui-react";
class NestedTableViewer extends React.Component {
render() {
return (
<>
<Modal closeIcon={true} open={this.props.modalOpen}>
<Modal.Header>Modal</Modal.Header>
<Modal.Content>
<h1> {this.props.items}</h1>
</Modal.Content>
</Modal>
</>
);
}
}
export default NestedTableViewer;

Save the value into state and move the modal into the render return. All renderable JSX needs to be returned as a single node tree.
import React from "react";
import Modal from "./Modal";
class LoaderExampleText extends React.Component {
constructor(props) {
super(props);
this.state = {
isModalOpen: false,
value: null
};
}
setModal = (e, value) => {
console.log(value);
e.stopPropagation();
this.setState({ isModalOpen: true, value });
};
render() {
return (
<>
<a onClick={e => this.setModal(e, "first item")}>Modal A</a>
<a onClick={e => this.setModal(e, "second Item")}>Modal B</a>
<Modal
modalOpen={this.state.isModalOpen}
handleClose={() => {
this.setState({ isModalOpen: false, value: null });
}}
items={this.state.value}
/>
</>
);
}
}
export default LoaderExampleText;

In setModal is a function , so you cannot do some think like that, but if you want return you must add in render {setModal}.
So in this case :
import React from "react";
import Modal from "./Modal";
class LoaderExampleText extends React.Component {
constructor(props) {
super(props);
this.state = {
isModalOpen: false,
value: ""
};
}
setModal = (e, value) => {
console.log(value);
e.stopPropagation();
this.setState({ isModalOpen: true });
this.setState({ value: value });
};
render() {
return (
<>
<a onClick={e => this.setModal(e, "first item")}>Modal A</a>
<a onClick={e => this.setModal(e, "second Item")}>Modal B</a>
<Modal
modalOpen={this.state.isModalOpen}
handleClose={() => {
this.setState({ isModalOpen: false });
}}
items={this.state.value}
/>
</>
);
}
}
export default LoaderExampleText;
it will work;

Related

ReactJS: TypeError: render is not a function updateContextConsumer

I'm trying to learn the Context API, and what I want to achieve, is showing the button Login and Sign up on the navigation but I had another error that had from another post I read the docs but me reading and not doing visually that how I learned by doing it making mistaking.
The buttons should open up two modal windows one login and sign up form.
Modal.js
import React from 'react';
import ReactDOM from "react-dom";
import Modal from "react-modal";
import ModalContext from '../Forms/ModalContext';
class ModalProvider extends React.Component {
state = {
loginOpened: false,
signupOpened: false
};
openModal = modalType => () => {
if (modalType === "login") {
this.setState({
loginOpened: true,
signupOpened: false
});
} else if (modalType === "signup") {
this.setState({
loginOpened: false,
signupOpened: true
});
}
};
closeModal = modalType => () => {
if (modalType === "login") {
this.setState({
loginOpened: false
});
} else if (modalType === "signup") {
this.setState({
signupOpened: false
});
}
};
render(props) {
return (
<ModalContext.Provider value={{openModal: this.openModal, closeModal: this.closeModal}}>
<Modal isOpen={loginOpened} onRequestClose={this.closeModal("login")}>
<h1>Login</h1>
<button onClick={this.openModal("signup")}>Open Signup</button>
<button onClick={this.closeModal("login")}>Close this modal</button>
</Modal>
<Modal isOpen={signupOpened} onRequestClose={this.closeModal("signup")}>
<h1>Sign Up</h1>
<button onClick={this.openModal("login")}>Open Login</button>
<button onClick={this.closeModal("signup")}>Close this modal</button>
</Modal>
{props.children}
</ModalContext.Provider>
)
}
}
export default ModalProvider
ModalContext.js
I don't know why the person that helped me and explain did a very great job explain to but just want to know why it is just this line of code.
import {createContext} from 'react'
export default createContext()
Navigation.js
import React from 'react';
import { BrowserRouter as Router, Link } from 'react-router-dom';
import Dropdown from "../dropdowns/dropdowns";
import hamburger from "../images/menu.svg"
// This will display the login and sign up buttons
import ModalContext from '../Forms/ModalContext';
class Navigation extends React.Component {
constructor(props) {
super(props);
this.state = {
isExpanded: false
};
}
handleToggle(e) {
e.preventDefault();
this.setState(prevState => ({
isExpanded: !prevState.isExpanded, // negate the previous expanded state
}));
}
render(props) {
const { isExpanded } = this.state;
return (
<Router>
<div className="NavbarContainer main">
<div className="mobilecontainer LeftNav">
<h2 className="BrandName LeftNav mobileboxmenu inline FarRight">Kommonplaces</h2>
<div className="hamburger inlinev" >
<img
onClick={e => this.handleToggle(e)}
alt="menubtn"
src={hamburger}
/>
</div>
</div>
<div className={`NavBar collapsed ${isExpanded ? "is-expanded" : ""}`}>
<div className="col-a">
<Dropdown/>
<li className="RightNav"><Link to="/">Host Your Space</Link></li>
<li className="RightNav"><Link to="/">About Us</Link></li>
<li className="RightNav"><Link to="/">Contact Us</Link></li>
</div>
<div className="col-c">
{ /* 4. call the prop functions in `Navigation` component */ }
<ModalContext.Consumer>
{({openModal, closeModal}) => <button onClick={openModal("login")}>Login</button>}
{({openModal, closeModal}) => <button onClick={openModal('signup')}>Sign Up</button>}
</ModalContext.Consumer>
</div>
</div>
</div>
</Router>
);
}
}
export default Navigation;
So you first created the ModalContext and the context gives you a Provider and a Consumer.
If you want to use the context for a Consumer, there should be a Provider providing it. For that to happen, the Consumer should be a child of the Provider so that it has access to it.
<Provider>
....
...
<Consumer />
...
</Provider>
But in your case, Provider is not a ancestor of the Consumer.
Typically this is how this gets played out.
Navigation.js
import React from "react";
import { BrowserRouter as Router, Link } from "react-router-dom";
import Dropdown from "../dropdowns/dropdowns";
import hamburger from "../images/menu.svg";
// This will display the login and sign up buttons
import ModalContext from "../Forms/ModalContext";
import ModalProvider from "../Forms/ModalProvider";
class Navigation extends React.Component {
constructor(props) {
super(props);
this.state = {
isExpanded: false
};
}
handleToggle(e) {
e.preventDefault();
this.setState(prevState => ({
isExpanded: !prevState.isExpanded // negate the previous expanded state
}));
}
render(props) {
const { isExpanded } = this.state;
return (
<Router>
{/* Modal provider provides the context to all the children */}
<ModalProvider>
<div className="NavbarContainer main">
<div className="mobilecontainer LeftNav">
<h2 className="BrandName LeftNav mobileboxmenu inline FarRight">
Kommonplaces
</h2>
<div className="hamburger inlinev">
<img
onClick={e => this.handleToggle(e)}
alt="menubtn"
src={hamburger}
/>
</div>
</div>
<div
className={`NavBar collapsed ${isExpanded ? "is-expanded" : ""}`}
>
<div className="col-a">
<Dropdown />
<li className="RightNav">
<Link to="/">Host Your Space</Link>
</li>
<li className="RightNav">
<Link to="/">About Us</Link>
</li>
<li className="RightNav">
<Link to="/">Contact Us</Link>
</li>
</div>
<div className="col-c">
{/* 4. call the prop functions in `Navigation` component */}
{/* Consumer has access to context as children as function*/}
<ModalContext.Consumer>
{({openModal, closeModal, loginOpened, signupOpened}) => {
return (
<React.Fragment>
<button onClick={openModal("login")}> Login</button>
<button onClick={openModal("signup")}>Sign Up</button>
<Modal
isOpen={loginOpened}
onRequestClose={closeModal("login")}
>
<h1>Login</h1>
<button onClick={openModal("signup")}>
Open Signup
</button>
<button onClick={closeModal("login")}>
Close this modal
</button>
</Modal>
<Modal
isOpen={signupOpened}
onRequestClose={closeModal("signup")}
>
<h1>Sign Up</h1>
<button onClick={openModal("login")}>
Open Login
</button>
<button onClick={closeModal("signup")}>
Close this modal
</button>
</Modal>
</React.Fragment>
);
}}
</ModalContext.Consumer>
</div>
</div>
</div>
</ModalProvider>
</Router>
);
}
}
export default Navigation;
ModalProvider.js
import React from "react";
import ReactDOM from "react-dom";
import Modal from "react-modal";
import ModalContext from "../Forms/ModalContext";
class ModalProvider extends React.Component {
state = {
loginOpened: false,
signupOpened: false
};
openModal = modalType => () => {
if (modalType === "login") {
this.setState({
loginOpened: true,
signupOpened: false
});
} else if (modalType === "signup") {
this.setState({
loginOpened: false,
signupOpened: true
});
}
};
closeModal = modalType => () => {
if (modalType === "login") {
this.setState({
loginOpened: false
});
} else if (modalType === "signup") {
this.setState({
signupOpened: false
});
}
};
render(props) {
return (
<ModalContext.Provider
value={{
openModal: this.openModal,
closeModal: this.closeModal,
signupOpened: this.state.signupOpened,
loginOpened: this.state.loginOpened,
}}
>
{props.children}
</ModalContext.Provider>
);
}
}
export default ModalProvider;
I was writing an example app as well so will post it as an answer:
function ToggleModal({ checked, onChange, modalId }) {
console.log('rendering:', modalId);
return (
<label>
{modalId}
<input
type="checkbox"
checked={checked}
onChange={onChange}
/>
</label>
);
}
const ToggleModalContainer = ({ modalId }) => {
const { modals, changeModal } = React.useContext(State);
const checked = modals[modalId];
return React.useMemo(
() =>
ToggleModal({
checked,
modalId,
onChange: () => changeModal(modalId, !checked),
}),
[changeModal, checked, modalId]
);
};
function Modals() {
const state = React.useContext(State);
return Object.entries(state.modals).map(
([key, value]) =>
value && <div key={key}>this is modal {key}</div>
);
}
const State = React.createContext();
const App = () => {
const [modals, setModals] = React.useState({
a: false,
b: false,
c: false,
});
const changeModal = React.useCallback(
(modalId, open) =>
setModals(modals => ({ ...modals, [modalId]: open })),
[]
);
const state = React.useMemo(
() => ({
modals,
changeModal,
}),
[changeModal, modals]
);
return (
<State.Provider value={state}>
<React.Fragment>
{Object.keys(modals).map(modalId => (
<ToggleModalContainer
modalId={modalId}
key={modalId}
/>
))}
<Modals />
</React.Fragment>
</State.Provider>
);
};
//render app
ReactDOM.render(
<App />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

How to hide some component based on some flag in react js

I want to hide some component based on some flag in react js.
I have an App component where I have Login and other components, I want to hide the other component until Login components this.state.success is false and on click of a button I am changing the sate, but it's not working, I am new to react,
My App Class compoenent -
import React, { Component } from "react";
import logo from "../../logo.svg";
// import Game from "../Game/Game";
import Table from "../Table/Table";
import Form from "../Table/Form";
import Clock from "../Clock/Clock";
import "./App.css";
import Login from "../Login/Login";
class App extends Component {
state = {
success: false
};
removeCharacter = index => {
const { characters } = this.state;
this.setState({
characters: characters.filter((character, i) => {
return i !== index;
})
});
};
handleSubmit = character => {
this.setState({ characters: [...this.state.characters, character] });
};
handleSuccess() {
this.setState({ success: true });
}
render() {
const { characters, success } = this.state;
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<span className="Span-inline">App</span>
<Clock time={new Date()} />
</header>
<Login success={success} handleSuccess={this.handleSuccess} />
{success && (
<div className="container">
<h1>React Tutorial</h1>
<p>Add a character with a name and a job to the table.</p>
<Table
characterData={characters}
removeCharacter={this.removeCharacter}
/>
<h3>Add New character</h3>
<Form handleSubmit={this.handleSubmit} />
</div>
)}
{/* <Game /> */}
</div>
);
}
}
export default App;
My Login component -
import React, { Component } from "react";
import Greeting from "./Greeting";
import LogoutButton from "./LogoutButton";
import LoginButton from "./LoginButton";
class Login extends Component {
constructor(props) {
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.state = {
isLoggedIn: false,
name: "",
success: false
};
}
handleLoginClick() {
this.setState({ isLoggedIn: true });
this.setState({ success: true });
}
handleLogoutClick() {
this.setState({ isLoggedIn: false });
this.setState({ success: false });
}
onChange = e => {
this.setState({
name: e.target.value
});
};
render() {
const isLoggedIn = this.state.isLoggedIn;
const name = this.state.name;
// const successLogin = this.state.success;
let button;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
return (
<div>
<Greeting
isLoggedIn={isLoggedIn}
name={name}
onChange={this.onChange}
/>
{button}
</div>
);
}
}
export default Login;
please guide me on what I am doing wrong.
Why sometime debuggers do not trigger in react component?
For the sake of example I have used functional stateless component here. You can use Class component all upto you.
const conditionalComponent = (props) => {
let condition = true;
return (
{condition && <div><h1>Hello world</h1></div>}
}
Instead of directly giving condition you can even call function which returns a boolean value.
handleLoginClick() {
this.setState({ isLoggedIn: true });
this.setState({ success: true });
this.props.handleSuccess()
}
do like this
<Login success={success} handleSuccess=
{this.handleSuccess} />
bind this function

How do I make my modal close in x seconds?

I am using ReactJS and a Bootstrap modal. I can open the modal just fine, but I would like it to close after 3 seconds.
I tried setTimeout as you can see below, but it doesn't close. I gave setTimeout a callback of handleClose, but after console logging, I can see that handleClose is not being called.
Here is the ItemDetailView Component:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Card, CardImg, CardText, CardBody,
CardTitle, CardSubtitle } from 'reactstrap';
import { addToCart } from '../actions/addToCartAction';
import './ItemDetailView.css';
import ItemAddedModal from './ItemAddedModal';
class ItemDetailView extends Component {
constructor(props) {
super(props);
this.state = {
modalOpen: false
}
// this.toggle = this.toggle.bind(this);
};
// toggle() {
// this.setState({
// modalOpen: !this.state.modalOpen
// });
// };
handleOpen = () => {
console.log("Cart Open", this.state.modalOpen);
this.setState({
modalOpen: true
},() => {setTimeout(this.handleClose(), 3000)});
// setTimeout(this.handleClose(), 3000);
};
handleClose = () => {
this.setState({
modalOpen: false
});
console.log('handleClose fired!')
};
addToCartHandler = () => {
this.props.addToCart(this.props.card);
console.log('addToCart++', this.props.quantity);
this.handleOpen()
// this.setState({
// modalOpen: true
// });
};
render() {
if (!this.props.title) {
return null;
}
return (
<div className="detail-view-wrapper">
<Card className="text-center detail-view-card">
{/* <CardImg top width="100%" src={"/" + this.props.img} alt={this.props.title} /> */}
<CardImg className="detail-view-img" top width="100%" src={"/" + this.props.img} alt={this.props.title} />
<CardBody>
<CardTitle className={"card-title"}>{this.props.title}</CardTitle>
<CardSubtitle>${this.props.price}</CardSubtitle>
<CardText>{this.props.description}</CardText>
{/* <SvgIcon className="cart-icon" onClick={() => this.addToCartHandler()} >
<AddShoppingCart />
</SvgIcon> */}
<button className= "add-to-cart-button" onClick={() => this.addToCartHandler()}>Add To Cart</button>
</CardBody>
</Card>
<ItemAddedModal open={this.state.modalOpen} toggle={this.toggle} />
</div>
);
}
}
const mapStateToProps = state => {
if (!state.data.cardData) {
return {
title: null,
img: null,
description: null,
price: null
}
}
const card = state.data.cardData[state.card.id]
return {
card: card,
title: card.title,
id: card.id,
img: card.img,
description: card.description,
price: card.price,
quantity: 0
};
}
export default connect(mapStateToProps, { addToCart })(ItemDetailView);
Here is the ItemAddedModal:
import React from 'react';
import { Modal, ModalHeader } from 'reactstrap';
import './ItemAddedModal.css';
class ItemAddedModal extends React.Component {
render () {
return (
<div>
<Modal className="item-added-modal" isOpen={this.props.open} toggle={this.props.toggle} className={this.props.className}>
<ModalHeader className="item-added-modal-header">
<p className="item-added-modal-p">Item Added To Cart</p>
</ModalHeader>
</Modal>
</div>
)
};
}
export default ItemAddedModal;
To perform an action after a state is set, we need to pass a callback to setState.
this.setState({
modalOpen: true
},()=>{
console.log(this.state.modalOpen);});

Why won't what the user clicked appear on my modal?

My code was a bit messy before in terms of having a method for each kind of food and using each of those methods to display which type of food the user selected upon clicking which ever </FoodButton> the user desires.
Now, I want to optimize it in a way where it's only one method that handles it all.
There error I'm getting says:
'item' is not defined
(referring to <FoodButton clicked={() => this.selectItem(item)} label={"Chicken Taco"}/>)
I've been trying so many different ways of fixing this but can't seem to find a solution.
How come what I'm doing isn't working?
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Modal from 'react-modal';
import Aux from '../../../../hoc/Aux';
import FoodButton from '../FoodButtons/FoodButton';
import CheckoutButton from '../CheckoutButton/CheckoutButton';
import axios from '../../../../axios-foodChosen';
import { CLOSE_MODAL, OPEN_MODAL } from "../../../../store/action/NoNameAction";
class TacoTypes extends Component {
state = {
selectedItem: ''
};
constructor(props) {
super(props);
this.items = {
chickenTaco: 'Chicken Taco',
beefTaco: 'Beef Taco',
chickenBurrito: 'Chicken Burrito'
};
}
componentWillMount() {
// for modal
Modal.setAppElement('body');
}
selectItem(item) {
this.setState({selectedItem: item})
}
render() {
return (
<Aux>
<FoodButton clicked={() => this.selectItem(item)} label={"Chicken Taco"}/>
<FoodButton clicked={() => this.beefTaco()} label={"Beef Taco"}/>
<FoodButton clicked={() => this.chickenBurrito()} label={"Chicken Burrito"}/>
<CheckoutButton clicked={() => this.props.openModalRedux()}/>
<Modal isOpen={this.props.isOpen}>
<p>
{this.items.map(item => (
<p key={item}>{item}</p>
))}
</p>
<button onClick={() => this.props.closeModalRedux()}>Close</button>
</Modal>
</Aux>
);
}
}
const mapStateToProps = state => {
return {
// props for modal
isOpen: state.global.isModalOpen,
}
};
const mapDispatchToProps = dispatch => {
return {
// Modal handlers
openModalRedux: () => dispatch({type: OPEN_MODAL}),
closeModalRedux: () => dispatch({type: CLOSE_MODAL})
}
};
export default connect(mapStateToProps, mapDispatchToProps)(TacoTypes);
Removing the Redux fluff for the time being, this should work:
import React, { Component } from "react";
import Modal from "react-modal";
import FoodButton from "../FoodButtons/FoodButton";
import CheckoutButton from "../CheckoutButton/CheckoutButton";
class TacoTypes extends Component {
constructor(props) {
super(props);
this.state = {
selectedItem: undefined,
modalIsOpen: false,
};
this.items = {
chickenTaco: "Chicken Taco",
beefTaco: "Beef Taco",
chickenBurrito: "Chicken Burrito",
};
}
componentWillMount() {
Modal.setAppElement("body");
}
selectItem(item) {
this.setState({ selectedItem: item });
}
render() {
return (
<Aux>
{Object.entries(this.items).map(([item, label]) => (
<FoodButton
clicked={() => this.selectItem.call(this, item)}
label={label}
key={item}
/>
))}
<CheckoutButton clicked={() => this.setState({ modalIsOpen: true })} />
<Modal isOpen={this.state.modalIsOpen}>
<p>
{Object.entries(this.items).map(([item, label]) => (
<p key={item}>
{item}
{this.state.selectedItem === item
? " (You chose this one!)"
: null}
</p>
))}
</p>
<button onClick={() => this.setState({ modalIsOpen: false })}>
Close
</button>
</Modal>
</Aux>
);
}
}
(I'm using this.selectItem.call(this, ...)) because I'm not sure if your environment is set up for functions as class properties. If it is, you can do
selectItem = (item) => {
this.setState({ selectedItem: item });
}
// ...
clicked={() => this.selectItem(item)}
instead.
)
If you want to pass item you need to specify it as a function parameter:
<FoodButton clicked={item => this.selectItem(item)} label={"Chicken Taco"}/>
As the error message indicates, the variable item is not defined. This should work:
<FoodButton clicked={() => this.selectItem('Chicken Taco')} label={"Chicken Taco"}/>
Or
<FoodButton clicked={() => this.selectItem(this.items.chickenTaco)} label={"Chicken Taco"}/>
UPDATE:
I want to display in the modal only the buttons that user clicked on
There are multiple ways to do that depending on how you arrange your data. I'll show one option here as a proof of concept:
There are three buttons. Click on either one of them will add corresponding item to the selected list (or remove from the list if it is already selected). You might do something similar in your specific setup.
class App extends React.Component {
constructor (props) {
super(props)
this.state = {
items: {
chicken: false,
beef: false,
other: false
}
}
}
onSelect (name) {
this.setState((prevState) => {
return {
items: {
...prevState.items,
[name]: !prevState.items[name]
}
}
})
}
getSelected () {
return Object.entries(this.state.items).filter(entry => entry[1]).map(entry => entry[0]).join(',')
}
render() {
return (
<div>
{Object.keys(this.state.items).map(name => (
<button onClick={() => this.onSelect(name)} key={name}>{name}</button>
))}
<div>
Selected: {this.getSelected()}
</div>
</div>
)
}
}
ReactDOM.render(
<App/>,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

I'm having problems creating a functioning "delete button" in my react todo app

I'm brand new to Reactjs and I'm working on my first app, a todo app of course. However, everything was going smoothly until I was asked to create a delete button to remove my todos. I'm stuck and have been puzzled over this for almost 3 days. Any help or advice would be appreciated.
react-to-do/src/App.js
import React, { Component } from "react";
import "./App.css";
import ToDo from "./components/ToDo.js";
class App extends Component {
constructor(props) {
super(props);
this.state = {
todos: [
{ description: "Walk the cat", isCompleted: true },
{ description: "Throw the dishes away", isCompleted: false },
{ description: "Buy new dishes", isCompleted: false }
],
newTodoDescription: ""
};
this.deleteTodo = this.deleteTodo.bind(this);
}
deleteTodo(description) {
this.setState({
todos: this.state.todos.filter(
(todos, index) => todos.description !== description
)
});
}
handleChange(e) {
this.setState({ newTodoDescription: e.target.value });
}
handleSubmit(e) {
e.preventDefault();
if (!this.state.newTodoDescription) {
return;
}
const newTodo = {
description: this.state.newTodoDescription,
isCompleted: false
};
this.setState({
todos: [...this.state.todos, newTodo],
newTodoDescription: ""
});
}
toggleComplete(index) {
const todos = this.state.todos.slice();
const todo = todos[index];
todo.isCompleted = todo.isCompleted ? false : true;
this.setState({ todos: todos });
}
render() {
return (
<div className="App">
<ul>
{this.state.todos.map((todo, index) => (
<ToDo
key={index}
description={todo.description}
isCompleted={todo.isCompleted}
toggleComplete={() => this.toggleComplete(index)}
/>
))}
</ul>
<form onSubmit={e => this.handleSubmit(e)}>
<input
type="text"
value={this.state.newTodoDescription}
onChange={e => this.handleChange(e)}
/>
<input type="submit" />
</form>
</div>
);
}
}
export default App;
react-to-do/src/ToDo.js
import React, { Component } from "react";
class ToDo extends Component {
deleteToDo(description) {
this.props.deleteToDo(description);
}
render() {
return (
<div className="wrapper">
<button
className="deleteToDo"
onClick={e => this.deleteToDo(this.props.description)}
>
Delete
</button>
{this.props.deleteToDo}
<li>
<input
type="checkbox"
checked={this.props.isCompleted}
onChange={this.props.toggleComplete}
/>
<span>{this.props.description}</span>
</li>
</div>
);
}
}
export default ToDo;
react-to-do/src/index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import registerServiceWorker from "./registerServiceWorker";
ReactDOM.render(<App />, document.getElementById("root"));
registerServiceWorker();
Your code should look like
class ToDo extends React.Component {
deleteToDo(description) {
this.props.deleteToDo(description);
}
render() {
return (
<div className="wrapper">
<button className="deleteToDo" onClick = {(e) =>
this.deleteToDo(this.props.description)}>Delete</button>
{() => this.props.deleteToDo(this.props.description)}
<li>
<input type="checkbox" checked={ this.props.isCompleted }
onChange={ this.props.toggleComplete } />
<span>{ this.props.description }</span>
</li>
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
todos: [
{ description: 'Walk the cat', isCompleted: true },
{ description: 'Throw the dishes away', isCompleted: false },
{ description: 'Buy new dishes', isCompleted: false }
],
newTodoDescription: ''
};
this.deleteTodo = this.deleteTodo.bind(this);
}
deleteTodo(description) {
const filteredTodos = this.state.todos.filter((todo, index) => todo.description !== description);
this.setState({
todos: filteredTodos
});
}
handleChange(e) {
this.setState({ newTodoDescription: e.target.value })
}
handleSubmit(e) {
e.preventDefault();
if (!this.state.newTodoDescription) { return }
const newTodo = { description: this.state.newTodoDescription,
isCompleted: false};
this.setState({ todos: [...this.state.todos, newTodo],
newTodoDescription: '' });
}
toggleComplete(index) {
const todos = this.state.todos.slice();
const todo = todos[index];
todo.isCompleted = todo.isCompleted ? false: true;
this.setState({ todos: todos });
}
render() {
return (
<div className="App">
<ul>
{ this.state.todos.map( (todo, index) =>
<ToDo key={ index } description={ todo.description }
isCompleted={ todo.isCompleted } toggleComplete={ () =>
this.toggleComplete(index) } deleteToDo={this.deleteTodo} />
)}
</ul>
<form onSubmit={ (e) => this.handleSubmit(e) }>
<input type="text" value={
this.state.newTodoDescription } onChange={ (e) =>
this.handleChange(e) } />
<input type="submit" />
</form>
</div>
);
}
}
Here you forgot to pass props from App and description parameter from ToDo component.
Try here https://jsfiddle.net/prakashk/69z2wepo/101369/#&togetherjs=B3l5GDzo8A
You never pass your "deleteTodo" function as a prop to your "toDo" component. change the portion where you create you're todo components in that map to resemble something like this...
map.... => <toDo deleteToDo={this.deleteTodo}......... />
remember components are scoped to themselves if you want to call a function that exists in the parents context in a child, a reference of that function must be passed to that child as a prop.

Categories