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;
}
});
Related
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 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();
I have the following usage of my hook, but it doesn't use the new timerDuration when I update inside my input:
const [secondsBetweenRepsSetting, setSecondsBetweenRepsSetting] = useState(DEFAULT_SECONDS_BETWEEN_REPS)
const {secondsLeft, isRunning, start, stop} = useTimer({
duration: secondsBetweenRepsSetting,
onExpire: () => sayRandomExerciseName(),
onTick: () => handleTick(),
});
const onTimeBetweenRepsChange = (event: any) => {
const secondsBetweenRepsSettingString = event.target.value;
const secondsBetweenRepsSettingInt = parseInt(secondsBetweenRepsSettingString)
setSecondsBetweenRepsSetting(secondsBetweenRepsSettingInt)
}
return <React.Fragment>
<input type="number" name="secondsBetweenRepsSetting" value={secondsBetweenRepsSetting} onChange={onTimeBetweenRepsChange}/>
</React.Fragment>
And here is the implementation of the useTimer hook, which I'm not sure why it's not getting my duration update?
import { useState } from 'react';
import Validate from "../utils/Validate";
import useInterval from "./useInterval";
export default function useTimer({ duration: timerDuration, onExpire, onTick}) {
const [duration] = useState(timerDuration)
const [secondsLeft, setSecondsLeft] = useState(timerDuration)
const [isRunning, setIsRunning] = useState(false)
function start() {
setIsRunning(true)
}
function stop() {
setIsRunning(false)
}
function handleExpire() {
Validate.onExpire(onExpire) && onExpire();
}
useInterval(() => {
const secondsMinusOne = secondsLeft - 1;
setSecondsLeft(secondsMinusOne)
if(secondsMinusOne <= 0) {
setSecondsLeft(duration) // Reset timer automatically
handleExpire()
} else {
Validate.onTick(onTick) && onTick();
}
}, isRunning ? 1000 : null)
return {secondsLeft, isRunning, start, stop, }
}
Remove the line:
const [duration] = useState(timerDuration);
You are already getting duration from timerDuration, just use that.
How can I access the updated value of the state? I am getting updated values on UI but not while accessing state value on the fly.
const OTP = () => {
const [counter, setCounter] = useState(3);
const [someState, setSomeState] = useState("something");
useEffect(() => {
startCountdownTimer();
}, []);
const countdownTimerFunc = () => {
let value = counter; // <--- initial value always
// someState <--- initial value always
console.log('==== countdown: START====', counter);
if (value && value > 0) {
setCounter(prev => prev - 1); // Updating right value
} else {
console.log('==== countdown: STOP====');
}
};
const startCountdownTimer = () => {
setSomeState("updated something");
internalID = BackgroundTimer.setInterval(() => {
countdownTimerFunc();
}, 1000);
};
const counterUI = () => {
return (
<Text>{counter}</Text>
)
}
...
return (
<View>
{counterUI()}
</View>
)
};
export default OTP;
Update: What is the right structure to access the state? Is it only with useEffect or can we handle with custom hook which will return state? The difficulty which am seeing in my structure to access the value inside seperate functions.
Please replace below code and let me know
const OTP = () => {
const [counter, setCounter] = useState(3);
const [someState, setSomeState] = useState("something");
useEffect(() => {
if (counter > 0) {
countdownTimerFunc()
}else{
startCountdownTimer();
}
}, [counter]);
const countdownTimerFunc = () => {
let value = counter; // <--- initial value always
// someState <--- initial value always
console.log('==== countdown: START====', counter);
if (value && value > 0) {
setCounter(counter - 1); // Updating right value
} else {
console.log('==== countdown: STOP====');
}
};
const startCountdownTimer = () => {
setSomeState("updated something");
setTimeout(() => {
countdownTimerFunc();
}, 1000);
};
const counterUI = () => {
return (
<Text>{counter}</Text>
)
}
...
return (
<View>
{counterUI()}
</View>
)
};
export default OTP;
I'm building Carousel Component using useLayoutEffect. this useLayoutEffect hook has been set in resizeWindow.ts separately. and resizeWindow function is called in functional Component named carousel. I can't find where breaking rule is.
//resizeWindow.ts
import { useLayoutEffect, useState, RefObject } from 'react'
/***
* #function resizeWindow
* this function is custom hook for grab resizing innerWidth of element.
*
*
*/
export const resizeWindow: (ref: RefObject<HTMLElement>) => number[] = (ref) => {
const [ elementWidth, elementHeight ] = ref.current ?
[ref.current.offsetWidth, ref.current.offsetHeight ] :
[0,0];
const [size, setSize] = useState([elementWidth, elementHeight]);
useLayoutEffect(() => {
const updateSize = () => {
setSize([elementWidth, elementHeight]);
console.log(`elementWidth: ${elementWidth}px`);
};
updateSize();
window.addEventListener('resize', updateSize);
return () => window.removeEventListener('resize', updateSize);
},[]);
return size;
};
//carousel.ts
//
import { resizeWindow } from './resizeWindow.ts';
export const Carousel: FC = ({
children
}) => {
const parentRef = useRef<HTMLDivElement>(null);
const slideRef = createRef<HTMLDivElement>();
const [count, setCount ] = useState<number>(0);
const [parentWidth, setParentWidth] = resizeWindow(parentRef);
const total = React.Children.count(children);
const nextSlide = () => {
if( count < total -1 ){
setCount( count + 1 );
} else if( count === total-1 ){
setCount(1);
}
}
const prevSlide = () => {
if( count > 0 ){
setCount( count -1 );
} else if( count === 0 ){
setCount(total -1 );
}
}
useEffect(()=> {
console.log('parentRef: ', parentRef);
if(slideRef.current){
slideRef.current.style.transition = "all 0.5s ease-in-out";
slideRef.current.style.transform = `translateX(-${count}00%)`;
}
if(parentRef.current){
resizeWindow(parentRef);
}
},[count || parentWidth])
return(
<SliderContainer ref={parentRef}>
<Slider ref={slideRef} width={parentWidth * total}>
{children}
</Slider>
<Indicator now={1} total={total}/>
<Button onClick={prevSlide}>left</Button>
<Button onClick={nextSlide}>right</Button>
</SliderContainer>
)
}
resizeWindow is a custom hook, you should not be using it inside useEffect hook. This usage is what gives you an error.
Also you must name your custom hooks by prefixing their name with use
Also you must destructure ref properties within the updateSize function in resizeWindow hook so that you don't face the closure problem within updateSize function
The updated solution will look like
export const useResizeWindow: (ref: RefObject<HTMLElement>) => number[] = (ref) => {
const [size, setSize] = useState([elementWidth, elementHeight]);
useLayoutEffect(() => {
const updateSize = () => {
const [ elementWidth, elementHeight ] = ref.current ?
[ref.current.offsetWidth, ref.current.offsetHeight ] :
[0,0];
setSize([elementWidth, elementHeight]);
console.log(`elementWidth: ${elementWidth}px`);
};
updateSize();
window.addEventListener('resize', updateSize);
return () => window.removeEventListener('resize', updateSize);
},[]);
return size;
};
and its usage will be as follows
//carousel.ts
//
import { useResizeWindow } from './resizeWindow.ts';
export const Carousel: FC = ({
children
}) => {
const parentRef = useRef<HTMLDivElement>(null);
const slideRef = createRef<HTMLDivElement>();
const [count, setCount ] = useState<number>(0);
const [parentWidth, setParentWidth] = useResizeWindow(parentRef);
const total = React.Children.count(children);
const nextSlide = () => {
if( count < total -1 ){
setCount( count + 1 );
} else if( count === total-1 ){
setCount(1);
}
}
const prevSlide = () => {
if( count > 0 ){
setCount( count -1 );
} else if( count === 0 ){
setCount(total -1 );
}
}
useEffect(()=> {
console.log('parentRef: ', parentRef);
if(slideRef.current){
slideRef.current.style.transition = "all 0.5s ease-in-out";
slideRef.current.style.transform = `translateX(-${count}00%)`;
}
},[count || parentWidth])
return(
<SliderContainer ref={parentRef}>
<Slider ref={slideRef} width={parentWidth * total}>
{children}
</Slider>
<Indicator now={1} total={total}/>
<Button onClick={prevSlide}>left</Button>
<Button onClick={nextSlide}>right</Button>
</SliderContainer>
)
}