I am new to react. I am trying to create a simple todolist using store2.js library. There is a problem in rendering the elements in that to do list.
var React = require('react');
var store = require("store2");
import {Button} from "react-bootstrap";
class CreateToDoList extends React.Component {
componentDidMount() {
this.inputVal="";
}
constructor() {
super();
this.addValueToList = this.addValueToList.bind(this);
this.handleInput=this.handleInput.bind(this);
};
handleInput(e)
{
this.inputValue=e.target.value;
}
addValueToList(){
if(store.has("todoList")===false)
{
store.set("todoList",{count:0})
}
var count=store.get("todoList").count;
count+=1;
var obj={
value:this.inputValue,
isChecked:false
};
store.transact("todoList",function(elements){
elements[count+""]=obj;
elements.count=count
});console.log(store.getAll("todoList"));debugger;
}
render() {
return ( <div>
<input type="input" onChange={this.handleInput}/>
<Button bsStyle="primary" onClick={this.addValueToList}>Add</Button>
<CreateShowPreviousTasks/>
</div>
)
}
}
class todoValues extends React.Component{
componentDidMount(){
this.handleClick=this.handleClick.bind(this);
}
handleClick(){
}
render(){
console.log(this.props)
return(
<div >
<input type="checkbox" checked={this.props.isCheck}></input>
<input type="input">{this.prop.value}</input>
</div>
)
}
}
class CreateShowPreviousTasks extends React.Component {
componentDidMount() {
console.log("here")
}
constructor(){
super();
this.handleClick=this.handleClick.bind(this);
}
handleClick(event)
{
}
render() {
if (store.has('todoList') !== undefined) {
var divElements=[];
this.loop=0;
var count=store.get("todoList").count;
for(this.loop=0;this.loop<count;this.loop++)
{
var obj=store.get("todoList");
obj=obj[count+""];
divElements.push(
<todoValues value={obj.value} key={this.loop+1}/>
)
}
} else {
store.set('todoList',{
count:0
})
}
return (<div>{divElements}</div>
)
}
}
export default CreateToDoList;
The class todoValues adds the div elements of two input buttons wrapped in a div. But the rendering is only done as <todovalues value="as"></todovalues>.
The class CreateShowPreviousTasks which retrieves the list of stored items in the local storage items and passing those values as properties to todoValues and wrapping as in a div.
use the folliwng syntax to render a list
render() {
let todos = store.get("todolist");
return (
<div>
{
Object.keys(todos).map(function(key){
let todo = todos[key];
return (<todoValues value={ todo.value } key={ key }/>)
})
}
</div>
}
Does this answer your question?
Related
so I was working on a basic Todo app using React.js and I was wondering why the todo component does not automatically re-render once the state changed (the state contains the list of todos- so adding a new todo would update this array)? It is supposed to re-render the Header and the Todo component of the page with the updated array of todos passed in as props. Here is my code:
import React from 'react';
import './App.css';
class Header extends React.Component {
render() {
let numTodos = this.props.todos.length;
return <h1>{`You have ${numTodos} todos`}</h1>
}
}
class Todos extends React.Component {
render() {
return (
<ul>
{
this.props.todos.map((todo, index) => {
return (<Todo index={index} todo={todo} />)
})
}
</ul>
)
}
}
class Todo extends React.Component {
render() {
return <li key={this.props.index}>{this.props.todo}</li>
}
}
class Form extends React.Component {
constructor(props) {
super(props);
this.addnewTodo = this.addnewTodo.bind(this);
}
addnewTodo = () => {
let inputBox = document.getElementById("input-box");
if (inputBox.value === '') {
return;
}
this.props.handleAdd(inputBox.value);
}
render() {
return (
<div>
<input id="input-box" type="text"></input>
<button type="submit" onClick={this.addnewTodo}>Add</button>
</div>
)
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = { todos: ['task 1', 'task 2', 'task 3']}
this.handleNewTodo = this.handleNewTodo.bind(this);
}
handleNewTodo(todo) {
let tempList = this.state.todos;
tempList.push(todo);
this.setState = { todos: tempList };
}
render() {
return (
<div>
<Header todos={this.state.todos} />
<Todos todos={this.state.todos} />
<Form todos={this.state.todos} handleAdd={this.handleNewTodo} />
</div>
)
}
}
You are not updating the state correctly.
You need to make a copy of the this.state.todos, add the new todo in the copied array and then call this.setState function
handleNewTodo(todo) {
let tempList = [...this.state.todos];
tempList.push(todo);
this.setState({ todos: tempList });
}
Notice that this.setState is a function
You're updating state incorrectly,
handleNewTodo(todo) {
let tempList = [...this.state.todos];
tempList.push(todo);
this.setState({ todos: tempList });
}
This is the correct syntax.
Im having issues with react todo list.
When submitted the list item appears fine.
I should then be able to delete this be clicking on the item,however nothing happens. As soon as i try to add another item the pages refreshes and all items are removed?
console log just shows
[object object]
App
import React, { Component } from 'react';
import './App.css';
import TodoItem from './TodoItem';
class App extends Component {
constructor(props) {
super(props);
this.state = {
items: []
};
this.addItems = this.addItems.bind(this);
this.deleteItem = this.deleteItem.bind(this);
}
addItems(e) {
if (this._inputElement !== '') {
let newItem = {
text: this._inputElement.value,
key: Date.now()
};
this.setState(prevState => {
return {
items: prevState.items.concat(newItem)
};
});
}
this._inputElement.value = '';
e.preventDefault();
}
deleteItem(key) {
console.log('key is' + key);
console.log('itesm as' + this.state.items);
var filteredItems = this.state.items.filter(function(item) {
return item.key !== key;
});
this.setState = {
items: filteredItems
};
}
render() {
return (
<div className="app">
<h2> things to do</h2>
<div className="form-inline">
<div className="header">
<form onSubmit={this.addItems}>
<input
ref={a => (this._inputElement = a)}
placeholder="enter task"
/>
<button type="submit">add</button>
</form>
</div>
</div>
<TodoItem entries={this.state.items} delete={this.deleteItem} />
</div>
);
}
}
export default App;
todoItem
import React, { Component } from 'react';
//search bar
class TodoItem extends Component {
constructor(props) {
super(props);
this.createTasks = this.createTasks.bind(this);
}
createTasks(item) {
return (
<li onClick={() => this.delete(item.key)} key={item.key}>
{item.text}
</li>
);
}
delete(key) {
console.log('key is ' + key);
this.props.delete(key);
}
render() {
let todoEntries = this.props.entries;
let listItems = todoEntries.map(this.createTasks);
return <ul className="theList">{listItems}</ul>;
}
}
export default TodoItem;
You are assigning to setState instead of using it as a method that it is
Change
this.setState = {
items: filteredItems
};
to
this.setState({
items: filteredItems
});
And that is also the reason it will reload the app, as you have overwritten the setState method you should be getting an error that setState is not a function and it would crash the app.
class Todo extends React.Component {
constructor(props) {
super(props);
this.state = {
saveText: '',
}
this.handleSaveText = this.handleSaveText.bind(this);
this.displayText = this.displayText.bind(this);
}
handleSaveText(saveText) {
this.setState({
saveText: saveText
})
}
render() {
return (
<div>
<Save saveText = {this.state.saveText}
onSaveTextChange = {this.handleSaveText}
/>
<Display saveText = {this.state.saveText}
/> </div>
);
}
}
class Save extends React.Component {
constructor(props) {
super(props);
this.handleSaveText = this.handleSaveText.bind(this);
}
handleSaveText(e) {
this.props.onSaveTextChange(e.target.value);
}
render() {
return ( <div>
<input type = "text"
value = {
this.props.saveText
}
onChange = {
this.handleSaveText
}
/> <input type = "button"
value = "save"
onClick = {
this.displayText
}
/> </div>
);
}
}
class Display extends React.Component {
render() {
var todos = [];
var todo = this.props.saveText;
//todos.push(todo);
return ( <div> {
todos
} </div>
);
}
}
ReactDOM.render( <Todo / > ,
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>
I am new to react still trying to figure out how state works.I am trying to implement a simple todo app which takes in an input and displays an output on the screen after click of a button.
According to the minimal UI representation I broke the UI into two parts, the first contains the Save class which has an input box and a button. The second contains a display class which will display the contents of the input box.
I am storing the value of input box in state.
How can I pass that state into Display class and display the values on the screen?
Codepen Link
This will do it:
class Todo extends React.Component {
constructor(props) {
super(props);
this.state = {
saveText: '',
displayText: []
}
this.handleSaveText = this.handleSaveText.bind(this);
this.displayText = this.displayText.bind(this);
}
handleSaveText(saveText) {
this.setState({
saveText: saveText
})
}
displayText(text) {
let newDisplay = this.state.displayText;
newDisplay.push(text);
this.setState({displayText: newDisplay});
}
render() {
return (
<div>
<Save saveText = {this.state.saveText}
onSaveTextChange = {this.handleSaveText}
displayText={this.displayText}
/>
<Display displayText = {this.state.displayText}
/> </div>
);
}
}
class Save extends React.Component {
constructor(props) {
super(props);
this.handleSaveText = this.handleSaveText.bind(this);
this.displayText = this.displayText.bind(this);
}
handleSaveText(e) {
this.props.onSaveTextChange(e.target.value);
}
displayText() {
this.props.displayText(this.props.saveText);
}
render() {
return ( <div>
<input type = "text"
value = {
this.props.saveText
}
onChange = {
this.handleSaveText
}
/> <input type = "button"
value = "save"
onClick = {
this.displayText
}
/> </div>
);
}
}
class Display extends React.Component {
render() {
return ( <div> {
this.props.displayText
} </div>
);
}
}
ReactDOM.render( <Todo / > ,
document.getElementById('root')
)
You can't push to the array in the render method because that won't exist anymore after it re-renders when it receives new props from you clicking the button again. My method saves an array of previous responses as "displayText" and sends that to the display component. Note that this method will display the entire array as a single line with no spaces. In practice you'll want to map it by doing this:
this.props.displayText.map((text, idx) => (<div key={idx}>{text}</div>));
Here is working example of todo list.
class Todo extends React.Component {
constructor(props) {
super(props);
this.state = {
text: '',
list: []
}
// this.handleSaveText = this.handleSaveText.bind(this);
this.addTodo = this.addTodo.bind(this);
}
handleSaveText(text) {
this.setState({
text: text
})
}
addTodo(saveText) {
var list = this.state.list;
list.push(saveText);
this.setState({
list: list
});
// to save to localstorage, uncomment below line
// window.localStorage.setItem('todos', list);
}
render() {
return ( <
div >
<
Save text = {
this.state.text
}
onClick = {
this.addTodo
}
/> <
Display list = {
this.state.list
}
/> < /
div >
);
}
}
class Save extends React.Component {
constructor(props) {
super(props);
this.state = {
input: this.props.text || '',
}
this.onChange = this.onChange.bind(this);
this.addToTodo = this.addToTodo.bind(this);
}
onChange(e) {
this.setState({
input: e.target.value
});
}
addToTodo() {
this.props.onClick(this.state.input);
this.setState({
input: ''
});
}
render() {
return ( < div >
<
input type = "text"
value = {
this.state.input
}
onChange = {
this.onChange
}
/> <input type = "button"
value = "save"
onClick = {
this.addToTodo
}
/> </div >
);
}
}
class Display extends React.Component {
constructor(props) {
super(props);
this.state = {
todos: []
}
}
componentWillReceiveProps(nextProps) {
this.setState({
todos: nextProps.list
});
}
render() {
var i = 1;
var renderList = this.state.todos.map((name) => {
return <div key = {
i++
} > {
name
} < /div>;
});
return ( < div > {
renderList
} < /div>);
}
}
ReactDOM.render( < Todo / > ,
document.getElementById('root')
)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My React Project on CodePen</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/4.1.0/sanitize.css">
<link rel="stylesheet" href="css/style.processed.css">
</head>
<body>
<div id="root"></div>
<script src="https://npmcdn.com/react#15.3.0/dist/react.min.js"></script>
<script src="https://npmcdn.com/react-dom#15.3.0/dist/react-dom.min.js"></script>
</body>
</html>
If you are trying create a todo list, you can tweak a little bit by adding array list in the main TODO component, and it down to display component.
save component you just have to handle the input change and on click function.
simple enough.
You must use:
componentWillReceiveProps(nextProps)
in your Display Component.
This is a working example:
class Todo extends React.Component {
constructor(props) {
super(props);
this.state = {
todos: []
}
this.handleSaveText = this.handleSaveText.bind(this);
}
handleSaveText(saveText) {
let todos = this.state.todos;
todos.push(saveText);
this.setState({
todos: todos
});
}
render() {
return (
<div>
<Save
onSaveTextClick = {this.handleSaveText}
/>
<Display todos = {this.state.todos}
/> </div>
);
}
}
class Save extends React.Component {
constructor(props) {
super(props);
this.state = {
saveText: ''
}
this.handleSaveText = this.handleSaveText.bind(this);
this.handleChangeText = this.handleChangeText.bind(this);
}
handleChangeText(e){
this.setState({saveText: e.target.value});
}
handleSaveText(e) {
this.props.onSaveTextClick(this.state.saveText);
}
render() {
return ( <div>
<input type = "text"
onChange = {
this.handleChangeText
}
/> <input type = "button"
value = "save"
onClick = {
this.handleSaveText
}
/> </div>
);
}
}
class Display extends React.Component {
constructor(props){
super(props);
this.state = {
todos: []
}
}
componentWillReceiveProps(nextProps){
this.setState({todos: nextProps.todos});
}
render() {
let todos = this.state.todos.map((todo)=>{return <div>{todo}</div>});
return ( <div> {
todos
} </div>
);
}
}
ReactDOM.render( <Todo / > ,
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 have a React app like:
Main.js-
import React, { Component } from 'react';
import _ from 'underscore';
import ApplicationsButtons from '../components/ApplicationsButtons';
let applications_url = 'http://127.0.0.1:8889/api/applications'
export default class Main extends Component {
constructor(props) {
super(props);
this.state = {applications: [], selected_app: 1};
this.updateSelectedApp = this.updateSelectedApp.bind(this);
}
componentDidMount() {
let self = this;
$.ajax({
url: applications_url,
method: 'GET',
success: function(data) {
console.log(data);
let objects = data.objects;
let apps = objects.map(function(object) {
return {name: object.name, id: object.id};
});
console.log(apps);
self.setState({applications: apps});
}
});
}
updateSelectedApp(id) {
this.setState({selected_app: id});
}
render() {
return (
<div>
{this.state.selected_app}
<ApplicationsButtons apps={this.state.applications} />
</div>
);
}
}
ApplicationsButtons.js-
import React, { Component } from 'react';
export default class ApplicationsButtons extends Component {
render() {
var buttons = null;
let apps = this.props.apps;
let clickHandler = this.props.clickHandler;
if (apps.length > 0) {
buttons = apps.map(function(app) {
return (<button key={app.id}>{app.name} - {app.id}</button>);
// return (<button onClick={clickHandler.apply(null, app.id)} key={app.id}>{app.name} - {app.id}</button>);
});
}
return (
<div>
{buttons}
</div>
);
}
}
I want to pass an onClick to the buttons that will change the currently selected app. Somehow, I just got my first infinite loop in React ("setState has just ran 20000 times"). Apparently, when I tried to pass the event handler to be called on click, I told it to keep calling it.
The onClick function should change state.selected_app for the Main component, based on the id for the button that was clicked.
You are not passing the handler as prop.
Here's what you should do:
render() {
return (
<div>
{this.state.selected_app}
<ApplicationsButtons
apps={this.state.applications}
handleClick={this.updateSelectedApp}
/>
</div>
);
}
And in ApplicationButtons:
render() {
var buttons = null;
let apps = this.props.apps;
let clickHandler = this.props.handleClick;
if (apps.length > 0) {
buttons = apps.map(app =>
<button key={app.id} onClick={() => clickHandler(app.id)}>{app.name} - {app.id}</button>);
);
}
return (
<div>
{buttons}
</div>
);
}
import React from 'react';
import {render} from 'react-dom';
class Form extends React.Component {
constructor(props) {
super(props);
this.validate = this.validate.bind(this);
}
componentDidMount() {
}
validate() {
this.props.children.map((field)=> {
field.validate();
});
}
render() {
return (
<form className={this.props.className}>
{this.props.children}
</form>
);
}
}
export default Form;
Above is the Form.jsx
import React from 'react';
import '../form.css';
class TextField extends React.Component {
constructor(props) {
super(props);
this.state = {
valid: true
};
this.validate = this.validate.bind(this);
}
validate() {
if (!!this.props.required) {
if (this.refs.field.value.trim().length === 0) {
this.setState({
valid: false
});
return false;
} else {
this.setState({
valid: true
});
return true;
}
}
return true;
}
setValue(event) {
if (this.validate()) {
this.props.setValue(this.props.name, event.target.value);
}
}
render() {
var input = (
<span className={this.state.valid ? null : 'field-invalid'}>
<input ref="field" type="text" name={this.props.name} placeholder={this.props.placeholder}
onBlur={this.setValue.bind(this)}/>
</span>
);
var field = input;
if (this.props.label) {
field = (
<div className="row">
<label className={"col-3 align-r" + (!!this.props.required ? " field-required" : "")}>
{this.props.label}
</label>
<span className="col-6">
{input}
</span>
</div>
);
}
return field;
}
}
export default TextField;
this is a field and contains validate method. but this method is not accessible from Form.jsx this.props.children.
Another parent Contains
<Form ref={(form)=> {this.form = form;}} className="label-input-group">
<TextField label="Vehicle Owner" required={true} name="owner"/>
</Form>
validate function is undefined and throwing error. this.props.children not updated in the parent after children mount i think. Any way to make it work?
I'm not sure it's a good idea, but you can do something like that :
// Form
constructor(props) {
super(props);
this.validate = this.validate.bind(this);
this.childrenInstances = [];
this.props.children.forEach(field => {
field.ref = inst => this.childrenInstances.push(inst);
});
}
validate() {
this.childrenInstances.forEach(field => {
field.validate();
});
}