Countdown timer multiple item - javascript

I'm using this package called react-countdown-now I have many items I'm just wondering why all the items counting down same time
https://codesandbox.io/s/4rpn84j5m0
As you can see below in my example the new item rest the old item
import React, { Component } from 'react';
import { render } from 'react-dom';
import Countdown from 'react-countdown-now';
class App extends Component {
constructor() {
super();
this.state = {
show: false
};
}
componentDidMount() {
setTimeout(() => {
this.setState({ show: true })
}, 2500)
}
render() {
return (
<div>
<Countdown date={Date.now() + 10000} /><br />
{this.state.show ? <Countdown date={Date.now() + 10000} /> : <span>waiting new item....</span>}
<p>
Why the new item rest the old item?
</p>
</div>
);
}
}
render(<App />, document.getElementById('root'));
Why the new item rest the old item?

The reason is because the expression Date.now() + 10000 gets recalculated on every re-render / state update. You need to store the dates somewhere.
As an example:
https://codesandbox.io/s/zq1mv4zmz4
import React, { Component } from "react";
import { render } from "react-dom";
import Countdown from "react-countdown-now";
class App extends Component {
constructor() {
super();
this.state = {
dates: [Date.now() + 10000]
};
}
componentDidMount() {
setTimeout(() => {
this.setState({ show: true });
}, 2500);
}
render() {
return (
<div>
{this.state.dates.map(date => (
<div>
<Countdown date={date} />
</div>
))}
<button onClick={() => this.addDate()}>New Item</button>
<p>Why the new item rest the old item?</p>
</div>
);
}
addDate() {
let dates = [...this.state.dates, Date.now() + 10000];
this.setState({ dates });
}
}
render(<App />, document.getElementById("root"));

Related

React setState() needs 2 clicks to update UI

I'm new in React. My question may be common in React developers and there are many same questions but I still don't know how to resolve that. I must still click twice to update UI state. The first click just calls event handler but not update counter variable in state. Even I used the callback form of setState() like the following:
this.setState({ hasButtonBeenClicked: true }, () => {console.log("Clicked")});
the console.log("Clicked") was not reached in first click as well!
App.js
import React, { Component, useState } from "react";
import { Summary } from "./Summary";
import ReactDOM from "react-dom";
let names = ["Bob", "Alice", "Dora"]
function reverseNames() {
names.reverse();
ReactDOM.render(<App />, document.getElementById('root'));
}
function promoteName(name) {
names = [name, ...names.filter(val => val !== name)];
ReactDOM.render(<App />, document.getElementById('root'));
}
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0
}
}
incrementCounter = (increment) => this.setState({counter: this.state.counter + increment});
render() {
return (
<table className="table table-sm table-striped">
<thead>
<tr><th>#</th><th>Name</th><th>Letters</th></tr>
</thead>
<tbody>
{names.map((name, index) =>
<tr key={name}>
<Summary index={index} name={name}
reverseCallback={() => reverseNames()}
promoteCallback={() => promoteName(name)}
counter={this.state.counter}
incrementCallback={this.incrementCounter}
/>
</tr>
)}
</tbody>
</table>
)
}
}
Summary Component
import React, { Component } from "react";
import { SimpleButton } from "./SimpleButton";
export class Summary extends Component {
render() {
const props = this.props;
return (
<React.Fragment>
<td>{props.index + 1} </td>
<td>{props.name} </td>
<td>{props.name.length} </td>
<td>
<SimpleButton
className="btn btn-warning btn-sm m-1"
callback={() => props.reverseCallback()}
text={`Reverse (${props.name})`}
{...this.props}
/>
</td>
</React.Fragment>
)
}
}
SimpleButton
import React, { Component } from "react";
export class SimpleButton extends Component {
constructor(props) {
super(props);
this.state = {
hasButtonBeenClicked: false
}
}
handleClick = (e) => {
this.props.incrementCallback(3);
this.setState({ hasButtonBeenClicked: true });
this.props.callback();
console.log(e);
}
render() {
return (
<button onClick={(e) => this.handleClick(e)}
className={this.props.className}
disabled={this.props.disabled === "true"
|| this.props.disabled === true}>
{ this.props.text} { this.props.counter}
{ this.state.hasButtonBeenClicked &&
<div>Button Clicked!</div>
}
</button>
)
}
}
I resolved the problem by commenting out the line in App.js
function reverseNames() {
names.reverse();
// ReactDOM.render(<App />, document.getElementById('root'));
}
I thinks the line making app rerender before the actual state updated so I was behind the actual state 1 span. The first click is the initial state, the second click is the state after the first click .etc

