I want to create an array of non-overlapping random numbers - reactjs

I want to create an array of non-overlapping random numbers with a length of 20.
However, when executed, the array is not complete and sometimes an error message "Too many re-renders" appears.
import React, {useState, useEffect} from "react";
const Ques = () => {
const [randArr, setRandArr] = useState([]);
const [randNum, setRandNum] = useState(0);
let rand = Math.floor(Math.random() * (19-0));
if(randArr.length !== 20 && randArr.includes(randNum) === true){
rand = Math.floor(Math.random() * (19-0));
setRandNum(rand)
}
useEffect(() =>{
setRandNum(rand);
setRandArr([...randArr, randNum]);
console.log("randNum : ", randNum);
console.log("randArr : ", randArr);
},[rand]);
return (
<div>
<button>Start</button>
</div>
);
};
export default Ques;

As has been pointed out in comments, you are declaring a new rand value each render cycle and using it as a dependency for an useEffect hook which then also enqueues a state update and triggers a rerender... repeat ad nauseam.
Instead of trying to iterate and populate an array with "random" numbers by "guessing" if they've already been selected, it'd be better to start with an array of [1..20] and "shuffle" it.
Example:
const res = Array.from({ length: 20 }, (_, i) => i + 1).sort(() => Math.random() - 0.5);
console.log(res.join(","));
You can just initialize your state to this value. No need for any loops and useEffect hooks.
const [randArr, setRandArr] = useState(
Array.from({ length: 20 }, (_, i) => i + 1).sort(() => Math.random() - 0.5)
);

import React, { useState, useEffect } from "react";
const App = () => {
const [random, setRandom] = useState([]);
const getRandom = () => Math.floor(Math.random() * (19 - 0));
useEffect(() => {
const arr = [];
for (let i = 0; i < 20; i++) {
arr.push(getRandom());
}
setRandom(arr);
console.log(arr);
}, []);
return (
<div>
<div>{random.join(",")}</div>
<button>Start</button>
</div>
);
};
export default App;

Related

Unable to update array with Min and Max values

I'm writing a code where I need to get Min and Max values. In my real-time scenario, I've got an SDK to which I make two promise calls and it returns min and max values. I'm trying to mock up the same in my example below. I've got a context in place that is used to store the values. Here is my sample code.
Here is my context:
import React, { useState } from "react";
import { useContext } from "react";
const ProductsContext = React.createContext();
export const ProductsProvider = ({ children }) => {
const [priceValues, setPriceValues] = useState([0, 0]);
return (
<ProductsContext.Provider
value={{
priceValues,
setPriceValues
}}
>
{children}
</ProductsContext.Provider>
);
};
export const useProductsContext = () => {
return useContext(ProductsContext);
};
And my code is as below:
import { useEffect } from "react";
import { PriceRange } from "./PriceRange";
import { useProductsContext } from "./ProductsContext";
const ShowRange = () => {
const { priceValues, setPriceValues } = useProductsContext();
const getMinVal = () => {
setPriceValues([Math.random() * 100, priceValues[1]]);
};
const getMaxVal = () => {
setPriceValues([priceValues[0], Math.random() * 100]);
};
useEffect(() => {
getMinVal();
getMaxVal();
}, []);
console.log(JSON.stringify(priceValues));
return <>{priceValues[0] && priceValues[1] && <PriceRange />}</>;
};
export default ShowRange;
Currently, when I run this, priceValues is of format [0, randomNumber]. And once I get the values.
I'm confused on why only the second value in Array gets updated but not the first. Where am I going wrong?
Here is working code of the same.
Even though getMinVal and getMaxVal are separate functions that update state, they are called at the same time. This is causing the first update to be lost by the second update since priceValues[1] will not be updated until the next render.
You could solve this by using the function update form of setting state:
const getMinVal = () => {
setPriceValues((prev) => ([Math.random() * 100, prev[1]]));
};
const getMaxVal = () => {
setPriceValues((prev) => ([prev[0], Math.random() * 100]));
};
After you call setPriceValues React sets value (internally) and schedule re-render, but value stored in priceValues const is not updated.
State is not exactly like variable. State has same value during one re-render. If you want actual value during re-render (after mutation) pass function with param to setState.
You code with expecting behaviour:
const ShowRange = () => {
const { priceValues, setPriceValues } = useProductsContext(); // priceValues == [0, 0]
const getMinVal = () => {
// priceValues == [0, 0]
setPriceValues((pricesValues) => [Math.random() * 100, priceValues[1]]);
};
const getMaxVal = () => {
// priceValues == [0, 0]
setPriceValues((priceValues) => [priceValues[0], Math.random() * 100]);
};
useEffect(() => {
// priceValues == [0, 0]
getMinVal();
// priceValues == [0, 0]
getMaxVal();
}, []);
console.log(JSON.stringify(priceValues));
return <>{priceValues[0] && priceValues[1] && <h1>Hi</h1>}</>;
};

