setIsLoading is an async function and you cannot get the state value immediately after update.
setState actions are asynchronous and are batched for performance gains. setState() does not immediately mutate this. Thus the setState calls are asynchronous as well as batched for better UI experience and performance. This applies on both
functional/Classcomponents.
From React documentation
React may batch multiple setState() calls into a single update for performance. Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state. You could read more about this here
If you want to get the updated state value then use useEffect hook with dependency array. React will execute this hook after each state update.
const {useEffect, useState } = React;
const App = (props) => {
const [isLoading, setIsLoading] = useState(false)
const buttonHandler = () => {
setIsLoading(current => !current)
}
useEffect( () => {
console.log(isLoading);
}, [isLoading]);
return (
<div>
<button onClick={buttonHandler} type="button">
Change
</button>
{isLoading? "Loading...": null}
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root">
loading.....
</div>
Answer from Sohail Ashraf on Stack OverflowsetIsLoading is an async function and you cannot get the state value immediately after update.
setState actions are asynchronous and are batched for performance gains. setState() does not immediately mutate this. Thus the setState calls are asynchronous as well as batched for better UI experience and performance. This applies on both
functional/Classcomponents.
From React documentation
React may batch multiple setState() calls into a single update for performance. Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state. You could read more about this here
If you want to get the updated state value then use useEffect hook with dependency array. React will execute this hook after each state update.
const {useEffect, useState } = React;
const App = (props) => {
const [isLoading, setIsLoading] = useState(false)
const buttonHandler = () => {
setIsLoading(current => !current)
}
useEffect( () => {
console.log(isLoading);
}, [isLoading]);
return (
<div>
<button onClick={buttonHandler} type="button">
Change
</button>
{isLoading? "Loading...": null}
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root">
loading.....
</div>
This is the expected behavior. You may want to use useEffect to access the latest value.
Here is a thread discussing the same issue: useState set method not reflecting change immediately
Hope this helps!
Is it a thing to set default state values using primitive type constructors when applicable? e.g.
// Boolean (defaults to false)
const [active, setActive] = useState(Boolean);
const [active, setActive] = useState(false);
// String (defaults to "")
const [searchTerm, setSearchTerm] = useState(String);
const [searchTerm, setSearchTerm] = useState("");
// Number (defaults to 0)
...Another developer who was learning React at the same time as me said I should do it this way and I pretty much have been doing it this way for the last 2 years.
I figured it helped me write more declarative code and never gave it much more thought until this week. I've been trying to find any information discussing this but haven't found a thing.
Is there any explanation for or against this that people can think of? Do many other people declare state like this?
What is const [isOpen, setOpen] = useState(false);
What is useState() in React?
why boolean variable is passed to useState() is always false? React hooks
Toggle true/false with useState hook
Videos
React hooks are a new way (still being developed) to access the core features of react such as state without having to use classes, in your example if you want to increment a counter directly in the handler function without specifying it directly in the onClick prop, you could do something like:
...
const [count, setCounter] = useState(0);
const [moreStuff, setMoreStuff] = useState(...);
...
const setCount = () => {
setCounter(count + 1);
setMoreStuff(...);
...
};
and onClick:
<button onClick={setCount}>
Click me
</button>
Let's quickly explain what is going on in this line:
const [count, setCounter] = useState(0);
useState(0) returns a tuple where the first parameter count is the current state of the counter and setCounter is the method that will allow us to update the counter's state. We can use the setCounter method to update the state of count anywhere - In this case we are using it inside of the setCount function where we can do more things; the idea with hooks is that we are able to keep our code more functional and avoid class based components if not desired/needed.
I wrote a complete article about hooks with multiple examples (including counters) such as this codepen, I made use of useState, useEffect, useContext, and custom hooks. I could get into more details about how hooks work on this answer but the documentation does a very good job explaining the state hook and other hooks in detail.
update: Hooks are not longer a proposal, since version 16.8 they're now available to be used, there is a section in React's site that answers some of the FAQ.
useState is one of build-in react hooks available in 0.16.7 version.
useState should be used only inside functional components. useState is the way if we need an internal state and don't need to implement more complex logic such as lifecycle methods.
const [state, setState] = useState(initialState);
Returns a stateful value, and a function to update it.
During the initial render, the returned state (state) is the same as the value passed as the first argument (initialState).
The setState function is used to update the state. It accepts a new state value and enqueues a re-render of the component.
Please note that useState hook callback for updating the state behaves differently than components this.setState. To show you the difference I prepared two examples.
class UserInfoClass extends React.Component {
state = { firstName: 'John', lastName: 'Doe' };
render() {
return <div>
<p>userInfo: {JSON.stringify(this.state)}</p>
<button onClick={() => this.setState({
firstName: 'Jason'
})}>Update name to Jason</button>
</div>;
}
}
// Please note that new object is created when setUserInfo callback is used
function UserInfoFunction() {
const [userInfo, setUserInfo] = React.useState({
firstName: 'John', lastName: 'Doe',
});
return (
<div>
<p>userInfo: {JSON.stringify(userInfo)}</p>
<button onClick={() => setUserInfo({ firstName: 'Jason' })}>Update name to Jason</button>
</div>
);
}
ReactDOM.render(
<div>
<UserInfoClass />
<UserInfoFunction />
</div>
, document.querySelector('#app'));
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
<div id="app"></div>
New object is created when setUserInfo callback is used. Notice we lost lastName key value. To fixed that we could pass function inside useState.
setUserInfo(prevState => ({ ...prevState, firstName: 'Jason' })
See example:
// Please note that new object is created when setUserInfo callback is used
function UserInfoFunction() {
const [userInfo, setUserInfo] = React.useState({
firstName: 'John', lastName: 'Doe',
});
return (
<div>
<p>userInfo: {JSON.stringify(userInfo)}</p>
<button onClick={() => setUserInfo(prevState => ({
...prevState, firstName: 'Jason' }))}>
Update name to Jason
</button>
</div>
);
}
ReactDOM.render(
<UserInfoFunction />
, document.querySelector('#app'));
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
<div id="app"></div>
Unlike the setState method found in class components, useState does not automatically merge update objects. You can replicate this behavior by combining the function updater form with object spread syntax:
setState(prevState => { // Object.assign would also work return {...prevState, ...updatedValues}; });
For more about useState see official documentation.
useEffect runs during the first render and all subsequent updates.
I believe you might need to explicitly perform setLadyDancing to true or false depending on the state that you want them to be, otherwise it will keep getting toggled based on its previous state.
Maybe this might work:
useEffect(() => {
if (isPlaying) {
audioElement.current.play();
setLadyDancing(true);
setCatDancing(true);
} else {
audioElement.current.pause();
setLadyDancing(false);
setCatDancing(false);
}
}, [isPlaying]);
Just use isPlaying state for images.
import { useEffect, useRef, useState } from 'react'
import styled from './Cat.module.scss'
import Swing from '../../assets/audio/GirlsLikeToSwing.mp3'
let dancingImg = ['https://i.gifer.com/P6XV.gif', 'https://i.gifer.com/DCy.gif']
const Cat = () => {
const audioElement = useRef()
const [isPlaying, setIsPlaying] = useState(false)
const toggleAudio = () => {
setIsPlaying((prevState) => !prevState)
}
return (
<div className={styled.headerContainer}>
<div className={styled.lady}>
{isPlaying ? <img src={dancingImg[0]} alt="" className={styled.ladyDancing} /> : null}
{isPlaying ? <img src={dancingImg[1]} alt="" className={styled.catDancing} /> : null}
</div>
<button onClick={toggleAudio}>Play</button>
<audio src={Swing} ref={audioElement}></audio>
</div>
)
}
export default Cat