I actually have 2 components that require data from a reducer that has some state there. The CurrentWeather component shows the weather at a very high level and the CurrentWeatherDetailed in a detailed way. The first one is rendered first, and if clicked on Details it will render CurrentWeatherDetailed. CurrentWeather dispatches an action to fetch some data and update the state with that data, later it gets the state through useReducer and it renders the data from that state as expected. On the other hand, CurrentWeatherDetailed, also tries to get the state that is currently populated, but it just returns null(which is the initialState) instead of what is actually in the state, considering that CurrentWeather, I assume; has already changed the state. So I have tried a lot but I have not found an explanation of why this happens.
This is the reducer:
export const initialState = {
loading: false,
error: null,
data: null,
}
const weatherRequestReducer = (state = initialState, action) => {
switch (action.type) {
case 'SEND':
return {...state,
loading: true,
error: null,
data: null,
};
case 'RESPONSE':
console.log(state);
console.log(action.responseData);
return {...state,
loading: false,
data: action.responseData
};
case 'ERROR':
return {...state,
loading: false,
error: action.errorMessage,
data: null
};
case 'CLEAR-ERROR':
return {...state,
error:null
};
default:
return state;
// throw new Error('Should not get there')
}
}
export default weatherRequestReducer;
And here is a hook that uses the reducer to generate a state and return it to other components:
import {useReducer, useCallback, useState, useEffect} from 'react';
import weatherRequestReducer from "../reducers/weatherRequestReducer";
import {initialState} from "../reducers/weatherRequestReducer";
import api from '../utils/WeatherApiConfig'
const useCurrentWeather = () => {
const [weatherRequestState, dispatchWeatherRequestActions] = useReducer(weatherRequestReducer, initialState);
const sendRequestToGetCurrentWeather = useCallback(() => {
dispatchWeatherRequestActions({type: 'SEND'});
const uri = 'weather?q=London,uk';
api.get(uri)
.then(res => {
const responseData = res.data;
console.log(responseData.base);
dispatchWeatherRequestActions({type: 'RESPONSE', responseData: responseData});
})
.catch(err => {
console.log(err);
dispatchWeatherRequestActions({type: 'ERROR', errorMessage: 'Something went wrong!'});
});
}, []);
return {
getCurrentWeatherPointer: sendRequestToGetCurrentWeather,
isLoading: weatherRequestState.loading,
error: weatherRequestState.error,
data: weatherRequestState.data,
}
}
export default useCurrentWeather;
Here is the App component calling the CurrentWeather and CurrentWeatherDetailed, depending on a dummy state:
import CurrentWeather from "./components/CurrentWeather/CurrentWeather";
import CurrentWeatherDetailed from "./components/CurrentWeather/CurrentWeatherDetailed";
import {useState} from "react";
import weatherRequestReducer from "./reducers/weatherRequestReducer";
function App() {
const [state, setState] = useState(0);
const renderComponent = () => {
setState(1)
}
let comp = <CurrentWeather renderComponent={renderComponent}/>;
if(state === 1){
comp = <CurrentWeatherDetailed/>
}
return {comp}
}
export default App;
The following component is working fine and console.log(data), shows the actual data that has been asked for.
import React, {useEffect} from 'react';
import useCurrentWeather from "../../hooks/useCurrentWeather";
const CurrentWeather = (props) => {
const {getCurrentWeatherPointer, isLoading, error, data} = useCurrentWeather();
useEffect(() => {
getCurrentWeatherPointer();
}, [getCurrentWeatherPointer])
useEffect(() => {
console.log(data);
}, [data]);
const displayDetails = () => {
props.renderComponent();
}
return ( <Some JSX>
<div className={classes.buttonContainer}>
<button onClick={displayDetails}>Details →</button>
</div>
<Some JSX> );
}
export default CurrentWeather;
Here is the component that is failing, because is getting data as null instead of what has been already fetched by the CurrentWeather component:
import React, {useEffect} from 'react';
import useCurrentWeather from "../../hooks/useCurrentWeather";
const CurrentWeatherDetailed = (props) => {
const {getCurrentWeatherPointer, isLoading, error, data} = useCurrentWeather();
useEffect(() => {
console.log(data)
setTimeout(() => {console.log(data);}, 500 );
}, [data])
return ( <Some JSX> );
}
export default CurrentWeatherDetailed;
If I just add useEffect(() => {getCurrentWeatherPointer();},[getCurrentWeatherPointer]) to CurrentWeatherDetailed it works fine as CurrentWeather, but, I do not want to do that because I already loaded that from the API to the reducer state, so it does not make sense to load that again, but to use what is already there when calling this detailed component.
Does anyone know why is this happening?
Thanks in advance guys.