Using Conditions cause React render limit error - javascript

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;

Related

how to validate user inputs in react using setState hook

I have a form. In the form I getting brandName, supplierName, and date of expiry from the user.
I am usinf TextField from mui library and submit button.
I want to disable submit button on empty form fields and enable it when user filled all the inputs
here is my code for declaring useStates
const[brandName, setBrandName] = useState("");
const[supplierName, setSupplierName] = useState("");
const[expiryDate, setExpiryDate] = useState(null);
const[brandNameError, setBrandNameError] = useState(false);
const[supplierNameError, setSupplierNameError] = useState(false);
const[expiryDateError, setExpiryDateError] = useState(false);
const[submitButton, setSubmitButton] = useState(true);
and here is all function which i used to validate my inputs
// checking brandName Error
const brandNameValidateOnBlur = ()=>{
if(brandName === ""){
setBrandNameError(true);
}
}
// checking supplier name error
const supplierNameValidateOnBlur = ()=>{
if(supplierName === ""){
setSupplierNameError(true);
}
}
// checking expiry date
const expiryDateValidateOnBlur = ()=>{
if(expiryDate === ""){
setExpiryDateError(true);
}
}
// now checking all inputs again if all inputs are good then
// button should be enabled
const checkAllInputs = ()=>{
if(brandName !== "" && supplierName !== "" && expiryDate !== ""){
setSubmitButton(false);
}else{
setSubmitButton(true);
}
}
and here is the rest of code
<TextField fullWidth id="productName" label="Product Name"
value={brandName.toLowerCase()} variant="outlined"
onChange={(data)=>{setBrandName(data.target.value.toUpperCase());checkAllInputs()}}
onBlur={brandNameValidateOnBlur}
onFocus={()=>setBrandNameError(false)}
error={brandNameError}
helperText = {brandNameError ? "Enter Brand Name" : ""}
/>
<TextField id="supplierName"
label="Supplier Name"
value={supplierName.toLowerCase()} variant="outlined"
onBlur={supplierNameValidateOnBlur}
onFocus={()=>setSupplierNameError(false)}
error={supplierNameError}
helperText={supplierNameError ? "Enter Supplier Name " : ""}
onChange={(data)=>{setSupplierName(data.target.value.toUpperCase());checkAllInputs()}} />
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DesktopDatePicker
label="Date Expiry"
inputFormat="MM/DD/YYYY"
value={expiryDate}
onBlur={expiryDateValidateOnBlur}
onFocus={()=>setExpiryDateError(false)}
error={expiryDateError}
helperText={expiryDateError ? "Enter Expiry Date ": ""}
onChange={(selectedDate)=>
{setExpiryDate(selectedDate.format("MM/DD/YYYY"));checkAllInputs()}}
renderInput={(params) => <TextField {...params} />}
/>
</LocalizationProvider>
<Button type='button' variant='contained' id="submitButton" disabled={submitButton}
style={{backgroundColor:'orangered'}} onClick={addData}>Add Data</Button>
now problem is when i enter a single word my useState is updated but my checkAllInputs not working as i want to be like if i enter a word my checkAllInputs method runs before setting of state as i enter second entry then it works like I want to be so i did not know what i am doing wrong
I TRY useEffect hook like that
useEffect(()=>{
checkAllInputs();
},[brandName, supplierName, expiryDate]);
its working fine as i accepted but I read that it should be a expensive to use useEffect
and an other approach which i use
const checkAllInputsWithDom = ()=>{
let brandNameEntry = document.getElementById("brandName").value;
let supplierNameEntry = document.getElementById("supplierName").value;
let expiryDateEntry = document.getElementById("expiryDate").value;
if(brandNameEntry !=="" && supplierNameEntry !== "" && expiryDateEntry !== ""){
setSubmitButton(false);
}else{
setSubmitButton(true);
}
}
it is working out of box but I thing in react this is against react principles to direct manipulate
dome elements can I do it with out using useeffect hook like only with my metohd and one more thing
this did not set expiryDate error in date field
As long as you're keeping the input values in state, you don't need another variable storing the button state as well. You can calculate it at render time. (Read more about avoiding redundant state here.)
You could do something like this:
const Component = () => {
const[brandName, setBrandName] = useState("");
const[supplierName, setSupplierName] = useState("");
const[expiryDate, setExpiryDate] = useState("");
const disabled = brandName.length === 0 && supplierName.length === 0 && expiryDate.length === 0
return (
<>
<input value={brandName} onChange={(e) => setBrandName(e.target.value)}/>
<input value={supplierName} onChange={(e) => setSupplierName(e.target.value)}/>
<input value={expiryDate} onChange={(e) => setExpiryDate(e.target.value)}/>
<button disabled={disabled}>Button</button>
</>
)
}
here is the link for codesandbox I have a suggestion for you to refactor this 6 use States with just one useState as an object having all the values like the following:
const initialValues = {
brandName: "",
supplierName: "",
expiryDate: "",
brandNameError: "",
supplierNameError: "",
expiryDateError: ""
};
and then update it with a generic handleInputChange like below:
const handleInputChange = (e) => {
//const name = e.target.name
//const value = e.target.value
const { name, value } = e.target;
setValues({
...values,
[name]: value
});
let isEmpty = Object.values(values).some((x) => x === "");
console.log(isEmpty);
setIsDisabled(isEmpty);
};
FOR YOUR SOLUTION
You will have to just check all the values and just create a boolean with isDisabled so when all of those values are empty then it will only set to false.
I have created a code snippet for you, it needs some improvement but it will serve your purpose. Link already at the top and here as well
as suggested by Eduardo Motta de Moraes
I used this for making my button enabled after all inputs validate
const disabled= brandName.length === 0 || supplierName.length === 0 || expiryDate.length === 0;
this solve my problem
thanks for Eduardo Motta de Moraes for this