React counter not changing on the right interval

I am doing a small project with react lifecycles and I am attempting to change all box colors randomly every time the counter reaches a number divisible by 5 however, the boxes only change when the number is divisible by 10.
App.js:
import React from 'react';
import './App.css';
import Counter from "./components/Counter";
import Box from "./components/Box";
function App() {
const randomColor = [
"#0d0d8c",
"#a41313",
"#064826",
"#996e00"
];
const [number, setNumber] = React.useState(0);
const [boxes, setBoxes] = React.useState([]);
const [color, setColor] = React.useState('red');
const [change, setChange] = React.useState(false);
let box = boxes.map((obj, idx) =>
<Box key={idx} color={color} />
);
let getColor = () => {
if (boxes.length % 5 === 0) {
let rand = Math.floor(Math.random() * randomColor.length);
setColor(randomColor[rand]);
return randomColor[rand];
}
return color;
};
React.useEffect(() => {
if (boxes.length % 5 === 0) {
let rand = Math.floor(Math.random() * randomColor.length);
setColor(randomColor[rand]);
return randomColor[rand];
}
return color;
}, [color])
React.useEffect(() => {
if (number % 2 === 0) {
let newBoxList = [...boxes];
newBoxList.push({color: getColor()});
setBoxes(newBoxList);
}
}, [number,change]);
let reset = () => {
setNumber(0);
setBoxes([]);
setChange(true);
};
return (
<div className="App">
<button onClick={() => setNumber(number + 1)}>Increase</button>
<button onClick={reset}>reset</button>
<Counter count={number}/>
<div className="boxes">{box}</div>
</div>
);
}
export default App;
deployed site: https://priceless-spence-9ae99a.netlify.app/
I think the culprit is your useEffect() hook listening to number. Whenever number is even you are copying the existing contents of boxes and then adding new boxes to the existing boxes; you are not actually updating all boxes in the array. It seems your desire is to have all of those boxes, existing and new, be updated to a new color so you'll need to iterate through the array and update each.
I think it comes down to the fact that you are assigning the return value of getColor to your new boxes rather than the value of local state color.

How to update several states at the same time if their value depend on each other with react hooks

I have a button and on button click I need to update several states. Other states like Salary or employerCompensationDays/insuranceCompensationDays I do update and it works. I use useState with an object to update several states, but their values depend on each other, so I need several clicks to update all of them. How could I update all of them with only one click?
import React from "react";
import { useState } from "react";
const Calculator = () => {
const [salary, setSalary] = useState(0);
const [employerCompensationDays, setEmployerCompensationDays] = useState(0);
const [insuranceCompensationDays, setInsuranceCompensationDays] = useState(0);
const [compensation, setCompensation] = useState({ dayliAllowance: 0, emoloyerCompensation: 0, insuranceCompensation: 0, compensationTotal: 0 })
const handleCompensation = () => {
setCompensation({
dayliAllowance: ((salary / 20) / 100) * 70,
emoloyerCompensation: compensation.dayliAllowance * employerCompensationDays,
insuranceCompensation: compensation.dayliAllowance * insuranceCompensationDays,
compensationTotal: compensation.emoloyerCompensation + compensation.insuranceCompensation
})
}
return (
<div>
<button onClick={handleCompensation} className="calculator-btn" type='button'>Calculate</button>
</div>
)
}
export default Calculator;
I think if you separate your calculations, you can do the state setting with one click.
import React from "react";
import { useState } from "react";
const Calculator = () => {
const [salary, setSalary] = useState(0);
const [employerCompensationDays, setEmployerCompensationDays] = useState(0);
const [insuranceCompensationDays, setInsuranceCompensationDays] = useState(0);
const [compensation, setCompensation] = useState({
dayliAllowance: 0,
emoloyerCompensation: 0,
insuranceCompensation: 0,
compensationTotal: 0
});
const calculate = () => {
const dayliAllowance = (salary / 20 / 100) * 70;
const emoloyerCompensation = dayliAllowance * employerCompensationDays;
const insuranceCompensation = dayliAllowance * insuranceCompensationDays;
const compensationTotal = emoloyerCompensation + insuranceCompensation;
return {
dayliAllowance,
emoloyerCompensation,
insuranceCompensation,
compensationTotal
};
};
const handleCompensation = () => {
const result = calculate();
setCompensation({
...result
});
};
return (
<div>
<button
onClick={handleCompensation}
className="calculator-btn"
type="button"
>
Calculate
</button>
</div>
);
};
export default Calculator;
I would use a useMemo instead of state for such cases since the only value you manipulate is salary.
The cons of the way you are doing is, setting state 4 times will means you will re-render min of 4 times.
Below method only render once.
const updateSalary = (amount) => {
.... some some stuff
setSalary(finalamount) //this will trigger useMemo below.
}
const calculated = useMemo(() => {
const dayliAllowance = ((salary / 20) / 100) * 70;
const emoloyerCompensation = dayliAllowance * employerCompensationDays;
const insuranceCompensation = dayliAllowance * insuranceCompensationDays,
const compensationTotal =compensation.emoloyerCompensation + insuranceCompensation
return { dayliAllowance, emoloyerCompensation, insuranceCompensation, compensationTotal }
},[salary]) //when salary changes, this will be recalculated
return <div>{calculated.daliAllowance}</div> //example

