React, Uncaught RangeError: Maximum call stack size exceeded - javascript

I have an array of objects containing details about players (name, skill(number 0-10)),
I'm trying to split that array into two arrays Team1 and Team2 while making sure that the sum of skill in both teams is equal or close at least.
This is the way I came up with but it seems like I have some kind of recursion error and I can't see where the problem is.
const [team1, setTeam1] = useState([]);
const [team2, setTeam2] = useState([]);
const [team1Skill, setTeam1Skill] = useState(0);
const [team2Skill, setTeam2Skill] = useState(0);
const [skillGap, setSkillGap] = useState(0);
const randomize = () => {
let randomNum1 = Math.floor(Math.random() * playersList.length);
let randomNum2 = Math.floor(Math.random() * playersList.length);
const pickedPlayer1 = playersList[randomNum1];
const pickedPlayer2 = playersList[randomNum2];
if (!team2.includes(pickedPlayer1) && !team1.includes(pickedPlayer1)) {
team1.push(pickedPlayer1);
}
if (!team1.includes(pickedPlayer2) && !team2.includes(pickedPlayer2)) {
team2.push(pickedPlayer2);
}
checkNumberEquality();
if (team1.length + team2.length === playersList.length) {
checkSkillSum();
}
};
const checkNumberEquality = () => {
if (team1.length + team2.length !== playersList.length) {
randomize();
}
};
const checkSkillSum = () => {
team1.map((player) => {
setTeam1Skill((value) => value + player.skill);
});
team2.map((player) => {
setTeam2Skill((value) => value + player.skill);
});
if (team1Skill === team2Skill) {
console.log("The teams are perfectly balanced.");
} else if (
team1Skill + 1 == team2Skill ||
team1Skill + 2 == team2Skill
) {
console.log(
"The teams have been balanced as much as possible, but team 2 is slightly better than team 2"
);
} else if (
team1Skill - 1 == team2Skill ||
team1Skill - 2 == team2Skill
) {
console.log(
"The teams have been balanced as much as possible, but team 1 is slightly better than team 2"
);
} else if (team1Skill !== team2Skill) {
setTeam1([]);
setTeam2([]);
randomize();
}
};
Here's a better and clearer image of the code
Also, in the app I have an input field (in another component) and whenever I type something there I notice that it keeps executing the commands and repeating things ( I kinda know how onChange and re-rendering work in react but it just seemed weird )

Related

Why when I'm setting Data with Axios it doesn't work

I'm learning React and I'm facing my first problem. I try to set some Data to use it but I failed.
Here is what I do :
const [randomCharacters, setRandomCharacters] = useState([]);
const randomNumber = (minimum,maximum, numberOfCharactersWanted) => {
let characters = [];
for (let i = 1; i <= numberOfCharactersWanted; ++i){
let randomNumber = (Math.random() * (maximum - minimum + 1) ) << 0;
if (!characters.includes(randomNumber)){
characters.push(randomNumber);
} else {
randomNumber = (Math.random() * (maximum - minimum + 1) ) << 0;
characters.push(randomNumber);
}
}
return characters.join();
}
useEffect(() =>{
axios.get('https://rickandmortyapi.com/api/character/'+ randomNumber(0,totalCharacters, 6))
.then((response) => {
response.data.map((res) => (
setRandomCharacters(res.id)
))
//setRandomCharacters(response.data)
})
})
TotalCharacter is equal to 826 and when I fetch, I get the goo url, for example =>
'https://rickandmortyapi.com/api/character/23,45,89,567,432,9' but when I fetch the randomCharacters, I can see state overriding over and over instand of getting an array of value and I don't understand why, any idea ?
change return characters.join(); to return characters.join('');
in your randomNumber function.
reason why, a blank .join() uses , as the default joining value.

Store value in a variable

