Can anyone help me to update state with timeout in react reducer.
I don't have much experience even with pure javascript, so I can hardly find an answer myself at this moment.
In my first ever react app (with useContex and useReducer) i have simple BUTTON checkbox with onClick function to dispatch type in reducer:
<ToggleButton
className="mb-2"
id="Getdocs"
type="checkbox"
variant="outline-secondary"
size="sm"
checked={Getdocs}
onChange={(e) => Getdocsaction()}
>
Render documents
</ToggleButton>
In my context.js i have:
import React, { useContext, useReducer} from 'react'
import reducer from './reducer'
const AppContext = React.createContext()
const initialState = {
.
.
.
Showdocs: false,
.
.
.
}
const AppProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState)
...
const Getdocsaction = () => {
dispatch({ type: 'GET_DOCS' })
}
...
return (
<AppContext.Provider
value={{
...state,
Getdocsaction
}}
>
{children}
</AppContext.Provider>
)
}
export const useGlobalContext = () => {
return useContext(AppContext)
}
export { AppContext, AppProvider }
In reducer.js i have:
const reducer = (state, action) => {
if (action.type === 'GET_DOCS') {
let newPassports = state.oldDocs.filter((doc) => doc.passport === true);
if (newPassports.length === 0) {
state.Passports = []
state.Showdocs = true
state.Getdocs = false /uncheck checkbox button
setTimeout(() => {
state.Showdocs = false //wont update this
console.log("setTimeout fired") //logs in console after 5 secs
}, 5000)
return { ...state }
}
if (newPassports.length !== 0) {
return { ...state, Passports: newPassports, Showdocs: true, Getdocs: !state.Getdocs }
}
return { ...state }
}
throw new Error('no matching action type')
}
export default reducer
Finally, in my App.js i check if Showdocs is true or false and return the rest (return the passports from updated array or bootstrap alert if there is no values in array (Passport.length === 0) )
What i am trying to achieve is that when i have empty Passports array i want set Showdocs: true (in order to show alert msg) and set it back to false after 5 secs (in order to remove msg ...)
Any help is welcome and even keywords by which i could research this issue. Thank you.
CodePudding user response:
I think you should implement an action that basically updates the state with this state.Showdocs = false
and then dispatch this action inside a setTimeout
.
So basically change Getdocsaction
to this:
const Getdocsaction = () => {
dispatch({ type: 'GET_DOCS' })
setTimeout(() => {dispatch({type: 'The action that sets Showdocs to false'})}, 5000);
}
CodePudding user response:
Reducers are intended to be “pure” and synchronous, and they shouldn't mutate input arguments. Since mutating state after a delay is a side-effect, you should consider instead handling this in a useEffect
hook separately.
E.g.:
const SomeComponent = () => {
const [state, dispatch] = useReducer(reducer)
const { hideDocsAfterDelay } = state
useEffect(() => {
if (!hideDocsAfterDelay) return
const timer = setTimeout(() => {
dispatch({ TYPE: "HIDE_DOCS" })
}, 5000)
return () => { clearTimeout(timer) }
}, [hideDocsAfterDelay])
// …
}
In this scenario, you would set a hideDocsAfterDelay
property in your state to trigger the timer and another action handler that would set showDocs
and hideDocsAfterDelay
to false
.