useReducer and useContext Dispatch doesn't work in onClick function - reactjs

I'll spare you the broader context as it's pretty simple. Using React hooks, the first dispatch here fires automatically and works just fine, but the second one doesn't. Context is imported from another file, so I assume it's a (lowercase) context issue, but I don't know how to fix it?
const Component = props => {
const [, dispatch] = useContext(Context);
useEffect(() => {
document.title = state.title;
// eslint-disable-next-line react-hooks/exhaustive-deps
});
// this works
dispatch({ type: "UPDATE_TITLE", payload: "Not Click!" });
function onAddClick() {
// this doesn't work
dispatch({ type: "UPDATE_TITLE", payload: "CLICKED!" });
}
return (
<div>
<AddButton onClick={onAddClick} />
</div>
);
};
Here's the parent.
const Reducer = (state, action) => {
switch (action.type) {
case "UPDATE_TITLE":
state["title"] = action.payload;
return state;
default:
return state;
}
};
const initialState = {
title: "My Title"
};
export const Context = createContext(initialState);
const App = () => {
const [state, dispatch] = useReducer(Reducer, initialState);
return (
<Context.Provider value={[state, dispatch]}>
<Component />
</Context.Provider>
);
};
export default App;
Console logs fire in the correct reducer case in both cases, but only the one marked 'this works' will actually update the state properly, the other one fails silently.
Fixed: https://codesandbox.io/s/cranky-wescoff-9epf9?file=/src/App.js

I don't know what you trying to achieve by placing the dispatch outside controlled env (like an event or useEffect):-
// this works
dispatch({ type: "UPDATE_TITLE", payload: "Not Click!" });
// but it will run in infinite loop tho (no changes can be made then)
So the fixes should be:-
in Reducer, make sure not to completely mutate your state:-
const Reducer = (state, action) => {
switch (action.type) {
case "UPDATE_TITLE":
// Don't do this
// let newState = state;
// newState["page"]["title"] = action.payload;
// console.log("updating", newState, newState.page.title);
// return newState;
// Do this instead
return {
...state,
page: {
...state.page,
title: action.payload
}
};
default:
return state;
}
};
Place your dispatch for not Click! inside an event or function or better yet in this case, useEffect since you wanna apply it once the component rendered.
const Demo = props => {
const [state, dispatch] = useContext(Context);
useEffect(() => {
document.title = state.title;
// eslint-disable-next-line react-hooks/exhaustive-deps
// this works (should be here)
dispatch({ type: "UPDATE_TITLE", payload: "Not Click!" });
}, []); // run it once when render
// this works but, (should not be here tho - cause it will run non-stop)
// dispatch({ type: "UPDATE_TITLE", payload: "Not Click!" });
function onAddClick() {
// this will work now
dispatch({ type: "UPDATE_TITLE", payload: "CLICKED!" });
}
return (
<div>
<button onClick={onAddClick}>Click Me!</button>
<p>State now: {state.title}</p>
</div>
);
};
You can try and refer to this sandbox and see how it works.
EDITED & UPDATED sandbox

It looks like you are attempting to mutate state directly. Instead try to return a new object that is the result of the changes from the action applied to the old state.
const Reducer = (state, action) => {
switch (action.type) {
case "UPDATE_TITLE":
return {
...state,
page: {
...state.page,
title: action.payload
}
};
default:
return state;
}
};
Alternatively, use produce from immerjs to give you the ability to write your reducer in this mutable style.
import produce from "immer";
const Reducer = produce((state, action) => {
switch (action.type) {
case "UPDATE_TITLE":
state["title"] = action.payload;
break;
default:
return;
}
});

Related

React Context - why is initial value always undefined for 1 property while not for other values?

