how to validate after checkboxed clicked? - javascript

I broke my Code down to make it look simple
const [factor, setfactor] = useState(1);
const [nullify, setNullify] = useState(1);
const Price = 10;
const Bonus = 15;
const finalPrice = (Price * factor - Bonus) * nullify;
// start 5 = (10 * 2 -15)* 1
// after Click -5 = (10 * 1 -15)* 1
//what i want 0 = (10 * 1 -15)* 0
const handleFactor = () => {
setfactor(1)
validate()
};
const validate = () => {
if (finalPrice <= 0) {
setNullify(0);
}
};
useEffect(() => {
handleFactor();
}, [factor]);
//HTML Stuff
return (
<>
<input type="checkbox" onClick={handleFactor} />
<input type="checkbox" onClick="activate bonus" />
{finalPrice}
</>
);
I want, if the bonus make it below 0 the final price should not be a minus number , instead it should becomes a zero itself - but it doesn't work.
I know, that the final price will be 5 when the validation do it's thing. But how do I get the newPrice?

State updates are asynchronous, so you can get the updated state on the next render. When you do this
const handleFactor = () => {
setfactor(1)
validate()
};
the state haven't updated yet, and validate would use the previous (valid) values. You'll need to move validate to the next render for this to work.
However, you don't need another state to ensure that finalPrice is above 0. Use Math.max(price * factor - bonus, 0)to get the max between the final price and0` (or any other minimum number):
const { useState, Fragment } = React;
const Demo = ({ price, bonus }) => {
const [factor, setfactor] = useState(2);
const finalPrice = Math.max(price * factor - bonus, 0);
const handleFactor = () => {
setfactor(1)
};
return (
<Fragment>
<input type="checkbox" onClick={handleFactor} />
{finalPrice}
</Fragment>
);
};
ReactDOM
.createRoot(root)
.render(<Demo price={10} bonus={15} />);
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<div id="root"></div>

Related

React, Uncaught RangeError: Maximum call stack size exceeded

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 )

Generate a random number and add to an array to be rendered in React Native

I have written some basic JS code in React Native to generate a random number then render the number. I would now like to add every generated number to a list.
Currently the generated random number can be added to a list, to do this I need to first generate the number (using
onPress={getRandomInt}
) THEN to add to the list on a separate button:
onPress={addNumber}
.
I've tried combining the functions and for the life of me can't get it to work. Any help would be much appreciated, thank you.
import React, { useEffect, useState } from 'react';
import { View, Text, Button } from 'react-native';
import Calls from './Calls';
export default function NumberGen() {
const [number, setNumber] = React.useState();
const [names, setNames] = React.useState([]);
const getRandomInt = (min, max) => {
min = Math.ceil(1);
max = Math.floor(90);
const randomNumber = Math.floor(Math.random() * (max - min +1) + min);
setNumber(randomNumber)
}
const addNumber = () => {
getRandomInt
setNames(current => [...current, number]);
}
return (
<>
<View>
<Button
title='get random number'
onPress={getRandomInt}
/>
<Text>{number}</Text>
<Text>{Calls.call[number-1]}</Text>
</View>
<Button title='add to list' onPress={addNumber}/>
<View>
{names.map((element) => {
return(
<Text>{element}</Text>
)
})}
</View>
</>
);
};
I've tried:
Adding a new array to state
Combining both functions
Using .then after the random number is generated but before it's set to state.
const addNumber = () => {
getRandomInt()
setNames(current => [...current, number]);
}
missing parenthesis on the getRandomInt
You have declared the function getRandomInt, which you are passing to the onPress function, which will call the function when you press the button. Which of course works as expected.
const getRandomInt = (min, max) => {
min = Math.ceil(1);
max = Math.floor(90);
const randomNumber = Math.floor(Math.random() * (max - min +1) + min);
setNumber(randomNumber)
}
The problem which I understand is that you want to combine both functions into one click.
const addNumber = () => {
getRandomInt()
setNames(current => [...current, number]);
}
In order to call the function, you need parenthesis after the name. You have arguments min and max in as parameters but since you've initialized them in your function, it is not necessary to pass them.
update your function like this
const getRandomInt = (min, max) => {
min = Math.ceil(1);
max = Math.floor(90);
const randomNumber = Math.floor(Math.random() * (max - min + 1) + min);
setNumber(randomNumber);
return randomNumber;
};
const addNumber = () => {
if (names.length > 0) {
const updateValue = getRandomInt();
setNames(current => [...current, updateValue]);
} else {
setNames(current => [...current, number]);
}
};

Assign numbers to alphabets in react js

