State variable change forces the whole component to render. Without useState() We can see likes value change in alert, but not on the screen, because a function component runs and show updates only if its state variable is updated.
function ComponentWithoutState() {
let likes = 0
return (
<>
<div>Likes <b>{likes}</b></div>
<button onClick={() => likes++} style={btnCss}>+1</button>
<button onClick={() => likes--} style={btnCss}>-1</button>
<button onClick={() => alert(likes)} style={btnCss}>Alert <b>likes</b></button>
</>
)
}
With useState()
import React from 'react'
function ComponentWithState() {
const [likes, setLikes] = React.useState(0)
return (
<>
<div>Likes <b>{likes}</b></div>
<button onClick={() => setLikes(likes + 1)} style={btnCss}>+1</button>
<button onClick={() => setLikes(likes - 1)} style={btnCss}>-1</button>
<button onClick={() => alert(likes)} style={btnCss}>Alert <b>likes</b></button>
</>
)
}
State init Initialize likes state by const [likes, setLikes] = React.useState(0) setLikes is a function to change a state value Pass a new state value as an argument setLikes(likes + 1) Initial state 0 is passed as an argument in useState(0) setState(newValue) If we update a state, we need to provide a new value, otherwise React doesn't trigger a render. Just provide a new primitive value or new reference for an object. State object mutation State update does not trigger a render Even if the state is really updated, which is visible in alert
import React from 'react'
function StateMutation() {
const [state, setState] = React.useState([1, 2, 3])
const mutateState = () => {
state.push(state.at(-1) + 1)
setState(state)
}
const alertState = () => alert(JSON.stringify(state))
return (
<>
<div>State value: <b>{JSON.stringify(state)}</b></div>
<button onClick={mutateState}>Add value to array by mutation</button> 
<button onClick={alertState}>Alert state</button>
</>
)
}
New state object To let render happen we need to provide new state.
function StateUpdate() {
const [state, setState] = React.useState([1, 2, 3])
const updateState = () => setState([...state, state.at(-1) + 1])
const alertState = () => alert(JSON.stringify(state))
return (
<>
<div>State value: <b>{JSON.stringify(state)}</b></div>
<button onClick={updateState}>Add value to array by update</button> 
<button onClick={alertState}>Alert state</button>
</>
)
}
State update with same value If a state is set to the same value via setState(sameVal) render does not happen.
function UpdateStateSameAndDifferentValue() {
const [state, setState] = useState(0)
const updateStateToSameValue = () => setState(state)
const updateStateToNewValue = () => setState(state + 1)
return (
<>
<div>Value: <b>{state}</b></div>
<button onClick={updateStateToSameValue}>Update state to same value</button> 
<button onClick={updateStateToNewValue}>Update state to new value</button>
</>
)
}
Update state from a child component To update a parent component's state from a child component, the update function setState should be passed via props to a child component.
const style = { border: '2px solid grey', padding: '10px', margin: '10px', maxWidth: '500px' }
function ParentWithState() {
const [state, setState] = React.useState(0)
const updateState = () => setState(state + 1)
return (
<div style={style}>
<h1>Parent Component</h1>
<div>Count state variable: <b>{state}</b></div>
<button onClick={updateState}>Update state from parent component</button>
<Child state={state} setState={setState} />
</div>
)
}
function Child(props) {
const { state, setState } = props
const updateState = () => setState(state + 1)
return (
<div style={style}>
<h1>Child Component</h1>
<button onClick={updateState}>Update state from child component</button>
</div>
)
}
State previous value When we update a state with setState(newValue) function, it is done asynchronously sometime in the future and we can not rely on updated state value in calculations. Async nature of setState() For example we increment the state 5 times with for (let i = 0; i < 5; i++) setState(state + 1) 5 calls go into the end of microtask queue remembering current state value currentValue = 0 . When they are executed in future they all will return 0 setState(prevVal => prevVal + 1) There is the second version of setState() function with callback, which provides access to the current state value at the moment of function execution.
function SetStateWithPreviousValue() {
const [state, setState] = useState(0)
const addOne5TimesAsync = () => {
for (let i = 0; i < 5; i++) setState(state + 1)
}
const addOne5TimesSync = () => {
for (let i = 0; i < 5; i++) setState(prevVal => prevVal + 1)
}
return (
<>
<h3>{state}</h3>
<button onClick={addOne5TimesAsync}>Async increment 5 times</button> 
<button onClick={addOne5TimesSync}>Sync increment 5 times</button>
</>
)
}