I have been working with useReducer()
so that I can share some state among some components. There is a property in the requestReducer
called isLoading
which default/initial state is set to false
. There are two functions dispatching actions to the reducer (initializeLoading
: changing isLoading to true
, and getRequestResponse
: changing isLoading back to false
). But when the function that the reducer uses to initialize loading -initializeLoading
-, is called by CallingComponent
that uses the pointer of those functions to call them; the change of state inside the useEffect
(which has a dependency for the requestState.isLoading
), is never displayed in the console or called neither in the useCurrentRequest
hook nor the CallingComponent
, when it is supposed to be called; because in that first calling the isLoading
prop, from false
, turns into true
, through the SEND
action.type
. Moreover when calling the getRequestResponse
function (which launches a RESPONSE
action.type
=> isLoading
= false
) from the CallingComponent
, it should switch from true
, to false
, thereby it should be displayed also by the useEffect
that is supposed to capture those changes. But nothing of that happens, instead nothing is displayed.
Here is the useCurrentRequest
and the requestReducer
:
import {useReducer, useCallback, useEffect} from 'react';
export const initialState = {
isLoading: false,
data: null,
}
const requestReducer = (state = initialState, action) => {
switch (action.type) {
case 'SEND':
return {...state,
isLoading: true,
};
case 'RESPONSE':
return {...state,
isLoading: false,
data: action.responseData
};
default:
return state;
}
}
//This is the hook with the logic that sets state using the reducer
const useCurrentRequest = () => {
const [requestState, dispatchRequestActions] = useReducer(requestReducer, initialState);
//The loading change of state is not displayed in the console
useEffect(() => {
console.log(requestState.isLoading);
}, [requestState.isLoading]);
const initializeLoading = useCallback(() => {
dispatchRequestActions({type: 'SEND'});
console.log(requestState);
},[]);
const getRequestResponse = useCallback(() => {
//some call to an api happens here
//Then the response is given using the reducer
dispatchRequestActions({type: 'RESPONSE', responseData: {someData:true}});
}, []);
return {
initializeLoadingPointer: initializeLoading,
getRequestResponsePointer: getRequestResponse,
isLoading: weatherRequestState.isLoading,
data: weatherRequestState.data,
}
}
export default useCurrentRequest;
And here is the CallingComponent
, which uses the function pointers provided by the useCurrentRequest
hook:
import {useEffect} from "react";
import useCurrentRequest from "../hooks/useCurrentRequest";
const CallingComponent = props => {
const {initializeLoadingPointer, getRequestResponsePointer, isLoading} = useCurrentWeather();
const getData = () => {
initializeLoadingPointer();
getRequestResponsePointer();
};
useEffect(() => {
console.log(isLoading);
}, [isLoading]);
return (<button onClick={getData}>Get data</button>)
}
The problem basically is that nothing is displayed after the Get Data
button is clicked, when there is supposed to display in the console, true
and false
in that respective order, because of the useEffect()
depending on isLoading
and those functions changing isLoading
, but again nothing is displayed.
I will appreciate any help on this.
Thanks! :)
I finally solved the issue by using async-await, the problem was that since the requests to those functions that were modifying the reducer were happening synchronously, the state was only updated until both of them were finished, and the function that was calling them has already finished too. So what I did was to block some pieces of code using promises via de async-await mode. Basically, I only modified useCurrentRequest()
hook by replacing those 2 functions by this:
const useCurrentRequest = () => {
const [requestState, dispatchRequestActions] = useReducer(requestReducer, initialState);
useEffect(() => {
console.log(requestState.isLoading);
}, [requestState.isLoading]);
const sendRequestAsync = useCallback(async (city) => {
const query = `weather?q=${city}`;
console.log(query);
dispatchRequestActions({ type: "SEND" });
try {
const result = await fakeRequest();
dispatchRequestActions({ type: "RESPONSE", responseData: result.data });
} catch (error) {
dispatchRequestActions({ type: "ERROR", errorMessage: error.message });
}
}, []);
const fakeRequest = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ data: "data" });
}, 1000);
});
};
return {
getRequestResponsePointer: sendRequestAsync,
isLoading: weatherRequestState.isLoading,
data: weatherRequestState.data,
}
}
export default useCurrentRequest;
That way I was able to see the changes using the useEffect()
hook.