How can I promptly update the number state and watch console.log(number) updated value?
const [number,setNumber] = useState(0);
const minus = () => {
setNumber(number-1);
console.log(number);
}
return (
<>
<div>{number}</div>
<button onClick={minus}>-</button>
</>
)
What you are trying to do is a side-effect: print something onto the console.
This is what useEffect hook is for - it lets you perform side effects.
So here is a possible implementation:
function App() {
const [number, setNumber] = useState(0);
const minus = () => {
setNumber(number - 1);
};
useEffect(() => {
console.log(number);
}, [number]);
return (
<>
<div>{number}</div>
<button onClick={minus}>-</button>
</>
);
}
Of course, it may be an overkill solution if you are just using console.log for debugging purpose. If that's the case, #zynkn and #deepak-k's answers work just fine.
Try this
setNumber((number)=> {number-1 ; console.log(number)});
const [number,setNumber] = useState(0);
const minus = () => {
// setNumber(number-1);
// It is also work well but it is working with async.
// So you can't see the below console.log().
// console.log(number);
setNumber((prevNumber) => {
newNumber = prevNumber - 1;
console.log(newNumber);
return newNumber;
});
}
return (
<>
<div>{number}</div>
<button onClick={minus}>-</button>
</>
)
Related
Hello
I am working on a timer in React to understand how Hooks works, and so far everything is ok except the start button (in my case the timer starts automatically and start button should be use with pause). I can't figure how to resolve this problem with these hooks.
const { useRef, useState, useEffect } = React;
function Minuteur() {
const intervalRef = useRef();
const [timer, setTimer] = useState(30);
useEffect(() => {
const id = setInterval(() => {
setTimer((oldTimer) => oldTimer - 1);
}, 1000);
intervalRef.current = id;
}, []);
const stopTimer = () => {
clearInterval(intervalRef.current);
};
const resetTimer = () => {
setTimer(30)
};
const playTimer = () => {
};
return (
<div>
<p>Il reste : {timer} secondes</p>
<button onClick={playTimer}> PLAY! </button>
<button onClick={stopTimer}> STOP! </button>
<button onClick={resetTimer}> RESET! </button>
</div>
);
Codepen
function Minuteur() {
// Définition de la référence
const intervalRef = useRef();
const [timer, setTimer] = useState(30);
const [timerRunning, setTimerRunning] = useState(false); // I added a state for if the timer should be running or not
useEffect(() => {
let interval = null;
if (timerRunning) { // Check if the timer is running
interval = setInterval(() => {
setTimer(timer => timer - 1);
}, 1000);
}
return () => clearInterval(interval);
}, [timerRunning]); // rerun side effect when timerRunning changes
// Fonction permettant d'arrêter le ‘timer’
const stopTimer = () => {
setTimerRunning(false) // Set running to false
};
const resetTimer = () => {
setTimer(30);
stopTimer();
};
const playTimer = () => {
setTimerRunning(true); // set running to true
};
...
}
Edit: Everything in the [] dependency array at the end of the useEffect hook is what the side effect "watches". So by adding the timerRunning to the dependency array the useEffect hook will watch for the timerRunning and when it changes, it will cause the hook to re-render. If it is an empty array then it will only ever run on the initial load. That is why your timer started on refresh.
As we all know that we need to make a subscription in useeffect and unsubscribe it when the component will unmount. But this kind of code will be triggered once the component is mounted. I'm now want to trigger the subscription after a specific action.Look at the code below.
const [timing, setTiming] = useState<number>(60)
const interval$ = interval(1000)
useEffect(() => {
})
const sendCodeOnceSubmit = async (phone: number) => {
const res = await sendCode(phone)
if (res.code !== 200) {
message.error(`${res.message}`)
} else {
interval$.pipe(take(60)).subscribe(() => setTiming(timing - 1))
}
}
I have a form in the dom,and once I click submit,the sendCodeOnceSubmit function will be triggered which will then send a request through sendCode function to the server. Once the server return a success code, I want to make a countdown with rxjs, but how can I unsubscribe it cause the normal way to do it is to subscribe a observable in useeffect. Thanks for anyone who can help.
Just wrap interval$ with useState and write a useEffect for it.
// Moved const out of component.
const defaultTiming = 60;
/* ... */
export default function App() {
const [timing, setTiming] = useState<number>(defaultTiming);
const [interval$, setInterval$] = useState<Observable<number> | undefined>();
useEffect(() => {
if (!interval$) return;
const subscription = interval$.pipe(take(defaultTiming)).subscribe(() => {
setTiming((prev) => prev - 1);
});
return () => subscription.unsubscribe();
}, [interval$]);
const sendCodeOnceSubmit = async (phone: number) => {
const res = await sendCode(phone);
if (res.code !== 200) {
// message.error(`${res.message}`);
console.error(res.message);
} else {
setInterval$(interval(1000));
}
};
return (
<div className="App">
<p>{timing}</p>
<button type="button" onClick={() => sendCodeOnceSubmit(123)}>
Click
</button>
</div>
);
}
I've got problem with my first react app.
I've set the interval function which counts down from 10 to 0 and after the 0 is reached the interval is cleared. At least it should work like this, but when I console log the time it's always 10 (even though it renders properly in the browser - the value is getting smaller), so it never jumps to the else statement.
What should I do to fix this problem?
const {useState} = React;
const Timer = () => {
let flag = true;
const [time, setTime] = useState(10);
const handleClick = () => {
if (flag) {
setInterval(counter, 500);
}
}
const counter = () => {
if (time > 0) {
console.log(time);
setTime(time => time - 1);
} else {
console.log('out');
clearInterval(timer);
}
}
return(
<div>
<div>{time}</div>
<button className="start" onClick={handleClick}>START</button>
</div>
)
}
ReactDOM.render(
<Timer />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
I managed to solve this problem. Thank you guys for trying to help :) Snippet below:
const {useState} = React;
const {useEffect} = React;
const Timer = () => {
const [flag, setFlag] = useState(false);
const [time, setTime] = useState(10);
const handleClick = () => {
setFlag(!flag);
}
useEffect(() => {
function counter () {
if (time > 0) {
setTime(time => time - 1)
}
}
if (flag) {
console.log('a');
const interval = setInterval(counter, 1000)
return () => clearInterval(interval);
}
}, [flag, time]);
return(
<div>
<div>{time}</div>
<button className="start" onClick={handleClick} >START</button>
</div>
)
}
ReactDOM.render(
<Timer />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
To summarize what I want to do:
Update the state depending on the previous state
I have searched in vain for a solution to the above problems. Found 3 solutions, unfortunately without any success.
1)
const Form = (props) => {
const [newValue, setNewValue] = useState(0);
const submitHandler = (e) => {
e.preventDefault();
const incrementOne = {
value: setNewValue((prevState) => {
return {...prevState, newValue: newValue + 1}
})
};
console.log(incrementOne);
};
const submitHandler = (e) => {
e.preventDefault();
const incrementOne = {
value: setNewValue(newValue + 1),
};
console.log(incrementOne);
};
3
const submitHandler = (e) => {
e.preventDefault();
const incrementOne = {
value: setNewValue(prevState => prevState + 1),
};
console.log(incrementOne);
};
Thank you in advance for your time and effort
Sincerely
/ Peter
In all your examples you are creating an object with a value property. You assume that is supposed to get it's value from calling set function returned by useState. However, the result of calling this function is updating the state, and re-rendering. The function itself doesn't return anything (undefined).
const incrementOne = {
value: setNewValue((prevState) => {
return {...prevState, newValue: newValue + 1}
})
};
You should call the setNewValue function when you want to update the value. You can calculate the new state using the previous one:
setNewValue(newValue + 1);
Or use a functional update to avoid depending on the state directly:
setNewValue(prevState => prevState + 1);
Note that the new value is only available after the component re-renders.
Example:
const { useState } = React;
const Form = (props) => {
const [newValue, setNewValue] = useState(0);
const submitHandler = () => {
setNewValue(prevState => prevState + 1);
};
const incrementOne = {
value: newValue,
};
console.log(incrementOne);
return (
<div>
<div>{newValue}</div>
<button onClick={submitHandler}>Submit</button>
</div>
);
}
ReactDOM.render(
<Form />,
root
)
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="root"></div>
a start-up component can new a class and set values of some public properties in it to be used later in methods of same class.
What will be the equivalent of this in a hook component? Should these properties be added to a context and read from there? Is there a simpler option?
Thank you
If this are constant properties, you can define consts inside or outside the component.
const noOfItems = 5
const Comp = () => {
const items = 'cats'
return (
<div>{noOfItems} {items }></div>
)
}
If the values can change, and changing them causes re-render, then you should use useState or useReducer:
const Comp = ({ items }) => {
const [noOfItems, setNoOfItems] = useState(0)
return (
<>
<div>{noOfItems} {items}></div>
<button onClick={() => setNoOfItems(x => x + 1)}>
Add Items
</button>
</>
)
}
And if it's something you wish to mutate, but changing it won't cause re-render, you can do so with useRef:
const Comp = ({ items, doSomething }) => {
const [noOfItems, setNoOfItems] = useState(0)
const fn = useRef() // fn would be preserved between renders, and mutating it won't cause re-renders
useEffect(() => {
fn.current = doSomething // changing this won't cause a re-render
});
useEffect(() => {
fn.current(noOfItems)
}, [noOfItems])
return (
<>
<div>{noOfItems} {items}></div>
<button onClick={() => setNoOfItems(x => x + 1)}>
Add Items
</button>
</>
)
}
Although not public, you can can also keep variables (and consts) inside a closure - for example useEffect, as long as it's not invoked again:
const Comp = ({ items }) => {
const [noOfItems, setNoOfItems] = useState(0)
useEffect(() => {
const interval = setInterval(() => /* do something */) // the interval would preserved in the closure at it ins't called again
return () => clearInterval(interval)
}, [])
return (
<>
<div>{noOfItems} {items}></div>
<button onClick={() => setNoOfItems(x => x + 1)}>
Add Items
</button>
</>
)
}