REACT - How can I edit the user input? - javascript

Making a CV creator and I've added a popup form for users to input data and then render to the page. I'm having a lot of trouble figuring out how to reopen the popup with already submitted data for editing however, been hours of googling and no closer to having any idea what to do
the main work code
import React, { Component } from "react";
import Popup from 'reactjs-popup';
import CreateItems from './CreateItems'
class Work extends Component {
constructor(props) {
super(props);
this.state = {
items: []
};
this.addItem = this.addItem.bind(this);
this.deleteItem = this.deleteItem.bind(this);
}
addItem(e) {
var newItem = {
title: this.title.value,
company: this.company.value,
location: this.location.value,
date: this.date.value,
description: this.description.value,
key: Date.now()
};
this.setState((prevState) => {
return {
items: prevState.items.concat(newItem)
};
});
this.title.value = "";
this.company.value = "";
this.location.value = "";
this.date.value = "";
this.description.value = "";
e.preventDefault();
}
deleteItem(key) {
var filteredItems = this.state.items.filter(function (item) {
return (item.key !== key);
});
this.setState({
items: filteredItems
});
}
render(){
return (
<div id = "work-container">
<h2>Experience:</h2>
<CreateItems entries = {this.state.items}
delete= {this.deleteItem}/>
<Popup trigger={<button> Add New</button>} modal nested>
{close => (
<form id = 'modal-container' onSubmit={this.addItem}>
<span id = 'modal-header'>Add work experience</span>
<div id = 'row'>
<div className = 'row-segment-left'>
<label htmlFor = "job-title">Job Title</label>
<input id = "job-title" type = "text" ref={(a) => this.title = a}></input>
</div>
<div className = "row-segment-right">
<label htmlFor ='job-company'>Company</label>
<input id = 'job-company' type= 'text' ref={(a) => this.company = a}></input>
</div>
</div>
<div id = 'row'>
<div className = 'row-segment-left'>
<label htmlFor = "job-location">Job Location</label>
<input id = "job-location" type = "text" ref={(a) => this.location = a}></input>
</div>
<div className = "row-segment-right">
<label htmlFor ='job-date'>Date of Employment</label>
<input id = 'job-date' type= 'text' ref={(a) => this.date = a}></input>
</div>
</div>
<div id ='bottom-row'>
<label htmlFor ='job-description'>Job Description</label>
<textarea id = 'job-description' rows = '5' placeholder = 'Describe your duties...'
ref={(a) => this.description = a}></textarea>
</div>
<div id = 'button-container'>
<button type = "submit" >Submit</button>
<button onClick={() => {
close();
}}>Cancel</button>
</div>
</form>
)}
</Popup>
</div>
)}};
export default Work;
The code for creating and adding the content
import React, { Component } from "react";
class CreateItems extends Component {
constructor(props) {
super(props);
this.createTasks = this.createTasks.bind(this);
}
delete(key) {
this.props.delete(key);
}
createTasks(item) {
return <div className = 'work-segment' key={item.key}>
<h3>{item.title}</h3>
<span>{item.company} / {item.location} / {item.date}</span>
<p>{item.description}</p>
<span>
<button>Edit</button>
<button onClick={() => this.delete(item.key)}>Delete</button>
</span>
</div>
}
render() {
var workEntries = this.props.entries;
var workItems = workEntries.map(this.createTasks);
return (
<div id = 'work-items'>
{workItems}
</div>
);
}
};
export default CreateItems;

Related

SetState overwrites previous state even with spread operator React.JS

