Reputation: 2019
Okay, I am experiencing some behaviour I don't really understand. I have this useState hook
const [permanent, setPermanent] = useState(false)
and this useEffect hook
useEffect(() => {
if (permanent) {
dispatch({ value: 'Permanent booth', key: 'period' })
} else {
dispatch({ value: '0', key: 'period' })
}
}, [permanent])
It triggers a rerender on initial render, and I do not call setPermanent
upon rendering my component, I have checked this both by commenting every single setPermanent
call out in my application. And I have also tried replacing it with a function that logs to the console.
//const [permanent, setPermanent] = useState(false)
const permanent = false
const setPermanent = () => {
console.log('I am called') //does not get called on initial render
}
I know it triggers a rerender because when I comment one of the second dispatch call in it out, it does not trigger the rerender.
useEffect(() => {
if (permanent) {
dispatch({ value: 'Permanent booth', key: 'period' })
} else {
//dispatch({ value: '0', key: 'period' })
}
}, [permanent])
Is there a reason for this, because I cannot seem to find documentation explaining this behaviour?
EDIT --------------
const shopOptions = (() => {
const options = [
{ label: 'Choose a shop', value: '0' },
]
Object.keys(stores).forEach(store => {
options[options.length] = { label: store, value: options.length }
})
return options
})()
const genderOptions = [
{ label: 'Choose a gender', value: '0' },
{ label: 'Female', value: '1' },
{ label: 'Male', value: '2' }
]
const periodOptions = [
{ label: 'Choose a period', value: '0' },
{ label: '1 week', value: '1' },
{ label: '2 weeks', value: '2' },
{ label: '3 weeks', value: '3' },
{ label: '4 weeks', value: '4' }
]
const initialState = {
shop: shopOptions[0],
gender: genderOptions[0],
period: periodOptions[0],
}
function reducer(prevState, { value, key }) {
const updatedElement = { ...prevState[key] }
updatedElement.value = value
return { ...prevState, [key]: updatedElement }
}
//form
const [state, dispatch] = useReducer(reducer, initialState)
Upvotes: 2
Views: 2357
Reputation: 569
This is my solution. Just a boolean property and ordering hook will do the job.
const _isMounted = React.useRef(false)
const [filter, setFilter] = React.useState('')
React.useEffect(() => {
if (_isMounted.current) {
getData(filter) // Now it will not get called very first time
}
}, [filter])
/* Order is important. [] or so called componentDidMount() will be the last */
React.useEffect(() => {
_isMounted.current = true
return () => {
_isMounted.current = false
}
}, [])
Upvotes: 0
Reputation: 126
useEffect
hooks run both after the first render and after every update of variables passed to the dependency array (in your case [permanent]
).
Because you have a boolean value that triggers the effect, it's hard to know whether it's the first render or a re-render within the effect. In your case I would consider not using a useEffect
here, and instead dispatching what you need while updating the state. For example:
const [permanent, setPermanent] = useState(false)
const makePermanent = () => {
setPermanent(true)
dispatch({ value: 'Permanent booth', key: 'period' })
}
const makeTemporary = () => {
setPermanent(false)
dispatch({ value: '0', key: 'period' })
}
Upvotes: 3