can't read else part

I want my divs to check/uncheck.
But, else part can't read my code (undefined)
Plz help me...
(First time click is fine
second time click is undefined)
import React, { useState } from "react";
import "./Blocking.css";
const Blocking = () => {
const [checked, setChecked] = useState([false, false, false, false, false]);
const onCheck = (e) => {
e.preventDefault();
console.log(e.target.getAttribute("name"));
if (checked[e.currentTarget.getAttribute("name")] === false) {
e.target.style.background = "aquamarine";
setChecked(!checked[e.target.getAttribute("name")]);
console.log("if " + checked[e.target.getAttribute("name")]);
} else {
e.target.style.background = "white";
setChecked(!checked[e.target.getAttribute("name")]);
console.log("else " + checked[e.target.getAttribute("name")]);
}
};
return (
<div className="container">
<div className="items" name="0" onClick={onCheck}>
1
</div>
<div className="items" name="1" onClick={onCheck}>
2
</div>
<div className="items" name="2" onClick={onCheck}>
3
</div>
<div className="items" name="3" onClick={onCheck}>
4
</div>
<div className="items" name="4" onClick={onCheck}>
5
</div>
</div>
);
};
export default Blocking;
Below code block is console.log in chrome)
[HMR] Waiting for update signal from WDS...
Blocking.jsx:9 2
Blocking.jsx:14 if false
Blocking.jsx:9 2
Blocking.jsx:18 else undefined
1st i click any blocks then, print fine.
2nd i click any blocks then, print undefined.
The error is in:
setChecked(!checked[e.target.getAttribute("name")]);
The "checked" array is being replaced by a boolean.
You could do it as:
let newChecked = [...checked]; //Destructure
let index = e.currentTarget.getAttribute("name") //Get index
newChecked[index] = !checked[index] //Toggle check
setChecked(newChecked)
Issues
You are mutating your checked state array. setChecked(!checked[e.target.getAttribute("name")]); mutates the state to a single boolean, so subsequent checked[e.currentTarget.getAttribute("name")] conditional tests will likely always be falsey.
You are also directly manipulating the DOM. This is an anti-pattern in React.
You need to shallow copy the entire array and update the specific index. Use the state value to derive the background color.
const onCheck = (e) => {
e.preventDefault();
const index = Number(e.currentTarget.getAttribute("name"));
setChecked(checked => checked.map((val, i) => i === index ? !val : val));
};
return (
<div className="container">
<div
className="items"
style={{ background: checked[0] ? 'white' : 'aquamarine' }}
name="0"
onClick={onCheck}
>
1
</div>
...etc
</div>
);

Filtring cards in React using checkbox onChange

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.

Thousand separator input with React Hooks

I would like to add on a input a thousand separator using React Hooks but I'm not sure how. I have tried the below code so far and is not working.
Can you please point out what could be the issue and how can I implement this?
Thank you.
const MainComponent = () => {
const [value, setValue] = useState(0);
const numberWithComma = () => {
return (+value).toFixed(2).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,')
}
return (
<div>
<input
type="number"
onChange={numberWithComma}
placeholder="0"
/>
</div>
);
}
You want a controlled form input, so one which gets given a value, and an onInput handler.
You also need it to be a type="text" to allow for the commas to be added, or Chrome will not allow you to set that. However, then to prevent non-numeric chars being added you need another function to strip them out before setting the value.
See the below working snippet:
const {useState} = React;
const MainComponent = () => {
const [value, setValue] = useState(0);
const addCommas = num => num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
const removeNonNumeric = num => num.toString().replace(/[^0-9]/g, "");
const handleChange = event =>
setValue(addCommas(removeNonNumeric(event.target.value)));
return (
<div>
<input type="text" value={value} onInput={handleChange} />
</div>
);
};
// Render it
ReactDOM.render(
<MainComponent/>,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>

_this3 not a function (React)

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(...)

Categories