react does not update DOM

import React, { Component } from "react";
import ReactDOM from "react-dom";
import "./index.css";
class App extends Component {
constructor(props) {
super(props);
this.state = {
text: "",
listItem: []
}
this.onChangeInput = this.onChangeInput.bind(this);
this.addToList = this.addToList.bind(this);
this.keyPress = this.keyPress.bind(this);
}
onChangeInput(event) {
this.setState({
text: event.target.value
});
}
addToList () {
let list = this.state.listItem;
list.push(this.state.text);
this.setState({
text: ""
});
this.setState({
listItem: list
});
}
deleteItem(event) {
console.log(event.target.remove());
}
keyPress (e) {
if (e.key === "Enter") {
this.addToList()
}
}
render() {
const listItem = this.state.listItem;
const list = listItem.map((val, i) =>
<li key={i.toString()} onClick={this.deleteItem}>
{val}
</li>
);
console.log(list);
return (
<div className="container">
<Input onChange={this.onChangeInput} value={this.state.text}
keyPress={this.keyPress}
/>
<Button addToList={this.addToList}/>
<ul>
{list}
</ul>
</div>
);
}
}
class Input extends Component {
render() {
return <input type="text" className="input" onChange={this.props.onChange}
onKeyPress={this.props.keyPress}
value={this.props.value}/>;
}
}
class Button extends Component {
render() {
return (
<button className="button" onClick={this.props.addToList}>
Add To List
</button>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
I'm very confused and couldn't find solution any where.
I'm new to react.
when I delete the list items, I delete them from DOM but is in state and I didn't delete it from state.
I put console.log(list) in render method and on every key press logs list in console
my question is why DOM does not re-render lists and output those where deleted from DOM and not from state?
and why it works for new list items and ignore those that deleted from DOM ?
react dosent pickup the update in the way you are doing it
deleteItem(event) {
console.log(event.target.remove());
}
although the item will be removed , but react dosent have any clue that happend, to notify react that the items has changed and it need to re-render, you need to call setState , then react calls the render method again,
deleteItem(e) {
const list= this.state.listItem;
list.pop() // remove the last element
this.setState({
list: list
});
}

Passing a function with a parameter to child component and sending return value back to parent to be stored in parent state in reactjs

Please forgive me, I am new to programming and JavaScript/React...
This is the question from my assignment:
Make a counter application using React and Node.js. The user must have the ability to click a button to increase, decrease, or reset a counter. The app must have the following components: Display, DecreaseCount , IncreaseCount, ResetCount. Pass the appropriate functions to be used and current counter value to each component.
I'm not sure what the point is of creating components for those simple operations. I also don't understand what will make those arithmetical components unique if I'm passing them both a function and a value to work on. But I am assuming the point of the assignment is to show that you can pass state to a child, work on it within the child, and then pass the worked-on result back to the parent to be stored in its state.
Here is the main page, Display.js.
For now I'm just trying to get the add functionality to work:
import React, { Component } from 'react';
import IncreaseCount from './IncreaseCount';
import DecreaseCount from './DecreaseCount';
import ResetCount from './ResetCount';
class Display extends Component {
constructor(props) {
super(props);
this.increment = this.increment.bind(this);
this.state = {
count: 0
};
}
increment = numToInc => {
this.setState({ count: numToInc++ });
};
decrement = numToDec => {
this.setState({ count: numToDec-- });
};
reset = numToReset => {
numToReset = 0;
this.setState({ count: numToReset });
};
render() {
return (
<div>
<h2>{this.state.count} </h2>
<IncreaseCount count={this.state.count} operation={this.increment} />
<DecreaseCount count={this.state.count} operation={this.decrement} />
<IncreaseCount count={this.state.count} operation={this.reset} />
</div>
);
}
}
export default Display;
And here is the IncreaseCount component class:
import React, { Component } from 'react';
class IncreaseCount extends Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
buttonClick = () => {
this.setState({ count: this.props.count }); // I am pretty sure this isn't necessary
this.props.operation(this.state.count);
};
render() {
return <button onClick={this.buttonClick}></button>;
}
}
export default IncreaseCount;
It is not throwing any errors but is not changing the value of either the Increase count or the Display count properties. I was expecting both to be changing in lockstep. My goal is to send the incremented value back to Display to be displayed. Is there a problem with the way I've written and passed my increment function?
You need to use this.props.count within the IncreaseCount
class IncreaseCount extends Component {
buttonClick = () => {
this.props.operation(this.props.count);
};
...
}
A full example might look something like this:
class Display extends Component {
state = {
count: 0
};
increment = numToInc => {
this.setState({ count: numToInc + 1 });
};
decrement = numToDec => {
this.setState({ count: numToDec - 1 });
};
reset = () => {
this.setState({ count: 0 });
};
render() {
return (
<div>
<h2>{this.state.count} </h2>
<Operation
name="+"
count={this.state.count}
operation={this.increment}
/>
<Operation
name="-"
count={this.state.count}
operation={this.decrement}
/>
<Operation
name="Reset"
count={this.state.count}
operation={this.reset}
/>
</div>
);
}
}
class Operation extends Component {
buttonClick = () => {
this.props.operation(this.props.count);
};
render() {
return <button onClick={this.buttonClick}>{this.props.name}</button>;
}
}
Note that you don't have to pass the counter value to each Operation and use a functional setState:
increment = () => {
this.setState(prev => ({ count: prev.count + 1 }));
};
Using a single component like <Operation /> is certainly how I'd do it. However, per the requirements of the OP, I'm adding this example that uses all 4 components specified.
import React, { Component } from 'react';
class IncreaseCount extends Component {
render(props) {
return <button onClick={this.props.action}>+</button>;
}
}
class DecreaseCount extends Component {
render(props) {
return <button onClick={this.props.action}>-</button>;
}
}
class ResetCount extends Component {
render(props) {
return <button onClick={this.props.action}>reset</button>;
}
}
class Display extends Component {
constructor(props) {
super(props);
this.state = { count: 0 };
this.increment = this.increment.bind(this);
this.decrement = this.decrement.bind(this);
this.reset = this.reset.bind(this);
}
increment() {
this.setState({ count: this.state.count + 1 });
}
decrement() {
if (this.state.count > 0) {
this.setState({ count: this.state.count - 1 });
}
}
reset() {
this.setState({ count: 0 });
}
render() {
return (
<div>
<h2>{this.state.count}</h2>
<DecreaseCount count={this.state.count} action={this.decrement} />
<IncreaseCount count={this.state.count} action={this.increment} />
<ResetCount count={this.state.count} action={this.reset} />
</div>
);
}
}
export default Display;
This version also prevents the counter from going below 0.

Child Component not updating after state changes

I am learning react, and I am making a simple ToDoApp. I set some todo data from a JSON file in the state of my App Component and use the values to populate a Child component. I wrote a method to be called each time the onChange event is fired on a checkbox element and flip the checkbox by updating the state. Thing is this code worked perfectly fine before, but it's not anymore. The state gets updated accordingly when I change the checkbox, but it doesn't update in the child element, I'd like to know why. Here's my code
App.js
import React from "react";
import TodoItem from "./TodoItem";
import toDoData from "./toDosData";
class App extends React.Component {
constructor() {
super();
this.state = {
toDoData: toDoData
};
this.handleOnChange = this.handleOnChange.bind(this);
}
handleOnChange(key)
{
this.setState(prevState => {
let newState = prevState.toDoData.map(currentData => {
if(currentData.id === key)
currentData.completed = !currentData.completed;
return currentData;
});
return {toDoData: newState};
});
}
render() {
let toDoComponents = this.state.toDoData.map(toDoDatum =>
<TodoItem key={toDoDatum.id} details={{
key: toDoDatum.id,
text: toDoDatum.text,
completed: toDoDatum.completed,
onChange: this.handleOnChange
}} />);
return (
<div>
{toDoComponents}
</div>
);
}
}
export default App;
TodoItem.js
import React from "react";
class TodoItem extends React.Component {
properties = this.props.details;
render() {
return (
<div>
<input type="checkbox" checked={this.properties.completed}
onChange={() => this.properties.onChange(this.properties.key)}
/>
<span>{this.properties.text}</span>
</div>
)
}
}
export default TodoItem;
Thanks in advance.
Why do you need to assign your details prop to properties in your class? If you do that properties does not reflect the prop changes and your child component can't see the updates. Just use the props as it is:
render() {
const { details } = this.props;
return (
<div>
<input
type="checkbox"
checked={details.completed}
onChange={() => details.onChange(details.key)}
/>
<span>{details.text}</span>
</div>
);
}
}
Also, since you don't use any state or lifecycle method in TodoItem component, it can be a functional component as well.
const TodoItem = ({ details }) => (
<div>
<input
type="checkbox"
checked={details.completed}
onChange={() => details.onChange(details.key)}
/>
<span>{details.text}</span>
</div>
);
One more thing, why don't you pass the todo itself to TodoItem directly?
<TodoItem
key={toDoDatum.id}
todo={toDoDatum}
onChange={this.handleOnChange}
/>
and
const TodoItem = ({ todo, onChange }) => (
<div>
<input
type="checkbox"
checked={todo.completed}
onChange={() => onChange(todo.id)}
/>
<span>{todo.text}</span>
</div>
);
Isn't this more readable?
Update after comment
const toDoData = [
{ id: 1, text: "foo", completed: false },
{ id: 2, text: "bar", completed: false },
{ id: 3, text: "baz", completed: false }
];
const TodoItem = ({ todo, onChange }) => (
<div>
<input
type="checkbox"
checked={todo.completed}
onChange={() => onChange(todo.id)}
/>
<span>{todo.text}</span>
</div>
);
class App extends React.Component {
constructor() {
super();
this.state = {
toDoData: toDoData
};
this.handleOnChange = this.handleOnChange.bind(this);
}
handleOnChange(key) {
this.setState(prevState => {
let newState = prevState.toDoData.map(currentData => {
if (currentData.id === key)
currentData.completed = !currentData.completed;
return currentData;
});
return { toDoData: newState };
});
}
render() {
let toDoComponents = this.state.toDoData.map(toDoDatum => (
<TodoItem
key={toDoDatum.id}
todo={toDoDatum}
onChange={this.handleOnChange}
/>
));
return <div>{toDoComponents}</div>;
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root" />

Why doesn't react component render when trying to render it in another components click function

I'm trying to render buttons to a page which when clicked render the hard-coded weather data to the page depending on the day that was clicked. The click function works fine and the buttons are rendered just as I expect them to, but when a button is clicked the Day component doesn't render.
I don't understand what I'm doing wrong since my code reaches the console.log in the click handler function. Then the handler function should render the component but for some reason it does not.
Here is my code:
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import myData from "./weather.json";
class Day extends React.Component {
constructor(props) {
super(props);
this.state = {
description: null
};
}
render() {
console.log("at last"); //this isn't reached
return (
<div className="dayWeather">
<div className="dayWeather">Humidity {this.props.humidity}</div>
<div className="dayWeather">Temperature {this.props.temperature}</div>
</div>
);
}
}
class DayRow extends React.Component {
constructor(props) {
super(props);
this.state = {
days: Array(7).fill(null)
};
this.handler = this.handler.bind(this);
this.renderDay = this.renderDay.bind(this);
}
handler(day) {
let daysWeather = myData[day];
console.log("now we've reached this far"); //this console log is reached when a button is clicked.
return (
<Day
humidity={daysWeather.humidity}
temperature={daysWeather.temperature}
/>
);
}
renderDay(day) {
return (
<div>
<button
className="day"
onClick={() => {
this.handler(day);
}}
>
{day}
</button>
</div>
);
}
render() {
return (
<div>
<div className="day-row">
{this.renderDay("Monday")}
{this.renderDay("Tuesday")}
{this.renderDay("Wednesday")}
{this.renderDay("Thursday")}
{this.renderDay("Friday")}
{this.renderDay("Saturday")}
{this.renderDay("Sunday")}
</div>
</div>
);
}
}
class Weather extends React.Component {
render() {
return (
<div className="weather">
<div className="weather-panel">
<DayRow />
</div>
<div className="day" />
</div>
);
}
}
// ========================================
ReactDOM.render(<Weather />, document.getElementById("root"));
Don't return UI elements in click handler as it won't render. Just set a flag inside handler and use it to display the Day component inside your render function.
class DayRow extends React.Component {
constructor(props) {
super(props);
this.state = {
days: Array(7).fill(null),
showDay: false
};
this.handler = this.handler.bind(this);
this.renderDay = this.renderDay.bind(this);
}
handler() {
this.setState({
showDay: true,
});
}
renderDayComponent(day) {
let daysWeather = myData[day];
console.log("now we've reached this far"); //this console log is reached when a button is clicked.
return (
<Day
humidity={daysWeather.humidity}
temperature={daysWeather.temperature}
/>
);
}
renderDay(day) {
return (
<div>
<button
className="day"
onClick={() => {
this.handler();
}}
>
{day}
</button>
{this.state.showDay && this.renderDayComponent(day)}
</div>
);
}
render() {
return (
<div>
<div className="day-row">
{this.renderDay("Monday")}
{this.renderDay("Tuesday")}
{this.renderDay("Wednesday")}
{this.renderDay("Thursday")}
{this.renderDay("Friday")}
{this.renderDay("Saturday")}
{this.renderDay("Sunday")}
</div>
</div>
);
}
}
My guess is that you are trying to render the day in the Weather compoment? To do this, you must keep some sort of state so that React knows what to render. Whenever you change the state, React will call render to re-render your compoment.
Therefore, since the state of wether or not a day is to be shown is local to the Weather component, you need to store the state there:
class Weather extends React.Component {
constructor(props) {
super(props)
this.state = { activeDay: undefined }
}
render() {
// Store the current dayData in a variable if an activeDay is chosen, it will be falsy if no day has been chosen.
const dayData = this.state.activeDay && myData[this.state.activeDay]
return (
<div className="weather">
<div className="weather-panel">
// DayRow will inform Weather when a day has been selected, i.e., clicked
<DayRow onSelected={day => this.setState({ activeDay: day })} />
</div>
<div className="day">
// This is where you are rendering the day, only if a day
// is active. I.e., dayData is truthy
{ dayData && <Day humitidy={dayData.humitidy} temperature={dayData.temperature} /> }
</div>
</div>
);
}
}
Your DayRow would simple communicate with Weather by saying which day is selected.
class DayRow extends React.Component {
constructor(props) {
super(props);
this.renderDay = this.renderDay.bind(this)
}
renderDay(day) {
return (
<div>
<button
className="day"
onClick={() => {
this.props.onSelected(day);
}}
>
{day}
</button>
</div>
);
}
render() {
return (
<div>
<div className="day-row">
{this.renderDay("Monday")}
{this.renderDay("Tuesday")}
{this.renderDay("Wednesday")}
{this.renderDay("Thursday")}
{this.renderDay("Friday")}
{this.renderDay("Saturday")}
{this.renderDay("Sunday")}
</div>
</div>
);
}
}
Returning JSX from your event handler will not render it. You have to do all rendering in your component's render method.
You could instead have an additional object in your state that keep track of if the day has been clicked or not, and use that state in your rendering.
class DayRow extends React.Component {
constructor(props) {
super(props);
this.state = {
days: Array(7).fill(null),
showDays: {}
};
this.handler = this.handler.bind(this);
this.renderDay = this.renderDay.bind(this);
}
handler(day) {
this.setState(previousState => {
const showDays = { ...previousState.showDays };
showDays[day] = !showDays[day];
return { showDays };
});
}
renderDay(day) {
let daysWeather = myData[day];
return (
<div>
<button
className="day"
onClick={() => {
this.handler(day);
}}
>
{day}
</button>
{this.state.showDays[day] && (
<Day
humidity={daysWeather.humidity}
temperature={daysWeather.temperature}
/>
)}
</div>
);
}
// ...
}
The returned component from handler function is being passed to onClick event. Its not getting into the DOM tree.
You can change the code as shown below.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import myData from './weather.json';
const myData =
class Day extends React.Component {
constructor(props) {
super(props);
this.state = {
description: null
};
}
render() {
console.log('at last'); //this isn't reached
return (
<div className="dayWeather">
<div className="dayWeather">Humidity {this.props.humidity}</div>
<div className="dayWeather">Temperature {this.props.temperature}</div>
</div>
);
}
}
class DayRow extends React.Component {
constructor(props) {
super(props);
this.state = {
days: Array(7).fill(null)
};
this.handler = this.handler.bind(this);
this.renderDay = this.renderDay.bind(this);
}
handler(day) {
let daysWeather = myData[day];
console.log('now we\'ve reached this far'); //this console log is reached when a button is clicked.
this.props.handler(daysWeather);
}
renderDay(day) {
return (
<div>
<button
className="day"
onClick={() => {
this.handler(day);
}}
>
{day}
</button>
</div>
);
}
render() {
return (
<div>
<div className="day-row">
{this.renderDay('Monday')}
{this.renderDay('Tuesday')}
{this.renderDay('Wednesday')}
{this.renderDay('Thursday')}
{this.renderDay('Friday')}
{this.renderDay('Saturday')}
{this.renderDay('Sunday')}
</div>
</div>
);
}
}
class Weather extends React.Component {
constructor(props){
super(props);
this.state = {
selectedDay: {}
};
this.handler = this.handler.bind(this);
}
handler(day){
this.setState({
selectedDay: day
});
}
render() {
let day = null;
if(Object.keys(this.state.selectedDay) > 0){
day = <Day
humidity={selectedDay.humidity}
temperature={selectedDay.temperature}
/>;
}
return (
<div className="weather">
<div className="weather-panel">
<DayRow onDayChange={this.handler}/>
</div>
<div className="day">
{day}
</div>
</div>
);
}
}
// ========================================
ReactDOM.render(<Weather />, document.getElementById('root'));

Categories

Resources