I am new in Javascript. I develop a program that store the score of humun and computer guess number. I want the human score and computer score will update when I call the updateScore() functions. However, it works but the score unable to increase by last score.
Here is the code:
let humanScore = 0;
let computerScore = 0;
let currentRoundNumber = 1;
// Write your code below:
const generateTarget = () => {
return Math.floor(Math.random()*10);
};
const compareGuesses = () => {
// Humun & Computer guess a number
const humunGuess = 1;
const computerGuess = 2;
// Call the generateTarget functions
const secretTargetNumber = generateTarget();
// Compare the difference between Target number and humun guess number
const humunTarget = Math.abs(humunGuess - secretTargetNumber);
// Compare the difference between Target number and computer guess number
const computerTarget = Math.abs(computerGuess - secretTargetNumber);
// Return true if humun won, false if computer won
if (humunTarget < computerTarget || humunTarget == computerTarget) {
return true;
}
else {
return false;
}
};
let updateScore = () => {
switch (compareGuesses()) {
case true:
return humanScore+=1;
case false:
computerScore+=1;
}
};
updateScore()
console.log(humanScore)
console.log(computerScore)
It is a programming language based on javascript event/trigger features. all variables are reset when you call the file again.
The variables seem to be reset every time you call the javascript file
let humanScore = 0,
computerScore = 0,
currentRoundNumber = 1;
// Write your code below:
const generateTarget = () => {
return Math.floor(Math.random() * 10);
};
const compareGuesses = () => {
// Humun & Computer guess a number
const humanGuess = 1;
const computerGuess = 2;
// Call the generateTarget functions
const secretTargetNumber = generateTarget();
// Compare the difference between Target number and humun guess number
const humanTarget = Math.abs(humanGuess - secretTargetNumber);
// Compare the difference between Target number and computer guess number
const computerTarget = Math.abs(computerGuess - secretTargetNumber);
// Return true if humun won, false if computer won
if (humanTarget < computerTarget) {
return true;
} else {
return false;
}
};
let updateScore = () => {
switch (compareGuesses()) {
case true:
return humanScore += 1;
case false:
computerScore += 1;
}
};
let showScore = () => {
updateScore();
console.log(humanScore)
console.log(computerScore)
}
<button onclick="showScore()">Click</button>
Woking Demo : https://jsfiddle.net/6v25y9qd/
Push click and show console
Because you're new, and I think #Onur Özkır is on to something, I will give some guidelines instead.
I understand that you mostly trying out to see if things works, and will refactor afterwards, but I recommend that you create methods that does only one thing. You already done that with generateTarget(), but if I look at compareGuesses(), that method doesn't just compare guesses. It also reads and generates numbers. I would instead read the values in updateScore() and then add them as parameters in compareGuesses.
By restricting your methods to only do one thing will make your code more readable, your methods will be smaller so they are easier to grasp, and the code is easier to debug.
I would also like to suggest that you only return a value at the end of the method, and always return (unless you can fit everything on one row) using a variable.
I also don't like switches in javascript, because the forced use of return. Would rather use ifs statement or a shorthand if statements, using conditional/ternary operator.
Use method names and variables to explain the code (ex. the constant HUMAN_WON in the code below). Try to avoid comments as far as possible. Comments should, IMHO, only be used if you generate a documentation. I suggest that you get your hands on Clean Code, which was a revelation for me to read, even as an experienced programmer.
I will refactor your code as a suggestion of how it can look like:
let humanScore = 0;
let computerScore = 0;
let currentRoundNumber = 1;
const HUMAN_WON = 1, COMPUTER_WON = -1, EQUAL = 0;
const generateTarget = () => {
return Math.floor(Math.random() * 10);
};
const readHumanGuess = () => {
return 1; // replace with appropriate code
}
const generateComputerGuess = () => {
return 2; // replace with generateTarget();
}
const compareGuesses = (humanGuess, computerGuess, secretTargetNumber) => {
let result = EQUAL;
const humanTarget = Math.abs(humanGuess - secretTargetNumber);
const computerTarget = Math.abs(computerGuess - secretTargetNumber);
if (humanTarget < computerTarget) {
result = HUMAN_WON;
} else if (computerTarget < humanTarget) {
result = COMPUTER_WON;
}
return result;
};
let updateScore = () => {
let humanGuess = readHumanGuess();
let computerGuess = generateComputerGuess();
let secretTargetNumber = generateTarget();
let whoWon = compareGuesses(humanGuess, computerGuess, secretTargetNumber);
if (whoWon == HUMAN_WON) {
humanScore++;
} else if (whoWon == COMPUTER_WON) {
computerScore++;
}
};
let displayCurrentScore = () => {
updateScore();
console.log(`${humanScore} vs ${computerScore}`);
}
<input type="button" onclick="displayCurrentScore()" value="Display Score">
You can even go one step further refactoring readGuesses() from updateScore() and separate updating UI—displayCurrentScore()—from handling logic in updateScore().
let humanScore = 0;
let computerScore = 0;
let currentRoundNumber = 1;
const HUMAN_WON = 1, COMPUTER_WON = -1, EQUAL = 0;
const generateTarget = () => {
return Math.floor(Math.random() * 10);
};
const readHumanGuess = () => {
return 1; // replace with appropriate code
}
const generateComputerGuess = () => {
return 2; // replace with generateTarget();
}
const readGuesses = () => {
let humanGuess = readHumanGuess();
let computerGuess = generateComputerGuess();
let secretTargetNumber = generateTarget();
return [humanGuess, computerGuess, secretTargetNumber]; // returning array
}
const compareGuesses = (humanGuess, computerGuess, secretTargetNumber) => {
let result = EQUAL;
const humanTarget = Math.abs(humanGuess - secretTargetNumber);
const computerTarget = Math.abs(computerGuess - secretTargetNumber);
if (humanTarget < computerTarget) {
result = HUMAN_WON;
} else if (computerTarget < humanTarget) {
result = COMPUTER_WON;
}
return result;
};
let updateScore = (humanGuess, computerGuess, secretTargetNumber) => {
let whoWon = compareGuesses(humanGuess, computerGuess, secretTargetNumber);
if (whoWon == HUMAN_WON) {
humanScore++;
} else if (whoWon == COMPUTER_WON) {
computerScore++;
}
return {'human': humanScore, 'computer': computerScore};
};
let displayCurrentScore = () => {
let [humanGuess, computerGuess, secretTargetNumber] = readGuesses();
let score = updateScore(humanGuess, computerGuess, secretTargetNumber);
console.log(`${score.human} vs ${score.computer}`);
}
<input type="button" onclick="displayCurrentScore()" value="Display Score">