Firstly, i wanna assign numbers to all alphabets like this:a=1,b=2,c=3,d=4 etc.
Secondly, I want to create an input field and submit Button.
Thirdly, when I enter alphabets on that input field,
(eg: abcd it shows 1234 because i already mentioned above, a=1, b=2, c=3, d=4).
And last, I wanna to add them (eg: I entered "abcd" and output is 1234 and add them.)Final Output for abcd is 1+2+3+4 =10.
import React,{useState} from 'react'
import "../node_modules/bootstrap/dist/css/bootstrap.min.css"
const Data = () => {
const [data, setData] = useState({a: 1,b: 2,c: 3,d: 4,e: 5,f: 6,g: 7,h: 8,i: 9,j: 10,k: 11,l: 12,m: 13,n: 14,o: 15,p: 16,q: 17,r: 18,s: 19,t: 20,u: 21,v: 22,w: 23,x: 24,y: 25,z: 26})
const changeHandler = e => {
setData({...data,[e.target.name]:[e.target.value]})
}
const submitHandler=e=> {
e.preventDefault();
}
return (
<div>
<form onSubmit={submitHandler}>
<input type="text"onChange={changeHandler}/><br/>
<button className="btn btn-primary"onClick={()=>setData(console.log(data))}>Click</button><br/>
</form>
</div>
)
}
export default Data
Presented below is one possible way to achieve the desired objective.
Code Snippet
const {useState} = React;
const Thingy = ({...props}) => {
// state variable to hold user input
const [userInput, setUserInput] = useState('');
// state variable to track user submit button click
const [showResult, setShowResult] = useState(false);
// method to update "userInput" state variable
const handleChange = ev => (setUserInput(ev.target.value), setShowResult(false));
// method to conver a single character (a to z) into number (1 to 26)
const convertChar = ch => {
const c = ch.toLowerCase();
// if user input is a digit, ensure return value is a "string"
if ('0' <= c && c <= '9') return ch.toString();
if (!('a' <= c && c <= 'z')) return ch; // do not convert non-alpha
return c.charCodeAt(0) - 'a'.charCodeAt(0) + 1;
};
// method to transform user-input into numbers
const transformInput = () => userInput
.split('') // split the string into array of individual letters
.map(c => convertChar(c)) // use the "convertChar" method
.join(' '); // join back to string with "space" seperator
// added a "space" only for rendering to UI
// method to find the sum/total of numbers
const getResult = () => userInput
.split('') // convert string to array of individual letters
.map(c => convertChar(c)) // transform a-z to numbers 1-26
.filter( // discard any characters except a-z, A-Z
c => (
typeof c !== 'string' && // check letter is no longer "string"
!isNaN(c) // and it is not "NaN"
)
)
.reduce( // add the numbers using ".reduce()"
(total, itm) => total + itm, 0
);
// return the JSX that will be rendered on the UI
// ----
// description of elements
// ^^^^^^^^^^^ ^^ ^^^^^^^^
// 1. label - "enter text" to prefix the input-box
// 2. input - to obtain the user input
// 3. "<p>" - paragraph elt to show a-z transformed to 1-26
// 4. button - submit button to calculate the sum
// 5. "<p>" - paragraphe elt to show the result of the summation
// -----
// NOTE: Items 4, 6 and conditionally rendered
return (
<div>
<div>
<label htmlFor={'usrinp'}>Enter text: </label>
<input
id="usrinp"
value={userInput}
onChange={handleChange}
/>
</div>
{
userInput.length > 0 &&
<p>Converted text to: {transformInput()}</p>
}
<button onClick={() => setShowResult(true)}>
Submit
</button>
{
showResult && <p>Result is: {getResult()}</p>
}
</div>
);
};
ReactDOM.render(
<div>
<h4>Transform User input text to numbers</h4>
<Thingy />
</div>,
document.getElementById("rd")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="rd" />
Explanation
Inline comments added to the snippet above.
you can do something like this
const dictionary = Object.fromEntries(Array(26).fill('a').map((s, i) => [String.fromCharCode(s.charCodeAt() + i), i + 1]))
export default function App() {
const [string, setString] = useState('')
const [numbers, setNumbers] = useState([])
const [total, setTotal] = useState(0)
const changeHandler = (e) => {
setString(e.target.value)
}
useEffect(() => {
setNumbers(
string.toLowerCase().split('').map(c => dictionary[c])
)
}, [string])
const calculateTotal = () => {
setTotal(numbers.reduce((a, b) => a + b, 0))
}
return (
<div className="App">
<div>{numbers.join(' ')}</div>
<input type="text" onChange={changeHandler} value={string}/><br/>
<button className="btn btn-primary"onClick={calculateTotal}>Click</button><br/>
{total > 0 && <div>the total is {total} }
</div>
);
}

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