I always see this type of code:
const [value, setValue] = useState(null);
since value can be changed using setValue, why are we using const for value.
What const means is that the value for that identifier in that scope cannot be reassigned. You can't do
const x = 5;
x = 6;
or
function foo(arg) {
const theArg = arg;
theArg = 5;
}
But there's nothing stopping you from running the function again, resulting in a new binding for that variable:
function foo(arg) {
const theArg = arg;
}
foo(5);
foo(6);
The above is what React is doing - it runs the whole function component again, which results in useState returning the updated state. No const identifier ever gets reassigned.
Another example:
let i = 0;
const useState = () => [i, newI => { i = newI; TheComponent(); }];
const TheComponent = () => {
const [i, setI] = useState();
console.log('component running with i of', i);
setTimeout(() => {
setI(i + 1);
}, 1000);
};
TheComponent();
Related
I have a variable in a component. And I want the value of the variable returned by the function SetScore()
btnRotate.jsx
setScoreState((prevS) => {
if (btn.deg == deg) {
//console.log(prevS);
return prevS + 1;
} else {
//console.log(prevS);
return prevS;
}
});
I want to use the value of the variable prevS here
Img.jsx
function Img({ i, lengthArr }) {
const [width, setWidth] = useState(0);
const [score1, setScore1] = useState(0);
const [score2, setScore2] = useState(0);
///
const [scoreState, setScoreState] = useState(1);
//console.log(prevS);
...
You can use useRef to keep previous state values
import { useRef, useState } from 'react'
function Img({ i, lengthArr }) {
const [width, setWidth] = useState(0);
const [score1, setScore1] = useState(0);
const [score2, setScore2] = useState(0);
const [scoreState, setScoreState] = useState(1);
const scoreStateRef = useRef(1); //add `useRef`
console.log(scoreStateRef.current); //replace for `prevS`
...
Here is how to set values for scoreStateRef
setScoreState((prevS) => {
scoreStateRef.current = prevS; //set value here
if (btn.deg == deg) {
//console.log(prevS);
return prevS + 1;
} else {
//console.log(prevS);
return prevS;
}
});
I have a webapi invoked that is working properly:
const [pItem, setPItem] = useState([]);
const [weight, setWeight] = useReducer(weightHandler, 0.0);
useEffect(() => {
setLoading(true);
let mounted = true;
(async function () {
await getPlantInfoById(itemId)
.then(item => {
if (mounted) {
setPItem(item)
setLoading(false);
}
})
})();
return () => { mounted = false; }
}, [itemId])
Here pItem contains data now I have another filled called weight(which can be changed by a user) .
So I need some calculations according to the weight changes:
const PaymentCalculator = function () {
const [item] = [...pItem];
const priceWithDiscount = DiscountCalc(item.price, item.discount);
const divideWeight = weight / item.weight;
const result = (divideWeight * priceWithDiscount) * 1000;
return result;
}
const use = useMemo(() => PaymentCalculator(), [weight])
But it seems PaymentCalculator invoked before useEffect !!
How can I fix this?
If you examine the contents of paymentCalculator you'll see you've more than just weight as a dependency.
const PaymentCalculator = function () {
const [item] = [...pItem];
const priceWithDiscount = DiscountCalc(item.price, item.discount);
const divideWeight = weight / item.weight;
const result = (divideWeight * priceWithDiscount) * 1000;
return result;
}
pItem is also a dependency!
Initially pItem is an empty array, and since all hooks are called on each render cycle, this would mean that item is undefined on the initial render and accessing item.price and item.discount will throw an error for attempting to "access X of undefined".
Add pItem to the dependency array and provide a fallback value.
const paymentCalculator = function() {
const [item = {}] = [...pItem];
const priceWithDiscount = discountCalc(item.price, item.discount);
const divideWeight = weight / item.weight;
const result = (divideWeight * priceWithDiscount) * 1000;
return result;
}
...
const use = useMemo(() => PaymentCalculator(), [pItem, weight]);
I am new to using React Hooks and was wondering if it was possible to have useEffect() fire only for specific values of state. Basically I have a simple tic tac toe game and I want the function I have to handle the CPU's turn be called after the human player has made their turn, in other words to essentially call handleCPUTurn() after handleClick() completes the new render.
From what I have already tried, I can either get handleCPUTurn to just once, or infinitely until the game ends. below is the code I have relating to handling turns.
const Game = () => {
const [history, setHistory] = useState([Array(9).fill(null)]);
const [stepNumber, setStepNumber] = useState(0);
const [xIsNext, setXIsNext] = useState(true);
const handleCPUTurn = () => {
const pointInHistory = history.slice(0, stepNumber + 1);
const current = pointInHistory[stepNumber];
const squares = [...current];
let randomSqaure = Math.floor(Math.random() * 8);
if (calculateWinner(squares)) return;
while(true){
if (squares[randomSqaure]) {
randomSqaure = Math.floor(Math.random() * 8);
continue;
}
else {
squares[randomSqaure] = 'O';
setHistory([...pointInHistory, squares]);
setStepNumber(pointInHistory.length);
setXIsNext(xIsNext);
break;
}
}
}
const handleClick = (i) => {
const pointInHistory = history.slice(0, stepNumber + 1);
const current = pointInHistory[stepNumber];
const squares = [...current];
if (calculateWinner(squares) || squares[i]) {
return;
}
squares[i] = 'X';
setHistory([...pointInHistory, squares]);
setStepNumber(pointInHistory.length);
setXIsNext(!xIsNext);
}
useEffect(() => (
handleCPUTurn(),[xIsNext]));
You can achieve this by a condition inside useEffect. As I can see you are already passing xIsNext as the dependency in useEffect. So it will call every time when value of xIsNext changes.
What you can do is: PUT A CONDITION
useEffect(() => {
if (!xIsNext) {
handleCPUTurn();
}
} ,[xIsNext]);
This will run handleCPUTurn only when xIsNext=false
I'm trying to setup an onKeyPress event listener and I'm confused as to why the initial value is undefined and then the value I want. The data is added on mount (see x in console). Why am I unable to immediately capture it and instead get an initial undefined, especially since it clearly already exists in state?
useEffect(() => {
console.log('x', multipleChoice); <-- logs the array of objects
const handleKeyPress = ({ key }) => {
const index = Number(key) - 1;
if (key === '1') {
console.log(multipleChoice[index]); <-- logs undefined, then logs object
}
};
window.addEventListener('keydown', (e) => handleKeyPress(e));
return () => {
window.removeEventListener('keydown', (e) => handleKeyPress(e));
};
}, [allCards, currentCard, multipleChoice]);
LocalState
const [currentCard, setCard] = useState(0);
const [multipleChoice, setMultipleChoice] = useState([]);
// allCards is passed as a prop on page load from the parent
When the user guesses an answer correctly the currentCard is incremented by 1
UseEffect that sets multipleChoice
useEffect(() => {
const generateMultipleChoice = (words: Word[]) => {
const possibleAnswers = words.reduce(
(accum: Word[]) => {
while (accum.length < 4) {
// randomly select words from pool
const index = getRandomInt(0, allCards.length - 1);
const randomWord = allCards[index];
// verify current hand doesn't already have that word
if (!accum.includes(randomWord)) {
accum.push(randomWord);
}
}
return accum;
},
// default with the current card already in the hand
[allCards[currentCard]]
);
// return the hand with the matching card and (3) other cards from pool
return possibleAnswers;
};
const shuffledCards = shuffle(generateMultipleChoice(allCards));
setMultipleChoice(shuffledCards);
}, [allCards, currentCard]);
screenshot of console
This is it:
// initial state
const [multipleChoice, setMultipleChoice] = useState([]);
// therefore, initially, if index is any Number
console.log(multipleChoice[index]) // undefined
The object is returned, only until the calculation is finished...
useEffect(() => {
// ...
// this has to run before `multipleChoice` is updated to return that object
const shuffledCards = shuffle(generateMultipleChoice(allCards));
setMultipleChoice(shuffledCards);
}, [allCards, currentCard]);
I need to initialize a state array with six elements, points, and then update the proper element in the points array when setVote function is called:
const [selected, setSelected] = useState(0)
const setNextItem = (value) => setSelected(value)
const initialValue = new Array(6).fill(0);
const [points, setPoint] = useState(initialValue)
const setVote = (value) => setPoint(value)
// randomly choose the next item
const setNextitem(Math.floor((Math.random() * 5) + 0))
const setVote => {
const copy = [...points]
copy[selected] += 1
return copy;
}
However, when I view points and copy arrays in the console window, points never gets updated with copy. It stay as Array(6) [ 0, 0, 0, 0, 0, 0 ]. Is it always initializing or is the copy array not retuned. What is the correct way to do this?
Here is my final code:
```
const App = () => {
const [selected, setSelected] = useState(0)
const [points, setPoints] = useState(new Array(6).fill(0))
const setNextItem = (value) => setSelected(value)
const setVoteAt = (value) => setPoints(value)
console.log(selected, points)
const next_button = {
name : 'next item',
onClick: () => setNextItem(Math.floor((Math.random() * 5) + 0))
}
const vote_button = {
name : 'vote',
onClick: () => setVoteAt(UpdatePoints(selected))
}
const UpdatePoints = (index) => {
let copy = [...points]
copy[index]++
return copy
}
```
Too many duplicated function and state
Just a little order the code..
react useState is not rective, then you can do useEffect to console.
And then you will see the update array.
const [points, setPoints] = useState(new Array(6).fill(0));
const UpdatePoints = (selected) => {
let copy = [...points];
copy[selected]++;
setPoints(copy);
}
const random = () => Math.floor((Math.random() * 5) + 0)
useEffect(() => console.log(points))
return (
<div onClick={() => UpdatePoints(random())}> ClickMe </div>
);
It looks like it should work for the most part. I'd say these two functions are conflicting, and causing an issue. Are there any errors returned in the console?
const setVote = (value) => setPoint(value)
const setVote => {
const copy = [...points]
copy[selected] += 1
return copy;
}
This last one you may want to rename. Something like setVoteAt which take an index parameter for which vote to set.
const setVoteAt = (index) => {
const copy = [...points]
copy[index] += 1
return copy;
}
setVoteAt(2) // [0, 0, 1, 0, 0, 0]
How to correctly initialize a state array React JS
Use useEffect to initialize it properly. This is similar to componentDidMount.
const [points, setPoints] = useState([])
useEffect(() =>
{
const initialValue = new Array(6).fill(0);
setPoints(initialValue);
}
, []) // initialize only once (on mount) and not on every render
Is it always initializing or is the copy array not retuned. What is the correct way to do this?
setVote() is not doing things correctly. You can update points every time selected is changed by:
useEffect(() =>
{
setPoints(points => {
points[selected] += 1
return points
})
}
, [selected] // only run this effect every time selected changed
)
setNextItem(2) // this will update selected then run the effect on top
// and also increment points[selected]