React JS: State become 'undefined' when working with useEffect

I am working on typewriter effect in React JS. I have previously implemented typewriter effect in Vanilla JS.
The React JS code that I am writing is
import React, { useEffect, useRef, useState } from 'react'
const Typewriter = () => {
const el = useRef();
const words = ["Life", "Could Be", "A Dream"];
const [index, setIndex] = useState(0);
const [subIndex, setSubIndex] = useState(0);
const [isEnd, setIsEnd] = useState(false);
const [isDeleting, setIsDeleting] = useState(false);
const [duration, setDuration] = useState(200);
const [currentWord, setCurrentWord] = useState("");
const spedUp = 50;
const normalSpeed = 200;
useEffect(() => {
const interval = setInterval(() => {
setIndex(index % words.length);
setIsEnd(false);
if (!isDeleting && subIndex <= words[index].length) {
setCurrentWord((prev) => prev.concat(words[index][subIndex]));
setSubIndex((prev) => prev += 1);
el.current.innerHTML = currentWord;
}
if (isDeleting && subIndex > 0) {
setCurrentWord((prev) => prev.substring(0, prev.length - 1));
setSubIndex((prev) => prev -= 1);
el.current.innerHTML = currentWord;
}
if (subIndex === words[index].length) {
setIsEnd(true);
setIsDeleting(true);
}
if (subIndex === 0 && isDeleting) {
setCurrentWord("");
setIsDeleting(false);
setIndex((prev) => prev += 1);
}
setDuration(isEnd ? 1500 : isDeleting ? spedUp : normalSpeed);
}, duration);
return () => clearInterval(interval);
}, [subIndex, index, currentWord, isEnd, isDeleting, duration, words])
return (
<>
<h1 ref={el}>Placeholder text</h1>
</>
)
}
export default Typewriter
When I run the React App, the phrases/words are appended with 'undefined' at the end. The application displays the 3 phrases once and then the app crashes because the code tries to get the length of an undefined array element.
Here are the visuals
React Application GIF
Can someone explain to me what is happening and are the dependencies used for useEffect hook correct or not?
Main issue
You are using set(X) and making an assumption that the value will be immediately set and that you can base later conditions on it. useState does not actually set the state immediately -- it happens on the next loop, so any conditions you have (like testing the index after setIndex) are going to go awry because they're using stale values. See useState set method not reflecting change immediately
I avoided this by using interim values (newIndex, newSubIndex) -- I'm not wild about this approach -- I'd probably refactor with a different strategy, but it was the quickest way to get this up and running.
import React, { useEffect, useRef, useState } from 'react'
const Typewriter = () => {
const words = ["Life", "Could Be", "A Dream"];
const [index, setIndex] = useState(0);
const [subIndex, setSubIndex] = useState(0);
const [isEnd, setIsEnd] = useState(false);
const [isDeleting, setIsDeleting] = useState(false);
const [duration, setDuration] = useState(200);
const [currentWord, setCurrentWord] = useState("");
const spedUp = 50;
const normalSpeed = 200;
useEffect(() => {
const interval = setInterval(() => {
let newSubIndex = subIndex;
let newIndex = index % words.length;
setIsEnd(false);
if (!isDeleting && subIndex < words[newIndex].length) {
setCurrentWord((prev) => prev.concat(words[newIndex][subIndex]));
newSubIndex += 1;
}
if (isDeleting && subIndex > 0) {
setCurrentWord((prev) => prev.substring(0, prev.length - 1));
newSubIndex -= 1;
}
if (newSubIndex === words[newIndex].length) {
setIsEnd(true);
setIsDeleting(true);
}
if (newSubIndex === 0 && isDeleting) {
setCurrentWord("");
setIsDeleting(false);
newIndex += 1;
}
setSubIndex(newSubIndex);
setIndex(newIndex);
setDuration(isEnd ? 1500 : isDeleting ? spedUp : normalSpeed);
}, duration);
return () => clearInterval(interval);
}, [subIndex, index, currentWord, isEnd, isDeleting, duration, words])
return (
<>
<h1>{currentWord}</h1>
</>
)
}
export default Typewriter
Minor issue
In React, rather than using a ref and editing innerHTML, normally the content is just inlined in the rendered DOM structure. See <h1>{currentWord}</h1>
You may want to look into storing your state in a larger object rather than everything being a separate useState. You may also want to look into useReducer.