I'm facing an issue with setState inside my handleChange.
So here in code after clicking addList a div with input and button will appear, then when i type inside input the input state will update and after clicking Add button the div will disappear.
so my problem is next time when clicking addList the value of previous input is still there and the hideAddList handler does not set the input state to empty and also when I type in the new opened div the previous input state will overwrite. even with indexing and spread operator its still same problem.
what should I do?
export class myClass extends Component {
constructor(props) {
super(props);
this.state = {
input: [{ title: '' }]
};
this.addList = this.addList.bind(this);
this.hideAddList = this.hideAddList.bind(this);
this.titleHandleChange = this.titleHandleChange.bind(this);
}
addList() {
var x = document.getElementById("btn-list")
var y = document.getElementById("add-button")
x.style.display = 'none'
y.style.display = ''
}
hideAddList() {
var x = document.getElementById("btn-list")
var y = document.getElementById("add-button")
x.style.display = ''
y.style.display = 'none';
this.setState(prevState => ({ input: [...prevState.input, {title:''}]}))
}
HandleChange(e, index){
const { name, value } = e.target;
const list = [...this.state.input];
list[index][name] = value;
this.setState({ input: list});
}
render() {
return (
<div>
<button id="btn-list" className="btn btn-default btn-list" type="submit" onClick={this.addList}>Add Another List</button>
{this.state.input.map((x, i) => {
return (
<div key={i} id="add-button" className="add-button" style={{ display: "none" }}>
<input id="input" type="text" onChange={(e) => {this.HandleChange(e, i)}} value={x.title} name="title" className="form-control add-input" />
<button className="btn btn-default btn-list-add" onClick={this.hideAddList} type="submit">Add</button>
</div>
)})}
</div>
)
}
}
export default myClass
from the code you provided, you will always get the first "add-button" element each time you call addList, because all of your list items have the same id.
{this.state.input.map((x, i) => {
...
<div key={i} id="add-button" ...> // here! they got the same id!
so what happened here is that you are actually editing the first item all the time. you will need to add different id to your elements. and the AddList should get the last item index.
for example:
AddList()
...
{this.state.input.map((x, i) => {
return (
<div key={i} id={`add-button-${i}`} .... // here! add unique id for each item!
the whole code should be like this:
export default class myClass extends React.Component {
constructor(props) {
super(props);
this.state = {
input: [{title: ''}],
};
this.addList = this.addList.bind(this);
this.hideAddList = this.hideAddList.bind(this);
this.titleHandleChange = this.titleHandleChange.bind(this);
}
addList() {
var lastItemIndex = this.state.input.length - 1; // you need to get the last item's index to make sure that you display the latest item
var x = document.getElementById('btn-list');
var y = document.getElementById(`add-button-${lastItemIndex}`);
x.style.display = 'none';
y.style.display = '';
}
hideAddList(i) {
var x = document.getElementById('btn-list');
var y = document.getElementById(`add-button-${i}`);
x.style.display = '';
y.style.display = 'none';
this.setState(prevState => ({input: [...prevState.input, {title: ''}]}));
}
titleHandleChange(e, index) {
const {name, value} = e.target;
const list = [...this.state.input];
list[index][name] = value;
this.setState({input: list});
}
render() {
return (
<div>
<button
id="btn-list"
className="btn btn-default btn-list"
type="submit"
onClick={this.addList}>
Add Another List
</button>
{this.state.input.map((x, i) => {
return (
<div
key={i}
id={`add-button-${i}`}
className="add-button"
style={{display: 'none'}}>
<input
id="input"
type="text"
onChange={e => {
this.titleHandleChange(e, i);
}}
value={x.title}
name="title"
className="form-control add-input"
/>
<button
className="btn btn-default btn-list-add"
onClick={() => this.hideAddList(i)}
type="submit">
Add
</button>
</div>
);
})}
</div>
);
}
}
for debugging, maybe you can try to log index out in titleHandleChange, so you can find out which item that you're actually editing.
ps, you could write export default class MyClass extends ... so you don't need to write export default MyClass at the end of the file again.
The state has already been changed correctly. You can add a console log to see it:
render() {
console.log(this.state);
return (
...
The reason that it is not showing on your screen is the {display: 'none'} that you set for id 'add-button'. Your var y = document.getElementById("add-button") will just get the first element, but not the whole list of elements.
FYI it is not quite right to use document.getElementById in a react project for your case. It should be done by state. Here is a complete example:
export class MyClass extends Component {
constructor(props) {
super(props);
this.state = {
input: [{title: ''}],
showAnotherListBtn: true,
showList: false,
};
}
onClickAnotherListBtn = () => {
this.setState({
showAnotherListBtn: false,
showList: true,
});
}
addList = () => {
this.setState({
input: [...this.state.input, {title: ''}],
});
}
handleChange(e, index) {
const {name, value} = e.target;
const list = [...this.state.input];
list[index][name] = value;
this.setState({input: list});
}
render() {
console.log(this.state);
return (
<div>
{this.state.showAnotherListBtn ?
<button id="btn-list" className="btn btn-default btn-list" type="submit" onClick={this.onClickAnotherListBtn}>Add Another
List</button> : null}
{this.state.showList && this.state.input.map((x, i) => {
return (
<div key={i} id="add-button" className="add-button">
<input id="input" type="text" onChange={(e) => {
this.handleChange(e, i)
}} value={x.title} name="title" className="form-control add-input"/>
<button className="btn btn-default btn-list-add" onClick={this.addList} type="submit">Add</button>
</div>
)
})}
</div>
)
}
}
export default MyClass
As you can see from the above code, you an just use a boolean state to determine whether you should show the element or not.
Hope this helps ;)

Toggle single element in React

I have one question. I created contact form in react and I want to collapsed only single contact, which was clicked. Toggle is method which should collapse it. And colapse is state. My problem is that when I click it affect all contact and all are collapsed. How can I improve it?
ContactBook.js
import React, { Component } from "react";
import Contact from "../Contact/Contact";
import "./ContactBook.css";
class ContactBook extends Component{
constructor(props){
super(props);
this.state = {
colapse :true,
contacts: [
{
id: 1,
name: 'Propulsion Academy',
address: 'Zurich',
avatar: 'propulsion-academy-logo.png'
},
{
id: 2,
name: 'Propulsion Academy',
address: 'Luzern',
avatar: 'propulsion-academy-logo.png'
},
{
id: 3,
name: 'Propulsion Academy',
address: 'Munich',
avatar: 'propulsion-academy-logo.png'
},
],
};
}
toggle=()=>{
const doesShow = this.state.colapse;
this.setState({colapse: !doesShow});
}
deleteContact=(contactIndex)=>{
//with slice method we create copy of an array
const contacts =this.state.contacts.slice();
contacts.splice(contactIndex, 1);
this.setState({contacts: contacts})
}
//get name from input
addName = e =>{
this.setState({
name: e.target.value,
})
}
//get address from input
addAddress = e =>{
this.setState({
address: e.target.value,
})
}
//update state on button click
handleSubmit = (e) =>{
e.preventDefault()
if(this.state.name && this.state.address) {
this.setState(state =>{
const newContact = {
id: Math.max(...state.contacts.map(c => c.id))+1,
name: this.state.name,
address: this.state.address,
}
return{
contacts:[...state.contacts, newContact]
}
})
}
}
render() {
return (
<div className="contactBook">
<form className ="addContact" >
<p>New Contact</p>
<label id="name"><p>Name</p><input type='text' id="name" onChange={this.addName}/></label>
<label id="address"><p>Address:</p><input type='text' id="address" onChange={this.addAddress} /></label>
<input type='file' name='file' />
<button type='submit' onClick= {this.handleSubmit}>SUBMIT</button>
</form>
<div className="contacts">
{this.state.contacts.map((contact, index) =>
< Contact key={contact.id} contact={contact} delete={()=>this.deleteContact(index)} colapse={this.state.colapse} toggle={this.toggle}/>)
}
</div>
</div>
);
}
};
export default ContactBook;
Contact.js
import React from "react";
import "./Contact.css";
import avatar from '../assets/user.png'
const Contact = (props) =>{
return (
<div className = "col" >
<img src={avatar} alt="avatar" onClick={props.toggle}/>
{props.colapse === true ?
<div>
<p>Name: {props.contact.name}</p>
<p>Address: {props.contact.address}</p>
<button onClick={props.delete}> Delete </button>
</div> : null
}
</div>
)
};
export default Contact;
I recommend to you to move the collapse and his method to the Contact component it self like this :
const Contact = (props) =>{
[collapse,setCollapse] = useState(true)
return (
<div className = "col" >
<img src={avatar} alt="avatar" onClick{()=>setCollape(prev=>!prev)}/>
{collapse === true ?
<div>
<p>Name: {props.contact.name}</p>
<p>Address: {props.contact.address}</p>
<button onClick={props.delete}> Delete </button>
</div> : null
}
</div>
)
};
In this component I created a state that will manage the collapse for each of the component the render in the map.
further more, the prev give you the last value you submit and it's best practice to use the prev instead of just setCollapse(!collapse)
You have one function for all contacts, and since you use .map() they will all behave the same, since toggling one toggles the state which is used to render all individual contacts. The solution would be to pass the selected contact in your state so your app actually knows which one is to be rendered! Hopefully that makes sense!
Good luck and let us know how things work out!
This is it:
ContactBook.js
import React, { Component } from "react";
import Contact from "./Contact";
// import "./ContactBook.css";
class ContactBook extends Component {
constructor(props) {
super(props);
this.state = {
contacts: [
{
id: 1,
name: "Propulsion Academy",
address: "Zurich",
avatar: "propulsion-academy-logo.png",
colapse: true
},
{
id: 2,
name: "Propulsion Academy",
address: "Luzern",
avatar: "propulsion-academy-logo.png",
colapse: true
},
{
id: 3,
name: "Propulsion Academy",
address: "Munich",
avatar: "propulsion-academy-logo.png",
colapse: true
}
]
};
}
// toggle = () => {
// const doesShow = this.state.colapse;
// this.setState({ colapse: !doesShow });
// };
deleteContact = contactIndex => {
//with slice method we create copy of an array
const contacts = this.state.contacts.slice();
contacts.splice(contactIndex, 1);
this.setState({ contacts: contacts });
};
togglecontact = contactIndex => {
let contacts = this.state.contacts.slice();
contacts[contactIndex].colapse = !contacts[contactIndex].colapse;
this.setState({ contacts: contacts });
};
//get name from input
addName = e => {
this.setState({
name: e.target.value
});
};
//get address from input
addAddress = e => {
this.setState({
address: e.target.value
});
};
//update state on button click
handleSubmit = e => {
e.preventDefault();
if (this.state.name && this.state.address) {
this.setState(state => {
const newContact = {
id: Math.max(...state.contacts.map(c => c.id)) + 1,
name: this.state.name,
address: this.state.address
};
return {
contacts: [...state.contacts, newContact]
};
});
}
};
render() {
return (
<div className="contactBook">
<form className="addContact">
<p>New Contact</p>
<label id="name">
<p>Name</p>
<input type="text" id="name" onChange={this.addName} />
</label>
<label id="address">
<p>Address:</p>
<input type="text" id="address" onChange={this.addAddress} />
</label>
<input type="file" name="file" />
<button type="submit" onClick={this.handleSubmit}>
SUBMIT
</button>
</form>
<div className="contacts">
{this.state.contacts.map((contact, index) => (
<Contact
key={contact.id}
contact={contact}
togglecontact={() => this.togglecontact(index)}
delete={() => this.deleteContact(index)}
colapse={this.state.colapse}
toggle={this.toggle}
/>
))}
</div>
</div>
);
}
}
export default ContactBook;
contactbook.js
import React from "react";
// import "./Contact.css";
const Contact = props => {
let buffer;
props.contact.colapse === true
? (buffer = (
<div>
<p>Name: {props.contact.name}</p>
<p>Address: {props.contact.address}</p>
<button onClick={props.delete}> Delete </button>
</div>
))
: null;
return (
<div className="col">
<img
onClick={props.togglecontact}
src="data:image/jpeg;base64,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"
/>
{buffer}
</div>
);
};
export default Contact;
can check it at:https://stackblitz.com/edit/react-mrvrr1?file=src%2FContact.js
only Toggling single element.

how to separate multiple input from the same div and get it in the parent component accordingly

I have a input (Inputu) function like so :
cexport default ({ onChange, value1, value2, value3, name }: any) => {
return (
<div name={name}>
<form >
<div>
<label> </label>
<input value={value1} onChange={onChange} />
</div>
<div>
<label></label>
<select onChange={onChange} value = {value2}>
<option>text</option>
</select>
</div>
<div >
<label></label>
<select value={value3} onChange={onChange}>
<option>text</option>
</select>
</div>
</form>
</div>
this is a dynamic function that added in the app component as the user pressing a button like so:
class App extends Component {
constructor(props: any) {
super(props);
this.state = {
clikctime: 0,
q1: []
};
}
onChange = ({ currentTarget }) => {
const value1= currentTarget.value.value1;
const value2 = currentTarget.value.value2;
const value3 = currentTarget.value.value3;
const index = this.state.clikctime;
let q1 = [...this.state.q1];
q3[index - 1] = [value1,value2 ,value3];
this.setState({ q1 });
};
renderInputs = ({ value1, value2, value3 }, idx) => {
const name = this.state.clikctime;
return (
<Inputu key={idx} value1={value1} value2={value2} value3={value3} name={name} onChange=
{this.onChangeu} />
);
};
addInputs = () => {
const newclick = this.state.clikctime;
const nw = newclick + 1;
this.setState({ clikctime: nw });
this.setState({ q1: this.state.q1.concat(nw) });
};
render() {
return (
<div>
<button
onClick={this.addInputs}>
click on me to add question !
</button>
</div>
<div>
{this.state.q1.map(this.renderInputs)}
</div>
);
}
}
I couldn't manage to separate the value, also how can I add the state as list\dic so the input would be associated with name\id (regardless of how many times the user klicked the button and add input)
In the end I would like to get somting like that:
q1: {name1:[value1 ,value2, value3], name2:[value1 ,value2, value3]....}
Thanks for trying to help

apps updates with previously clicked info- react and redux

Problem is when i click on edit button, they fill the text inputs with the previously clicked info. when i check my store, the right object is placed into my editor object. so I don't know what's wrong
import React,{Component} from 'react';
import {get_contact, update_contact} from '../actions/';
import {connect} from 'react-redux';
class Edit extends Component {
constructor(props){
super(props);
this.state = {// constructor and initialization of my state
namex: '',
emailx: '',
phonex: '',
idx: ''
}
this.nameInput = React.createRef();
this.emailInput = React.createRef(); //creating references for inputs
this.phoneInput = React.createRef();
}
componentDidMount(){
let id_1 =this.props.location.id;
this.props.get_contact(id_1);
const {id,name, email, phone} = this.props.edits;
this.setState({
namex: name,
emailx: email, //fetching info from props
phonex: phone,
idx: id
})
}
submit_handler = (e) => {
e.preventDefault();
this.setState({
name: this.nameInput.current.value,
email: this.emailInput.current.value,// accessing input values
phone: this.phoneInput.current.value
})
// this.props.update_contact(this.state);
// this.setState({
// name: '',
// email: '',
// phone: '',
// id: ''
// }) console.log(this.state)
} render(){
const {id,namex, emailx, phonex} = this.state;
return (
<div>
<div className = "ml-20 mt-10 " style = {{width: '500px', marginLeft: '400px',marginTop: '80px'}}>
<h3 className="display-4 text-center">Edit Contacts </h3>
<form onSubmit = {this.submit_handler.bind(this)}>
<div className = "form-group">
<label htmlFor ="Names">Names</label>
<input className="form-control" type="text" name="name" defaultValue={namex} ref = {this.nameInput}/>
</div>
<div className = "form-group">
<label htmlFor="email">Email</label>
<input className="form-control" type="email" name="email" defaultValue={emailx} ref = {this.emailInput}/>
</div>
<div className = "form-group">
<label htmlFor="contact">Contact</label>
<input className="form-control" type="text" name="phone" defaultValue={phonex} ref = {this.phoneInput} />
</div>
<div className = "form-group">
<input className="btn btn-primary btn-block" type="submit" name="editor" value="Edit" />
</div>
</form>
</div>
</div>
) } } const mapStateToProps = (state) => ({ edits: state.Users.editor }) export default connect(mapStateToProps, {get_contact,update_contact})(Edit);

ReactJs Todo List push state

I have written the start of a todo list in reactjs and was looking to improve the functionality of how the todos are added to the state. Currently I am concat(ting) the value in put to an array which is in the state, then splicing off the selected li element. It seems to be a bit buggy when you add the first todo. Should i be using reacts immutability helpers to acheive this? Seems overkill to add another thing that can be acheived in plain js.
//Input component
const Input = props => {
return (
<div className="form-group">
<input
className="form-control"
value={props.value}
onChange={props.update}
type="text"
/>
<button className="btn btn-default" onClick={props.handleClick}>
Add Todo
</button>
</div>
);
};
//display list of todos
const Displaytodo = (props) => {
const todolist = props.todo;
const listItems = todolist.map((todo, index) =>
<li
className={
props.highlight ? 'list-unstyled todoItem highlight' : 'list-unstyled todoItem '
}
key={index}>
{todo}
<div
onClick={props.removeTodo.bind(this, index)}
className="removeTodo">
<i className="fa fa-trash" />
</div>
<div onClick={props.changeHighlight.bind(this,index)} className="checkTodo">
<i className="fa fa-check-circle" onClick={props.highlight} />
</div>
</li>
);
return <ul className="todos">{listItems}</ul>;
};
//controlled state component
class Layout extends React.Component {
constructor() {
super();
this.state = { text: "Hello", todo: [], highlight: false };
}
update(e) {
this.setState({ text: e.target.value });
}
handleClick() {
const text = this.state.text;
if (text.length > 0) {
this.setState(
{ todo: this.state.todo.concat(text), text: "", highlight: false },
function() {
console.log(this.state.todo);
}
);
} else {
alert("please enter something");
}
}
removeTodo(e) {
this.state.todo.splice(e, 1);
this.setState({ todo: this.state.todo });
}
changeHighlight(index, e) {
const highlight = this.state.highlight;
this.setState(prevState => ({
highlight: !prevState.highlight
}));
}
render() {
return (
<div className="container">
<div className="row">
<div className="col-md-4 col-md-offset-4">
<div className="wrapper">
<h1>Todo List</h1>
<Input
value={this.state.text}
update={this.update.bind(this)}
handleClick={this.handleClick.bind(this)}
/>
<Displaytodo
removeTodo={this.removeTodo.bind(this)}
todo={this.state.todo}
changeHighlight={this.changeHighlight.bind(this)}
highlight={this.state.highlight}
/>
</div>
</div>
</div>
</div>
);
}
}
const app = document.getElementById("app");
ReactDOM.render(<Layout />, app);
https://codepen.io/mhal12/pen/MomWVg
Also when the user clicks the green tick, it will highlight the row by toggling class 'highlight' off and on, but in console it giving an error. which links to
https://facebook.github.io/react/docs/error-decoder.html?invariant=94&args[]=onClick&args[]=boolean
Simply remove the onClick on <i className="fa fa-check-circle" onClick={props.highlight} />.
As for the highlighting on each todo, it's a bit more complex. You have to have an id on each todo, and then pass the id to the changeHighlight function. You have to remove highlight from global state, and assign a highlight boolean on each todo. Then you have to display todos accordingly.
Same stuff for the removeTodo function, you pass in an id to remove it in the parent component.
Here's the full code :
const Input = props => {
return (
<div className="form-group">
<input
className="form-control"
value={props.value}
onChange={props.update}
type="text"
/>
<button className="btn btn-default" onClick={props.handleClick}>
Add Todo
</button>
</div>
);
};
const Displaytodo = (props) => {
const changeHighlight = function(id) {
props.changeHighlight(id);
}
const removeTodo = function(id) {
props.removeTodo(id);
}
const todolist = props.todo;
const listItems = todolist.map((todo, index) =>
<li
className={
todo.highlight ? 'list-unstyled todoItem highlight' : 'list-unstyled todoItem '
}
key={todo.id}>
{todo.text}
<div
onClick={removeTodo.bind(event, todo.id)}
className="removeTodo">
<i className="fa fa-trash" />
</div>
<div onClick={changeHighlight.bind(event, todo.id)} className="checkTodo">
<i className="fa fa-check-circle" />
</div>
</li>
);
return <ul className="todos">{listItems}</ul>;
};
class Layout extends React.Component {
constructor() {
super();
this.state = {text: "Hello", todo: []};
}
update(e) {
this.setState({ text: e.target.value });
}
handleClick() {
const text = this.state.text;
if (text.length > 0) {
this.setState(
{ todo: this.state.todo.concat({
id: this.state.todo.length + 1,
text: this.state.text,
highlight: false
}), text: ""},
function() {
console.log(this.state.todo);
}
);
} else {
alert("Please enter something");
}
}
removeTodo(id) {
let todos = this.state.todo;
for (let i = 0; i < todos.length; i++) {
let todo = todos[i];
if (todo.id == id) {
todos.splice(i, 1);
}
}
this.setState({ todo: todos });
}
changeHighlight(id) {
let todos = this.state.todo;
for (let i = 0; i < todos.length; i++) {
let todo = todos[i];
if (todo.id == id) {
todos[i].highlight = !todos[i].highlight;
}
}
this.setState({
todo : todos
});
}
render() {
return (
<div className="container">
<div className="row">
<div className="col-md-4 col-md-offset-4">
<div className="wrapper">
<h1>Todo List</h1>
<Input
value={this.state.text}
update={this.update.bind(this)}
handleClick={this.handleClick.bind(this)}
/>
<Displaytodo
removeTodo={this.removeTodo.bind(this)}
todo={this.state.todo}
changeHighlight={this.changeHighlight.bind(this)}
/>
</div>
</div>
</div>
</div>
);
}
}
const app = document.getElementById("app");
ReactDOM.render(<Layout />, app);

Categories

Resources