React js state management - javascript

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>

Related

How to take the input from a react component and pass the length of the input to another react component

I have a textarea where I want to have an onChange event. When text is entered, I want to compute the length of the string entered and then pass it to another react component to display the character count. However, I'm having trouble passing the data into my react component.
I have 3 react components in total:
SegmentCalculator: this is my full app
InputBox: this is where a user would enter their string
CharacterBox: this is where I'd like to display my character count
Here's what I have so far:
class InputBox extends React.Component {
constructor(props) {
super(props);
this.state = {
value: null,
}
}
render() {
return (
<label>
Input:
<textarea
type="text"
value={this.state.value}
onChange={() => this.props.onChange(this.state.value)}
/>
</label>
);
}
}
class CharacterBox extends React.Component {
render() {
return (
<div>Character Count:{this.props.charCount}</div>
)
}
}
class SegmentCalculator extends React.Component {
constructor(props) {
super(props);
this.state = {
inputChars: null,
};
}
handleChange(value) {
this.setState({
inputChars: value,
inputCharsLength: value.length,
});
}
render() {
let charaterCount = this.state.inputCharsLength;
return (
<div className="segment-calculator">
<div className="input-box">
<InputBox onChange={() => this.handleChange()} />
</div>
<div className="characters">
<CharacterBox charCount={charaterCount}/>
</div>
</div>
);
}
}
You have a semi-controlled input, meaning, it has local state but you don't update it.
Pass the input state from the parent.
InputBox - Pass the value prop through to the textarea element. Pass the onChange event's target value to the onChange callback prop.
class InputBox extends React.Component {
render() {
return (
<label>
Input:
<textarea
type="text"
value={this.props.value}
onChange={(e) => this.props.onChange(e.target.value)}
/>
</label>
);
}
}
SegmentCalculator - Pass this.state.inputChars to the InputBox value prop. The input length is derived state so there is no reason to store it in state.
class SegmentCalculator extends React.Component {
constructor(props) {
super(props);
this.state = {
inputChars:'',
};
}
handleChange = (value) => {
this.setState({
inputChars: value,
});
}
render() {
const { inputChars } = this.state;
return (
<div className="segment-calculator">
<div className="input-box">
<InputBox
onChange={this.handleChange}
value={inputChars}
/>
</div>
<div className="characters">
<CharacterBox charCount={inputChars.length}/>
</div>
</div>
);
}
}
class InputBox extends React.Component {
render() {
return (
<label>
Input:
<textarea
type="text"
value={this.props.value}
onChange={(e) => this.props.onChange(e.target.value)}
/>
</label>
);
}
}
class CharacterBox extends React.Component {
render() {
return (
<div>Character Count:{this.props.charCount}</div>
)
}
}
class SegmentCalculator extends React.Component {
constructor(props) {
super(props);
this.state = {
inputChars: '',
};
}
handleChange = (value) => {
this.setState({
inputChars: value,
});
}
render() {
const { inputChars } = this.state;
return (
<div className="segment-calculator">
<div className="input-box">
<InputBox
onChange={this.handleChange}
value={inputChars}
/>
</div>
<div className="characters">
<CharacterBox charCount={inputChars.length}/>
</div>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<SegmentCalculator />,
rootElement
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="root" />

Why isn't React re-rendering the page after the state is changed?

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.

React JS : grandparent component's setState method doesn't update state of a grandchild input field onChange event click

In the same code, I was able to the get the grandparent component's setState method to update accordingly for an onClick event from the grandchild component, however, for the onChange event, it is failing. I am not getting any errors.
class GrandChild extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
changeNumber=()=> {
this.props.changeNumber();//call child method
}
handleChange(e) {
this.props.onChange(e.target.value);
}
render() {
const data = this.props.data;
return(
<div>
<h1>The number is {this.props.number}</h1>
<input type="text" value = {data} onChange={this.handleChange} />
<button onClick={this.changeNumber}>Increase number by 1</button>
</div>
)
}
}
class Child extends React.Component {
render() {
return(
<div>
<GrandChild number={this.props.number} changeNumber={this.props.changeNumber} value={this.props.data} onChange={this.props.handleChange}/>
</div>
)
}
}
class App extends React.Component {
constructor() {
super()
this.state = {
number: 1,
data: ""
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(data) {
this.setState({data:this.state.data});
console.log(data);
}
changeNumber=()=>{
this.setState((prevState)=>{
console.log(prevState,this.state.data);
return {
number : prevState.number + 1
}
});
}
render() {
const data = this.state.data;
const input = data;
return (
<Child number={this.state.number}
changeNumber = {this.changeNumber}
data={input}
onChange = {this.handleChange}
/>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
Console Result:
Object {
data: "",
number: 1
} ""
result screenshot:
console.log result
see code pen for live code:
https://codepen.io/codehorse/pen/yLyEwBw?editors=0011
Your improved code with live demo https://codesandbox.io/s/laughing-sky-kk97b
What need to change <GrandChild number={this.props.number} changeNumber={this.props.changeNumber} value={this.props.data} onChange={this.props.onChange}/>
Complete Code
class GrandChild extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
changeNumber = () => {
this.props.changeNumber(); //call child method
};
handleChange(e) {
this.props.onChange(e.target.value);
}
render() {
const data = this.props.data;
return (
<div>
<h1>The number is {this.props.number}</h1>
<input type="text" value={data} onChange={this.props.onChange} />
<button onClick={this.changeNumber}>Increase number by 1</button>
</div>
);
}
}
class Child extends React.Component {
render() {
return (
<div>
<GrandChild
number={this.props.number}
changeNumber={this.props.changeNumber}
value={this.props.data}
onChange={this.props.onChange}
/>
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
number: 1,
data: ""
};
}
handleChange = e => {
this.setState({ data: e.target.value });
console.log(e.target.value);
};
changeNumber = () => {
this.setState(prevState => {
console.log(prevState, this.state.data);
return {
number: prevState.number + 1
};
});
};
render() {
const data = this.state.data;
const input = data;
return (
<Child
number={this.state.number}
changeNumber={this.changeNumber}
data={input}
onChange={this.handleChange}
/>
);
}
}
export default App;

ReactJS hover/mouseover effect for one list item instead of all list items

I am a React novice, so this might seem really simple, or maybe it isn't, I'm not sure. I'm building a basic to-do list. I want a mouseover effect on list items to pop up "delete this" text. But for my code so far, when I mouseover a list item, "delete this" pops up for all list items instead of just the one.
When I tried solving this by creating a new component for individual list items, that didn't seem to work. Any help is much appreciated!
class ToDosContainer extends React.Component {
constructor(props) {
super(props)
this.state = {
heading: 'Something You Need To Do?',
todos: [
'wake up',
'brush your teeth'
],
}
this.addToDo = this.addToDo.bind(this)
}
addToDo(todo) {
this.setState((state) => ({
todos: state.todos.concat([todo])
}))
}
render() {
return (
<div>
<h1>To Do List</h1>
<h3>{this.state.heading}</h3>
<AddToDo addNew={this.addToDo} />
<ShowList tasks={this.state.todos} />
</div>
)
}
}
class AddToDo extends React.Component {
constructor(props) {
super(props)
this.state = {
newToDo: ''
}
this.updateNewToDo = this.updateNewToDo.bind(this)
this.handleAddToDo = this.handleAddToDo.bind(this)
}
//I think I can delete this part
updateNewToDo(e) {
this.setState({
newToDo: e.target.value
})
}
//
handleAddToDo() {
this.props.addNew(this.state.newToDo)
this.setState({
newToDo: ''
})
}
render() {
return (
<div>
<input
type="text"
value={this.state.newToDo}
onChange={this.updateNewToDo}
/>
<button onClick={this.handleAddToDo}> Add To Do </button>
</div>
)
}
}
class ShowList extends React.Component {
constructor(props) {
super(props)
this.state = {
newToDo: ''
}
}
onMouseOver(e) {
this.setState({
text: 'delete me'
})
console.log('hey')
}
onMouseOut(e) {
this.setState({
text: ''
})
console.log('hey hey')
}
render() {
const { text } = this.state;
return (
<div>
<h4>To Do's</h4>
<ul>
{this.props.tasks.map((todo) => {
return <li onMouseEnter={this.onMouseOver.bind(this)} onMouseLeave={this.onMouseOut.bind(this)}> {todo} {text}</li>
})}
</ul>
</div>
)
}
}
ReactDOM.render(<ToDosContainer />, document.getElementById('helloworld'));
I would make a Task component. You don't want to set the state of the text in the ListView component, because then this.state.text is shared by each task in the map. Each task should be aware of its own hover, and not the hover of the other children.
class Task extends React.Component {
state = {
text: ""
};
onMouseOver(e) {
this.setState({
text: "delete me"
});
}
onMouseOut(e) {
this.setState({
text: ""
});
}
render() {
return (
<li
onMouseEnter={this.onMouseOver.bind(this)}
onMouseLeave={this.onMouseOut.bind(this)}
>
{this.props.todo} {this.state.text}
</li>
);
}
}
class ShowList extends React.Component {
constructor(props) {
super(props);
this.state = {
newToDo: ""
};
}
render() {
const { text } = this.state;
return (
<div>
<h4>To Do's</h4>
<ul>
{this.props.tasks.map(todo => {
return <Task todo={todo} />;
})}
</ul>
</div>
);
}
}

Rendering of react components in unexpected way

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?

Categories