React State value not updated in Arrow functional component

React state value not updated in the console but it is updated in the view.
This is my entire code
import React, { useEffect, useState } from 'react';
const Add = (props) => {
console.log("a = ", props.a)
console.log("b = ", props.b)
const c = props.a+props.b;
return (
<div>
<p><b>{props.a} + {props.b} = <span style={{'color': 'green'}}>{c}</span></b></p>
</div>
)
}
// export default React.memo(Add);
const AddMemo = React.memo(Add);
const MemoDemo = (props) => {
const [a, setA] = useState(10)
const [b, setB] = useState(10)
const [i, setI] = useState(0);
useEffect(() => {
init()
return () => {
console.log("unmounting...")
}
}, [])
const init = () => {
console.log("init", i)
setInterval(()=>{
console.log("i = ", i)
if(i == 3){
setA(5)
setB(5)
}else{
setA(10)
setB(10)
}
setI(prevI => prevI+1)
}, 2000)
}
return (
<div>
<h2>React Memo - demo</h2>
<p>Function returns previously stored output or cached output. if inputs are same and output should same then no need to recalculation</p>
<b>I= {i}</b>
<AddMemo a={a} b={b}/>
</div>
);
}
export default MemoDemo;
Please check this image
Anyone please explain why this working like this and how to fix this
The problem is as you initialized the setInterval once so it would reference to the initial value i all the time. Meanwhile, React always reference to the latest one which always reflect the latest value on the UI while your interval is always referencing the old one. So the solution is quite simple, just kill the interval each time your i has changed so it will reference the updated value:
React.useEffect(() => {
// re-create the interval to ref the updated value
const id = init();
return () => {
// kill this after value changed
clearInterval(id);
};
// watch the `i` to create the interval
}, [i]);
const init = () => {
console.log("init", i);
// return intervalID to kill
return setInterval(() => {
// ...
});
};
In callback passed to setInterval you have a closure on the value of i=0.
For fixing it you can use a reference, log the value in the functional update or use useEffect:
// Recommended
useEffect(() => {
console.log(i);
}, [i])
const counterRef = useRef(i);
setInterval(()=> {
// or
setI(prevI => {
console.log(prevI+1);
return prevI+1;
})
// or
conosole.log(counterRef.current);
}, 2000);

Visualising pattern matching in JavaScript

I have a naive pattern matching function, and I'm trying to slow down execution of each comparison so I can create a visualiser for it. However, I want to be able to access my i and j variables outside of the function. I am attempting to do this by declaring them outside of the function, passing them in, and returning them after each match. This way I can press a button to control the flow of execution. However, they are not being returned properly, and I suspect this has something to do with my use of async/await, and the need to return the values as a Promise.
https://codesandbox.io/s/staging-http-0zm04?file=/src/App.tsx:0-1072
import React, { useState } from "react";
import "./styles.css";
const delay = (ms) => new Promise((resolve, reject) => setTimeout(resolve, ms));
export const naive = async (text: string, pattern: string, i: number, j: number) => {
const matches = [];
let n = text.length;
let m = pattern.length;
while (i < n){
while (j < pattern.length && pattern[j] === text[i + j]){
j += 1;
await delay(500);
}
if (j === m){
matches.push(i)
}
return [i, j, matches]
}
}
export default function App() {
const [text, setText] = useState<string>("abcdefghijklmnopqrstuvwxyzabcd")
const [pat, setPat] = useState<string>("abc")
const [i, updateI] = useState(0);
const [j, updateJ] = useState(0);
const nextMatch = () => {
let results = naive(text, pat, i, j);
updateI(results[0]);
updateJ(results[1]);
}
return (
<div>
<button style = {{width: "100px", height: "50px"}}onClick = {() => nextMatch()}/>
{i}
{j}
</div>
);
}
As navie is an async function you have to add then.This would help to return correct i and j values
const nextMatch = () => {
naive(text, pat, i, j).then((results) => {
updateI(results[0]);
updateJ(results[1]);
});
};

Resources