I have a simple countdown component where a user inputs two times and it counts down the seconds between them. The start, stop, and reset work. Except, when I reset the countdown and input two new times (without refreshing the page), I am hit with this error:
TypeError: _this3.start is not a function
> 108 | <button onClick={(e) => this.start()}>Start</button>
Below is my code:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(){
super();
this.start = this.start.bind(this);
this.toTimestamp = this.toTimestamp.bind(this);
this.getDifference = this.getDifference.bind(this);
this.state = {
input1: '',
input2: '',
countdown: null
}
}
input1ContentChange(e){
const text = e.target.value;
this.setState(()=>{
return {input1: text};
})
}
input2ContentChange(e){
const text = e.target.value;
this.setState(()=>{
return {input2: text};
})
}
toTimestamp(input){
let time = input.split(':');
let seconds = ((+time[0]) * 60 * 60) + ((+time[1]) * 60) + (+time[2]);
return seconds;
}
getDifference(input1, input2){
let difference = (this.toTimestamp(input2))- (this.toTimestamp(input1));
if(this.toTimestamp(input2) < this.toTimestamp(input1)){
alert("please input a later time in Time 2");
}
this.setState({
countdown: difference
})
}
start() {
if(this.state.input1 === '' && this.state.input2 === ''){
alert('please choose 2 times');
}
this.getDifference(this.state.input1, this.state.input2);
this.start = setInterval((e) => {
this.setState((prevState) => {
return {countdown: prevState.countdown - 1};
});
if(this.state.countdown <= 0){
clearInterval(this.start);
}
}, 1000);
}
stop(){
clearInterval(this.start);
}
reset(){
clearInterval(this.start);
this.setState((prevState) => {
return {countdown: null, input1: '', input2:''}
})
}
render() {
return (
<div className="App">
<h1>Countdown Timer</h1>
<p>Please choose two different times below</p>
<div className="input1">
<label>
Time 1:
<input type="time"
step="1"
min= "12:00"
max= "18:00"
value={this.state.input1}
onChange={(e)=> this.input1ContentChange(e)}/>
</label>
</div>
<div className="input2">
<label>
Time 2:
<input type="time"
step="1"
min="12:00"
max="18:00"
value={this.state.input2}
onChange={(e)=> this.input2ContentChange(e)}/>
</label>
</div>
<button onClick={(e) => this.start()}>Start</button>
<button onClick={(e) => this.stop()}>Stop</button>
<button onClick={(e) => this.reset()}>Reset</button>
<h3>{this.state.countdown}</h3>
</div>
);
}
}
export default App;
The error is happening at the start function for restarting the countdown. When I check in my chrome tools using the React extension, the state is managed fine. It seems "this" is getting lost.
You're modifying your class function. When your app loads, your class has a start method, but inside that method you do:
this.start = setInterval(...)
setInterval does not return a function, but an id that you can use later to clear the interval. Even if it did return a function, you probably don't want to be modifying your class methods at runtime.
I would suggest using another variable name:
this.intervalId = setInterval(...)
Related
this is my first project using React.js, I want to filter the restaurants cards using checkbox when it it check it show only the restaurants cards with these filters or types true such as the music and WIFI. The problems are it show the default cards perfectly but after I checked the checkbox it's change the all type or filters values into false such as Music and WIFI instead of create or map only the cards that false. In addition, it will not create the default cards after double check, can you please help me
The code:
import React, { Component } from 'react';
import axios from 'axios';
import App from "../App";
import Cards from "../Card";
function CreateCards(resturants) {
//Handel the Music, Wifi, Partition (to transfer it from bolean form into string)
if (resturants.Music == true){
resturants.Music = "Music";
}else{
resturants.Music = "No Music";
}
if (resturants.Wifi == true){
resturants.Wifi = "Wifi";
}else{
resturants.Wifi = "No Wifi";
}
if (resturants.Partition == true){
resturants.Partition = "Partition";
}else{
resturants.Partition = "No Partition";
}
return(
<Cards
key={resturants._id} // done
theCardId={resturants._id} // done
placeName={resturants.Name} // done
stars={resturants.Rating} // done
PRating={resturants.PRating} //until filters
music= {resturants.Music} // done
img={resturants.icon} // need uploads file
status={Status(resturants.OpenTime, resturants.CloseTime)} // done
descreption={resturants.Description} // done
wifi={resturants.Wifi} // done
partition={resturants.Partition} // done
/>
);
}
// Check if the place is open or closed depending on the work hours
function Status (Open, Close){
const date = new Date();
var hours = date.getHours();
const red = 'red';
const green = 'green';
if ((Open <= hours) && (hours < Close)){
// console.log("Open");
return "Open";
}else{
// console.log("Close");
return "Close";
}
}
export default class Resturants extends Component {
//constructor elemnts in login
constructor(props){
super(props);
//intialy no data enterd
this.state = {
resturants: [],
filter: ""
}
this.Filtering = this.Filtering.bind(this);
}
componentDidMount(){
//Get Resturants data
axios.get('http://localhost:3000/places')
.then(resp => {
console.log(resp)
this.setState({
resturants: resp.data
})
})
}
Filtering(e){
// this.setState({filter:e.target.value});
e.preventDefault();
this.state.resturants.filter(Type => {
// console.log(Type.Music === true);
})
}
render(){
return(
<div className="flexthem">
<div className="Filters">
<h4>Filters</h4>
<input className="Checkbox" type="checkbox" id="Type1" value="" onClick={this.Filtering}></input>
</div>
<div className="general-card">
{this.state.resturants.map(CreateCards)}
</div>
</div>
);
}
}
a bit of advice.
use "==="
use function componented.
the hook "useState" is a lot simpler than class component state.
restraunt.music = "music"
is a string.
and
restaurant.music = true
is a boolean.
if you set a variable as a string and try to check if it is false or true after. It will return undefined. If it is an empty string, it will return false.
if (resturants.Music == true){
resturants.Music = "Music";
}else{
resturants.Music = "No Music";
}
in react when you set or change the state, then it refreshes. If you are changing the state with this, you are going to put it through he if statement again. It will return undefined and then not change the checkboxes from their default value.
I've made a dark mode function. There are using checkbox and getting current hour to change this mode.
When I write a traditional If statement it cause an error of render limits (like stack overflow).
Here's my code:
const curTime = new Date();
let t = curTime.getHours().valueOf();
const [isNight, setDarkMode] = useState(false);
if (t >= 6 && t <= 19) {
document.documentElement.classList.remove("dark-mode");
setDarkMode(false);
} else {
document.documentElement.classList.add("dark-mode");
setDarkMode(true);
}
const handleChecked = e => {
if (e.target.checked === true) {
document.documentElement.classList.add("dark-mode");
setDarkMode(true);
} else {
document.documentElement.classList.remove("dark-mode");
setDarkMode(false);
}
};
<input id="myCheck" type="checkbox" onChange={handleChecked} defaultChecked={isNight} />
I can't find a solution from an Internet.
I think the answer would be using an UseEffect method but I don't really know how to use it in my situation. Or this checkbox could cause this infinite changing values?
Can you help me with this?
In React-style I would do it like this:
import React, { useState, useEffect } from "react";
const App = () => {
const [isNight, setDarkMode] = useState(false);
useEffect(() => {
let t = new Date().getHours().valueOf();
setDarkMode(t < 6 || t > 19);
}, []);
return (
<div className={isNight ? "dark-mode" : "light-mode"}>
<div>{isNight ? "dark-mode" : "light-mode"}</div>
<input
type="checkbox"
onChange={(e) => setDarkMode(e.target.checked)}
checked={isNight}
name="darkModeInput"
/>
<label htmlFor="darkModeInput">Switch dark mode</label>
</div>
);
};
export default App;
I have a table that display employees information. I want to add a search to display all employees between spicfic number that i put in min textbox and max textbox. Here is my table code.
constructor() {
super();
this.state = {
employees: []
};
}
componentDidMount() {
this.employeesTracker = Tracker.autorun(() => {
const employees = Employees.find().fetch();
this.setState({ employees });
});
}
renderEmployeesListItems() {
return this.state.employees.map(employee => {
return (
<tr key={employee._id}>
<td>{employee.name}</td>
<td>{employee.email}</td>
<td>{employee.age}</td>
<td>{employee.gender}</td>
<td>{employee.city}</td>
</tr>
);
});
}
here where I render my app:
render() {
return (
<div>
<input type="text" id="min" name="min"/>
<input type="text" id="max" name="max"/>
<button onClick={this.ageFilter.bind(this)}>filter</button>
<table id="myTable">
<tbody>
{this.renderEmployeesListItems()}
</tbody>
</table>
</div>
);
}
instead of || (OR) you need to use && (AND) operator
...
var age = parseInt(td.innerHTML)
if (age > filterMin && age < filterMax) {
...
} else {
...
}
Also don't why are you accessing data through document.. This is not the way to do document update in react.
You could have this filtered Data separately as a state variable, filteredEmployee or such. Then the filter function will be,
ageFilter = () => {
const { employees, min, max } = this.state;
const filteredEmployees = employees.filter(employee => min < employee.age && employee.age < max);
this.setState({ filteredEmployees })
}
you'll need to add logic to clear the filter too, if you need that. use this filteredEmployees in renderEmployeesListItems function instead of employees
Below is my code and I got a feeling I am really doing this wrong. I am new to react and I been spending many hours trying to figure this out with no luck.
I am trying to get users to input values of age, gender, height, weight etc..and then make the BMR box update with a value.
What i have so far is when the user clicks "Calculate for BMR" the onClick function spits out the correct result, but I have no clue how to get the value to appear in the "BMR input box" without any sort of refreshing.
Any help would be appreciated. Thanks
class Calculator extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.handleGenderChange = this.handleGenderChange.bind(this);
this.handleAgeChanged = this.handleAgeChanged.bind(this);
this.handleWeightChanged = this.handleWeightChanged.bind(this);
this.handleFeetChanged= this.handleFeetChanged.bind(this);
this.handleInchesChanged=this.handleInchesChanged.bind(this);
}
handleGenderChange = (event) => {
this.setState({Gender: event.target.value});
}
handleAgeChanged = (event) => {
this.setState({Age: event.target.value});
}
handleWeightChanged = (event) => {
this.setState({Weight: event.target.value});
}
handleHeightChanged = (event) => {
this.setState({Height: event.target.value});
}
handleFeetChanged = (event) => {
this.setState({Feet: event.target.value});
}
handleInchesChanged = (event) => {
this.setState({Inches: event.target.value});
}
onClick= (event) => {
event.preventDefault();
console.log(this.state);
const totalHeight = Number(this.state.Feet) * 12 + Number(this.state.Inches);
if(this.state.Gender == 'Male'){
var BMR = 66 + (6.23 * Number(this.state.Weight)) + (12.7 * totalHeight) - (6.8 * Number(this.state.Age));
console.log(BMR);
}
if(this.state.Gender == 'female'){
var BMR = 655 + (4.35 * Number(this.state.weight)) + (4.7 * totalHeight) - (4.7 * Number(this.state.age));
console.log(BMR);
}
}
render() {
return (
<div>
<Container>
<form>
<h3>Calories/TDEE Calculator</h3>
<div className="form-group">
<select className="form-control" value={this.state.Gender} onChange={this.handleGenderChange}>
<option disabled selected value> -- Gender-- </option>
<option value="Male">Male</option>
<option value="Female">Female</option>
</select>
</div>
<div className="form-group">
<label htmlFor="Age">Age</label>
<input className="form-control"
onChange={this.handleAgeChanged}
type="input"
id="Age"
name="Age"
placeholder="Enter an Age"
value={this.state.Age}
/>
</div>
<div className="form-group">
<label htmlFor="Weight">Weight (lbs)</label>
<input className="form-control"
onChange={this.handleWeightChanged}
type="input"
id="Weight"
name="Weight"
placeholder="Enter Weight"
value={this.state.Weight}
/>
</div>
<div className="form-group">
<label>"Height (Ft/In)"</label>
<input type="input"
name="Feet"
placeholder="Feet"
onChange={this.handleFeetChanged}
value={this.state.Feet}
/>
<input type="input"
name="Inches"
placeholder="Inches"
onChange={this.handleInchesChanged}
value={this.state.Inches}
/>
</div>
<div className="form-group">
<label>BMR</label>
<input className="form-control"
id="BMR"
name="BMR"
value= ""
/>
</div>
<button className="btn btn-lg btn-primary btn-block" onClick={this.onClick.bind(this)}>Click for BMR</button> <br />
</form>
</Container>
</div>
);
}
}
export default Calculator;
EDIT:
Thanks everyone for taking your time to help, it worked :D. I learned from all your replies.
You don't have anything that renders this.state.BMR so it's not too surprising you don't see it anywhere. Some advice: don't use nonbreaking spaces and <br>: that's what CSS is for. Also, don't use bind, use arrow notation to preserve this, there is no reason to use all these bind calls in your constructor.
And then for the actual question: you need to actually render something, so have an element that either shows the button, or shows the BMR value, based on whether you computed it:
class Calculator extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
handleGenderChange(evt) {
// you really should validate this
this.setState({Gender: event.target.value});
}
...
render() {
return <div>
...
<inputelement onChange={evt => this.handleInchesChanged(evt) } />
...
{ this.showButtonOrResult() }
</div>;
}
showButtonOrResult() {
// if the button wasn't clicked yet, then `BMR` will not yet be a state value
if (!this.state.BMR) {
return <button className="..." onClick={evt => this.onClick(evt)>Click for BMR</button>
}
// if it IS a state value, just render it
return <div>Your BMR is: { this.state.BMR }</div>;
}
}
So when your button is clicked, you do what you do, calculate BMR, then setState that, and render() automatically gets called again. Now there is a value to show, and instead of the button, it'll show a div with the result.
Also note that we are absolutely not using bind(this) in the constructor, because that's ridiculous. Properly handle your events with an arrow function so that you get the event, and then pass the event to the correct function, with normal this context.
You need to label your state properties correctly. Your female calculation is going to spit out NaN because you're using {this.state.weight} when you're setting it as 'Weight'
Initialize your state
this.state = {
bmr: ''
};
Set the value of your input
<div className="form-group">
<label>BMR</label>
<input className="form-control"
id="BMR"
name="BMR"
value={this.state.bmr}
/>
</div>
Set the state in your onclick function
onClick = (event) => {
event.preventDefault();
let BMR;
const totalHeight = Number(this.state.Feet) * 12 + Number(this.state.Inches);
if (this.state.Gender === 'Male') {
BMR = 66 + (6.23 * Number(this.state.Weight)) + (12.7 * totalHeight) - (6.8 * Number(this.state.Age));
this.setState({ bmr: BMR });
} else if (this.state.Gender === 'Female') {
BMR = 655 + (4.35 * Number(this.state.Weight)) + (4.7 * totalHeight) - (4.7 * Number(this.state.Age));
this.setState({bmr: BMR});
}
}
I have a react-bootstrap React Class, where the createList function gets called on every key input to either of the form inputs (workDone, or hoursAndMinutes). I'm new to reactjs, and perhaps this is normal behavior, but it seems to me that it isn't, and hence I'm doing something wrong.
var SubjectBox = React.createClass({
getInitialState(){
return({
totalHoursAndMinute:0,
subject:'',
workDone:'',
hoursAndMinutes:'',
})
},
dropDownSelected:function(e){
this.setState({subject:e.target.value})
},
handleChangeToWorkDone(){
let s = this.refs.workDone.getValue();
console.log(s);
this.setState({
workDone: s
});
},
validateWorkDone:function(){
let length = this.state.workDone.length;
if (length >= 10) return 'success';
else if (length > 5) return 'warning';
else if (length > 0) return 'error';
},
validateHoursAndMinutes(){
let hm = this.state.hoursAndMinutes.split(':');
if (hm.length === 2){
return 'success';
}else{
return 'error';
}
},
handleChangeToHoursMinute(){
var minutes =0;
let s =this.refs.hoursAndMinutes.getValue();
let hm = s.split(':');
if (hm.length===2){
var h = parseInt(hm[0].trim());
var m = parseInt(hm[1].trim());
if (!m.isNaN){
var minutes = h*60+m;
}
}
this.setState({
hoursAndMinutes: s,
totalMinutes:minutes
});
},
createList: function(){
console.log("create list function.");
var list=[];
for (var i = 0; i < this.props.subjects.length;i++){
list.push(<option key={i} value={this.props.subjects[i].subject}>{this.props.subjects[i].subject}</option>)
}
return list;
},
handleSubmit: function(e){
e.preventDefault();
console.log(this.state.workDone);
console.log(this.state.subject);
},
render(){
return(
<form onSubmit={this.handleSubmit}>
<Input ref="subjectList" type="select" label="Subject" onChange={this.dropDownSelected}>
{this.createList()}
</Input>
<Input ref="workDone"
type="text"
value={this.state.workDone}
placeholder="What did you do?"
label="What did you do" help="Input is 10 or more characters."
bsStyle={this.validateWorkDone()} hasFeedback
groupClassName="group-class" labelClassName="label-class"
onChange={this.handleChangeToWorkDone} />
<Input ref="hoursAndMinutes"
type="text" value={this.state.hoursAndMinutes} placeholder="HH:MM?"
label="How long did you do it?" help="Input in hours:minutes, example 1:5 = an hour and five minutes."
bsStyle={this.validateHoursAndMinutes()} hasFeedback
groupClassName="group-class"
labelClassName="label-class" onChange={this.handleChangeToHoursMinute} />
<Button type="submit" bsStyle="success">Submit</Button>
</form>
)
}
});
It happens because you are using in handleChangeToWorkDone and handleChangeToWorkDone setState which calls re-render
setState() will always trigger a re-render unless conditional
rendering logic is implemented in shouldComponentUpdate(). If mutable
objects are being used and the logic cannot be implemented in
shouldComponentUpdate(), calling setState() only when the new state
differs from the previous state will avoid unnecessary re-renders.
In React Js, the HTML/DOM is always just a representation on the State of your React Component.
Whenever there is a onChange or onBlur or any event, if the State of the React Component is changed(using setState func), the ReactJs component is re-rendered(using Render func).
Only when the State of the React Js component changes, your UI can be updated with the value you key in. Hence, It is the expected behaviour in React Js.