Im using react context (not redux).When I put an initial value in my reducer function, every value I pass is fine except 1 which is my notifications array. I pass empty array but initial value is always undefined. I just dont get what is different in 'notifications' vs any other value
const NotificationContext = createContext();
const reducer = (state, action) => {
switch (action.type) {
case 'fetch_notifications':
return {
...state, notifications: action.payload.messages
}
case 'try':
return {
...state, test: action.payload
}
default:
return state;
}
};
export const NotificationsProvider = ({children}) => {
const [state, dispatch] = useReducer(reducer, {notifications: [], test: "test",)
const tryme = () => {
dispatch({type: 'try', payload: "tryingme"})
}
const fetchNotifications = async () => {
const notifications = await API.fetchNotifications();
dispatch({type: 'fetch_notifications', payload: notifications})
};
return (
<NotificationContext.Provider
value={{
state,
fetchNotifications,
tryme
}}
>
{children}
</NotificationContext.Provider>
)
}

variable updated by redux state does not trigger useEffect

variable updated by redux state does not trigger useEffect
not sure what i am missing but i can see state.user.fen updating but it does not trigger useEffect to be called?
export default function BoardSquare({ piece, black, position,isFromSquare,isToSquare}) {
dispatch(setFen(fen))
}
//userActions.js
export const setFen = (fen) => (dispatch) => {
dispatch({
type: SET_FEN,
payload: fen,
});
}
//userReducer.js
export default function userReducer(state = initialState,action) {
switch(action.type){
case SET_FEN:
return{
...state,
fen: action.payload
}
}
function GameApp() {
const fen = useSelector(state => state.user.fen)
useEffect(() => {
alert("should be working now ?")
console.log("should be working now ??????")
setBoard(fen)
}, [fen])
}

Correct way to update a state inside useReducer (React.js)

I have two 'select' inputs (html) that work togueter to display info, this is the state:
const [foodValues, setFoodValues] = useState({ type: 'Everything', category: 'Everything' })
Everything works just fine, I'm using a classic handleChange() on both inputs:
const handleChange = e => {
const value = e.target.value
const name = e.target.name
dispatch({type: ACTIONS.CHANGE, payload: {e: value, name: name}})
}
As you can see, I call the dispatch to use the useReducer:
const OperationsReducer = (foodReducer, action) => {
const { foodValues, setFoodValues } = useContext(AllContext)
switch (action.type) {
case ACTIONS.CHANGE:
setFoodValues({
...foodValues,
[action.payload.name]: action.payload.e
})
break;
}
}
And it works just fine! But I'm getting this warning in the console:
warning Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks. You can only call Hooks at the top level of your React function. For more information, see
So, my question is: is there a way to use this useReducer without that warning? I know I could just use setFoodValues outside of it, but I'm curious (I really like using useReducer...). I already read the React docs on this topic, but I saw some people doing some really weird (and cool) to stuff to work around this.
const initialState = { type: 'Everything', category: 'Everything' };
function reducer(state, action) {
switch (action.type) {
case ACTIONS.CHANGE:
return {
...state,
[action.payload.name]: action.payload.e
};
default:
return state;
}
}
function Component() {
const [state, dispatch] = useReducer(reducer, initialState);
const handleChange = e => {
const value = e.target.value
const name = e.target.name
dispatch({type: ACTIONS.CHANGE, payload: {e: value, name: name}})
}
...
}

axios delete operation with use reducer in react js

i am working on crud operation with context api and use reducer. i fetch data from an api and store it as initial value. but now i am confused how to delete a user from my fetched list. i made a remove function which works fine on manual refresh but does not auto refresh and gives back an error. how to make a delete function.
const InitialState = {
Users: []
}
const Reducer = (state, action) =>
{
switch(action.type)
{
case 'FETCH_USERS':
return{
Users: action.payload
}
case 'REMOVE_USER':
return{
Users: action.payload
}
default:
return state
}
}
const GlobalContext = createContext(InitialState)
const GlobalState = ({children}) => {
const [state, dispatch] = useReducer(Reducer, InitialState);
useEffect(()=>
{
fecthapi();
},[])
const fecthapi = async () =>
{
const res = await axios.get('http://localhost:3002/users')
dispatch({type: 'FETCH_USERS', payload: res.data})
}
const Remove = async (id) =>
{
await axios.delete(`http://localhost:3002/users/${id}`)
dispatch({
type: 'REMOVE_USER', payload: id
})
}
return (
<>
<GlobalContext.Provider value = {{Users: state.Users, Remove: Remove}}>
{children}
</GlobalContext.Provider>
</>
)
}```

React useReducer async data fetch

I'am trying to fetch some data with new react useReducer API and stuck on stage where i need to fetch it async. I just don't know how :/
How to place data fetching in switch statement or it's not a way how it's should be done?
import React from 'react'
const ProfileContext = React.createContext()
const initialState = {
data: false
}
let reducer = async (state, action) => {
switch (action.type) {
case 'unload':
return initialState
case 'reload':
return { data: reloadProfile() } //how to do it???
}
}
const reloadProfile = async () => {
try {
let profileData = await fetch('/profile')
profileData = await profileData.json()
return profileData
} catch (error) {
console.log(error)
}
}
function ProfileContextProvider(props) {
let [profile, profileR] = React.useReducer(reducer, initialState)
return (
<ProfileContext.Provider value={{ profile, profileR }}>
{props.children}
</ProfileContext.Provider>
)
}
export { ProfileContext, ProfileContextProvider }
I was trying to do it like this, but it's not working with async ;(
let reducer = async (state, action) => {
switch (action.type) {
case 'unload':
return initialState
case 'reload': {
return await { data: 2 }
}
}
}
This is an interesting case that the useReducer examples don't touch on. I don't think the reducer is the right place to load asynchronously. Coming from a Redux mindset, you would typically load the data elsewhere, either in a thunk, an observable (ex. redux-observable), or just in a lifecycle event like componentDidMount. With the new useReducer we could use the componentDidMount approach using useEffect. Your effect can be something like the following:
function ProfileContextProvider(props) {
let [profile, profileR] = React.useReducer(reducer, initialState);
useEffect(() => {
reloadProfile().then((profileData) => {
profileR({
type: "profileReady",
payload: profileData
});
});
}, []); // The empty array causes this effect to only run on mount
return (
<ProfileContext.Provider value={{ profile, profileR }}>
{props.children}
</ProfileContext.Provider>
);
}
Also, working example here: https://codesandbox.io/s/r4ml2x864m.
If you need to pass a prop or state through to your reloadProfile function, you could do so by adjusting the second argument to useEffect (the empty array in the example) so that it runs only when needed. You would need to either check against the previous value or implement some sort of cache to avoid fetching when unnecessary.
Update - Reload from child
If you want to be able to reload from a child component, there are a couple of ways you can do that. The first option is passing a callback to the child component that will trigger the dispatch. This can be done through the context provider or a component prop. Since you are using context provider already, here is an example of that method:
function ProfileContextProvider(props) {
let [profile, profileR] = React.useReducer(reducer, initialState);
const onReloadNeeded = useCallback(async () => {
const profileData = await reloadProfile();
profileR({
type: "profileReady",
payload: profileData
});
}, []); // The empty array causes this callback to only be created once per component instance
useEffect(() => {
onReloadNeeded();
}, []); // The empty array causes this effect to only run on mount
return (
<ProfileContext.Provider value={{ onReloadNeeded, profile }}>
{props.children}
</ProfileContext.Provider>
);
}
If you really want to use the dispatch function instead of an explicit callback, you can do so by wrapping the dispatch in a higher order function that handles the special actions that would have been handled by middleware in the Redux world. Here is an example of that. Notice that instead of passing profileR directly into the context provider, we pass the custom one that acts like a middleware, intercepting special actions that the reducer doesn't care about.
function ProfileContextProvider(props) {
let [profile, profileR] = React.useReducer(reducer, initialState);
const customDispatch= useCallback(async (action) => {
switch (action.type) {
case "reload": {
const profileData = await reloadProfile();
profileR({
type: "profileReady",
payload: profileData
});
break;
}
default:
// Not a special case, dispatch the action
profileR(action);
}
}, []); // The empty array causes this callback to only be created once per component instance
return (
<ProfileContext.Provider value={{ profile, profileR: customDispatch }}>
{props.children}
</ProfileContext.Provider>
);
}
It is a good practice to keep reducers pure. It will make useReducer more predictable and ease up testability. Subsequent approaches both combine async operations with pure reducers:
1. Fetch data before dispatch (simple)
Wrap the original dispatch with asyncDispatch and let context pass this function down:
const AppContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initState);
const asyncDispatch = () => { // adjust args to your needs
dispatch({ type: "loading" });
fetchData().then(data => {
dispatch({ type: "finished", payload: data });
});
};
return (
<AppContext.Provider value={{ state, dispatch: asyncDispatch }}>
{children}
</AppContext.Provider>
);
// Note: memoize the context value, if Provider gets re-rendered more often
};
const reducer = (state, { type, payload }) => {
if (type === "loading") return { status: "loading" };
if (type === "finished") return { status: "finished", data: payload };
return state;
};
const initState = {
status: "idle"
};
const AppContext = React.createContext();
const AppContextProvider = ({ children }) => {
const [state, dispatch] = React.useReducer(reducer, initState);
const asyncDispatch = () => { // adjust args to your needs
dispatch({ type: "loading" });
fetchData().then(data => {
dispatch({ type: "finished", payload: data });
});
};
return (
<AppContext.Provider value={{ state, dispatch: asyncDispatch }}>
{children}
</AppContext.Provider>
);
};
function App() {
return (
<AppContextProvider>
<Child />
</AppContextProvider>
);
}
const Child = () => {
const val = React.useContext(AppContext);
const {
state: { status, data },
dispatch
} = val;
return (
<div>
<p>Status: {status}</p>
<p>Data: {data || "-"}</p>
<button onClick={dispatch}>Fetch data</button>
</div>
);
};
function fetchData() {
return new Promise(resolve => {
setTimeout(() => {
resolve(42);
}, 2000);
});
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
<div id="root"></div>
2. Use middleware for dispatch (generic)
dispatch might be enhanced with middlewares like redux-thunk, redux-observable, redux-saga for more flexibility and reusability. Or write your own one.
Let's say, we want to 1.) fetch async data with redux-thunk 2.) do some logging 3.) invoke dispatch with the final result. First define middlewares:
import thunk from "redux-thunk";
const middlewares = [thunk, logger]; // logger is our own implementation
Then write a custom useMiddlewareReducer Hook, which you can see here as useReducer bundled with additional middlewares, akin to Redux applyMiddleware:
const [state, dispatch] = useMiddlewareReducer(middlewares, reducer, initState);
Middlewares are passed as first argument, otherwise API is the same as useReducer. For the implementation, we take applyMiddleware source code and carry it over to React Hooks.
const middlewares = [ReduxThunk, logger];
const reducer = (state, { type, payload }) => {
if (type === "loading") return { ...state, status: "loading" };
if (type === "finished") return { status: "finished", data: payload };
return state;
};
const initState = {
status: "idle"
};
const AppContext = React.createContext();
const AppContextProvider = ({ children }) => {
const [state, dispatch] = useMiddlewareReducer(
middlewares,
reducer,
initState
);
return (
<AppContext.Provider value={{ state, dispatch }}>
{children}
</AppContext.Provider>
);
};
function App() {
return (
<AppContextProvider>
<Child />
</AppContextProvider>
);
}
const Child = () => {
const val = React.useContext(AppContext);
const {
state: { status, data },
dispatch
} = val;
return (
<div>
<p>Status: {status}</p>
<p>Data: {data || "-"}</p>
<button onClick={() => dispatch(fetchData())}>Fetch data</button>
</div>
);
};
function fetchData() {
return (dispatch, getState) => {
dispatch({ type: "loading" });
setTimeout(() => {
// fake async loading
dispatch({ type: "finished", payload: (getState().data || 0) + 42 });
}, 2000);
};
}
function logger({ getState }) {
return next => action => {
console.log("state:", JSON.stringify(getState()), "action:", JSON.stringify(action));
return next(action);
};
}
// same API as useReducer, with middlewares as first argument
function useMiddlewareReducer(
middlewares,
reducer,
initState,
initializer = s => s
) {
const [state, setState] = React.useState(initializer(initState));
const stateRef = React.useRef(state); // stores most recent state
const dispatch = React.useMemo(
() =>
enhanceDispatch({
getState: () => stateRef.current, // access most recent state
stateDispatch: action => {
stateRef.current = reducer(stateRef.current, action); // makes getState() possible
setState(stateRef.current); // trigger re-render
return action;
}
})(...middlewares),
[middlewares, reducer]
);
return [state, dispatch];
}
// | dispatch fn |
// A middleware has type (dispatch, getState) => nextMw => action => action
function enhanceDispatch({ getState, stateDispatch }) {
return (...middlewares) => {
let dispatch;
const middlewareAPI = {
getState,
dispatch: action => dispatch(action)
};
dispatch = middlewares
.map(m => m(middlewareAPI))
.reduceRight((next, mw) => mw(next), stateDispatch);
return dispatch;
};
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux-thunk/2.3.0/redux-thunk.min.js" integrity="sha256-2xw5MpPcdu82/nmW2XQ6Ise9hKxziLWV2GupkS9knuw=" crossorigin="anonymous"></script>
<script>var ReduxThunk = window.ReduxThunk.default</script>
Note: we store intermediate state in mutable refs - stateRef.current = reducer(...), so each middleware can access current, most recent state at the time of its invocation with getState.
To have the exact API as useReducer, you can create the Hook dynamically:
const useMiddlewareReducer = createUseMiddlewareReducer(middlewares); //init Hook
const MyComp = () => { // later on in several components
// ...
const [state, dispatch] = useMiddlewareReducer(reducer, initState);
}
const middlewares = [ReduxThunk, logger];
const reducer = (state, { type, payload }) => {
if (type === "loading") return { ...state, status: "loading" };
if (type === "finished") return { status: "finished", data: payload };
return state;
};
const initState = {
status: "idle"
};
const AppContext = React.createContext();
const useMiddlewareReducer = createUseMiddlewareReducer(middlewares);
const AppContextProvider = ({ children }) => {
const [state, dispatch] = useMiddlewareReducer(
reducer,
initState
);
return (
<AppContext.Provider value={{ state, dispatch }}>
{children}
</AppContext.Provider>
);
};
function App() {
return (
<AppContextProvider>
<Child />
</AppContextProvider>
);
}
const Child = () => {
const val = React.useContext(AppContext);
const {
state: { status, data },
dispatch
} = val;
return (
<div>
<p>Status: {status}</p>
<p>Data: {data || "-"}</p>
<button onClick={() => dispatch(fetchData())}>Fetch data</button>
</div>
);
};
function fetchData() {
return (dispatch, getState) => {
dispatch({ type: "loading" });
setTimeout(() => {
// fake async loading
dispatch({ type: "finished", payload: (getState().data || 0) + 42 });
}, 2000);
};
}
function logger({ getState }) {
return next => action => {
console.log("state:", JSON.stringify(getState()), "action:", JSON.stringify(action));
return next(action);
};
}
function createUseMiddlewareReducer(middlewares) {
return (reducer, initState, initializer = s => s) => {
const [state, setState] = React.useState(initializer(initState));
const stateRef = React.useRef(state); // stores most recent state
const dispatch = React.useMemo(
() =>
enhanceDispatch({
getState: () => stateRef.current, // access most recent state
stateDispatch: action => {
stateRef.current = reducer(stateRef.current, action); // makes getState() possible
setState(stateRef.current); // trigger re-render
return action;
}
})(...middlewares),
[middlewares, reducer]
);
return [state, dispatch];
}
}
// | dispatch fn |
// A middleware has type (dispatch, getState) => nextMw => action => action
function enhanceDispatch({ getState, stateDispatch }) {
return (...middlewares) => {
let dispatch;
const middlewareAPI = {
getState,
dispatch: action => dispatch(action)
};
dispatch = middlewares
.map(m => m(middlewareAPI))
.reduceRight((next, mw) => mw(next), stateDispatch);
return dispatch;
};
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux-thunk/2.3.0/redux-thunk.min.js" integrity="sha256-2xw5MpPcdu82/nmW2XQ6Ise9hKxziLWV2GupkS9knuw=" crossorigin="anonymous"></script>
<script>var ReduxThunk = window.ReduxThunk.default</script>
More infos - external libraries: react-use, react-hooks-global-state, react-enhanced-reducer-hook
I wrote a very detailed explanation of the problem and possible solutions. Dan Abramov suggested Solution 3.
Note: The examples in the gist provide examples with file operations but the same approach could be implemented for data fetching.
https://gist.github.com/astoilkov/013c513e33fe95fa8846348038d8fe42
Update:
I’ve added another comment in the weblink below. It’s a custom hook called useAsyncReducer based on the code below that uses the exact same signature as a normal useReducer.
function useAsyncReducer(reducer, initState) {
const [state, setState] = useState(initState),
dispatchState = async (action) => setState(await reducer(state, action));
return [state, dispatchState];
}
async function reducer(state, action) {
switch (action.type) {
case 'switch1':
// Do async code here
return 'newState';
}
}
function App() {
const [state, dispatchState] = useAsyncReducer(reducer, 'initState');
return <ExampleComponent dispatchState={dispatchState} />;
}
function ExampleComponent({ dispatchState }) {
return <button onClick={() => dispatchState({ type: 'switch1' })}>button</button>;
}
Old solution:
I just posted this reply here and thought it may be good to post here as well in case it helps anyone.
My solution was to emulate useReducer using useState + an async function:
async function updateFunction(action) {
switch (action.type) {
case 'switch1':
// Do async code here (access current state with 'action.state')
action.setState('newState');
break;
}
}
function App() {
const [state, setState] = useState(),
callUpdateFunction = (vars) => updateFunction({ ...vars, state, setState });
return <ExampleComponent callUpdateFunction={callUpdateFunction} />;
}
function ExampleComponent({ callUpdateFunction }) {
return <button onClick={() => callUpdateFunction({ type: 'switch1' })} />
}
I wrapped the dispatch method with a layer to solve the asynchronous action problem.
Here is initial state. The loading key record the application current loading status, It's convenient when you want to show loading page when the application is fetching data from server.
{
value: 0,
loading: false
}
There are four kinds of actions.
function reducer(state, action) {
switch (action.type) {
case "click_async":
case "click_sync":
return { ...state, value: action.payload };
case "loading_start":
return { ...state, loading: true };
case "loading_end":
return { ...state, loading: false };
default:
throw new Error();
}
}
function isPromise(obj) {
return (
!!obj &&
(typeof obj === "object" || typeof obj === "function") &&
typeof obj.then === "function"
);
}
function wrapperDispatch(dispatch) {
return function(action) {
if (isPromise(action.payload)) {
dispatch({ type: "loading_start" });
action.payload.then(v => {
dispatch({ type: action.type, payload: v });
dispatch({ type: "loading_end" });
});
} else {
dispatch(action);
}
};
}
Suppose there is an asynchronous method
async function asyncFetch(p) {
return new Promise(resolve => {
setTimeout(() => {
resolve(p);
}, 1000);
});
}
wrapperDispatch(dispatch)({
type: "click_async",
payload: asyncFetch(new Date().getTime())
});
The full example code is here:
https://codesandbox.io/s/13qnv8ml7q
it is very simple
you can change state in useEffect after async Fuction result
define useState for result of fetch
const [resultFetch, setResultFetch] = useState(null);
and useEffect for listen to setResultFetch
after fetch async API call setResultFetch(result of response)
useEffect(() => {
if (resultFetch) {
const user = resultFetch;
dispatch({ type: AC_USER_LOGIN, userId: user.ID})
}}, [resultFetch])

Resources