Using JavaScript to create an animated counter with React.js

I have four counters that I would like to animate (incrementing the count from 0 to a specific number) using JavaScript. My code is the following:
const allCounters = document.querySelectorAll('.counterClass');
counters.forEach(allCounters => {
const updateCounter = () => {
const end = +allCounters.getAttribute('data-target');
const count = +allCounters.innerText;
const increment = end / 200;
if (count < end) {
allCounters.innerText = count + increment;
setTimeout(updateCounter, 1);
} else {
allCounters.innerText = end;
}
};
updateCounter();
});
In React, I wasn't sure how to get it to run. I tried including the code after the using dangerouslySetInnerHTML, but that's not working. (I'm new to React).
I appreciate any assistance you could give me. Thanks so much!
Right before I posted my question, I found a plug-in (https://github.com/glennreyes/react-countup) that could do it, but wondered if it's still possible using JS. Thanks!
While using React, try to avoid direct DOM operations (both query and modifications). Instead, let React do the DOM job:
const Counter = ({start, end}) = {
// useState maintains the value of a state across
// renders and correctly handles its changes
const {count, setCount} = React.useState(start);
// useMemo only executes the callback when some dependency changes
const increment = React.useMemo(() => end/200, [end]);
// The logic of your counter
// Return a callback to "unsubscribe" the timer (clear it)
const doIncrement = () => {
if(count < end) {
const timer = setTimeout(
() => setCount(
count < (end - increment)
? count + increment
: end
),
1);
return () => clearTimeout(timer);
}
}
// useEffect only executes the callback once and when some dependency changes
React.useEffect(doIncrement, [count, end, increment]);
// Render the counter's DOM
return (
<div>{count}</div>
)
}
const App = (props) => {
// Generate example values:
// - Generate 5 counters
// - All of them start at 0
// - Each one ends at it's index * 5 + 10
// - useMemo only executes the callback once and
// when numCounters changes (here, never)
const numCounters = 5;
const countersExample = React.useMemo(
() => new Array(numCounters)
.fill(0)
.map( (c, index) => ({
start: 0,
end: index*5 + 10,
})
),
[numCounters]
);
return (
<div id="counters-container">
{
// Map generated values to React elements
countersExample
.map( (counter, index) => <Counter key={index} {...counter}/> )
}
</div>
)
}

Logical NaN Error in my React Typescript counter for win percentage

I am making a simple Typescript counter to track my win percentage in my Legends of Runeterra games I play. I can't figure out why when I increment a win or a loss I get NaN as my win percentage. The logic seems fine (obviously you can't decrement right now, that's a problem for later), I just want to focus on fixing the NaN error for now.
Here's my counter component:
import React, { useState } from 'react'
// add a ? after the type name if you want any one of these to be optional, ex: wins?
const Counter: React.FC<{
initialGamesPlayed: number
initialWins: number
initialLosses: number
initialWinPercentage: number
initialDeckName: string
}> = ({
initialDeckName,
initialWinPercentage,
initialWins,
initialLosses,
initialGamesPlayed,
}) => {
const [deckName, setDeckName] = useState(initialDeckName)
const [wins, setWins] = useState(initialWins)
const [losses, setLosses] = useState(initialLosses)
const [totalGames, setTotalGames] = useState(initialGamesPlayed)
const [winPercentage, setWinPercentage] = useState(initialWinPercentage)
const incrementWins = () => {
setWins(wins + 1)
winPercentageCalc()
console.log(winPercentage)
}
const decrementWins = () => {
if (wins > 0) setWins(wins - 1)
winPercentageCalc()
}
const incrementLosses = () => {
setLosses(losses + 1)
winPercentageCalc()
console.log(winPercentage)
}
const decrementLosses = () => {
if (losses > 0) setLosses(losses - 1)
winPercentageCalc()
}
const winPercentageCalc = () => {
setTotalGames(wins + losses)
setWinPercentage((wins / totalGames) * 100)
}
return (
<div>
<p>Deck Name: </p>
<p>wins: {wins} </p>
<button onClick={incrementWins}>+</button>
<button onClick={decrementWins}>-</button>
<p>losses: {losses}</p>
<button onClick={incrementLosses}>+</button>
<button onClick={decrementLosses}>-</button>
<p>Win Percentage: {winPercentage} % </p>
</div>
)
}
export default Counter
Thanks for taking a look!
The setWins, setLosses, setTotalGames and setWinPercentage are all asynchronous functions. So the first time your call winPercentageCalc, this is what happens:
const winPercentageCalc = () => {
setTotalGames(wins + losses) // This is asynchronous, so...
setWinPercentage((wins / totalGames) * 100) // totalGames = 0 => NaN
}
When you divide wins by totalGames, totalGames has not been updated so you divide by 0 which gives NaN (Not a Number) as a result

Categories

Resources