From 84fdd9bbfeb05e0dd68077cc0e8d068013b61651 Mon Sep 17 00:00:00 2001 From: Dennis Kigen Date: Sat, 3 Jul 2021 23:29:04 +0300 Subject: [PATCH] Manage view states with useReducer --- src/components/app.js | 81 +++++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 29 deletions(-) diff --git a/src/components/app.js b/src/components/app.js index 222caab..30395f3 100644 --- a/src/components/app.js +++ b/src/components/app.js @@ -13,16 +13,41 @@ const apiUrl = process.env.REACT_APP_API_URL; const apiKey = process.env.REACT_APP_API_KEY; const searchTimeoutInMs = 1000; +function viewStateReducer(state, action) { + switch (action.type) { + case 'error': + return { + ...state, + status: 'rejected', + error: action.error, + }; + case 'weather_success': + return { + ...state, + status: 'resolved', + weather: action.weather, + }; + case 'forecast_success': + return { + ...state, + status: 'resolved', + forecast: action.forecast, + }; + default: + throw new Error(`Unhandled action type: ${action.type}`); + } +} + const App = () => { const [location, setLocation] = React.useState('Eldoret'); const [debouncedSearchTerm, setDebouncedSearchTerm] = React.useState(''); const [isSearching, setIsSearching] = React.useState(false); const [units, setUnits] = React.useState('metric'); - const [viewState, setViewState] = React.useState({ + const [state, dispatch] = React.useReducer(viewStateReducer, { status: 'idle', error: null, - weather: {}, forecast: [], + weather: {}, }); const debounceSearch = React.useMemo( @@ -58,18 +83,17 @@ const App = () => { try { const weather = await fetchWeather(location, units); - setViewState((state) => ({ - ...state, - status: 'resolved', - weather: weather, - error: null, - })); + if (weather) { + dispatch({ + type: 'weather_success', + weather: weather, + }); + } } catch (err) { - setViewState((state) => ({ - ...state, - status: 'rejected', + dispatch({ + type: 'error', error: err, - })); + }); } } @@ -82,18 +106,17 @@ const App = () => { try { const forecast = await fetchForecast(location, units); - setViewState((state) => ({ - ...state, - status: 'resolved', - forecast: forecast, - error: null, - })); + if (forecast) { + dispatch({ + type: 'forecast_success', + forecast: forecast, + }); + } } catch (err) { - setViewState((state) => ({ - ...state, - status: 'rejected', + dispatch({ + type: 'error', error: err, - })); + }); } } @@ -106,7 +129,8 @@ const App = () => { - {viewState.status === 'rejected' ? ( + {state.status === 'idle' ? : null} + {state.status === 'rejected' ? (
{ fillRule="evenodd" /> - {viewState?.error?.message} + {state.error.message}
) : null} - {viewState.status === 'idle' ? : null} - {(viewState.weather && Object.keys(viewState.weather).length) || - (viewState.forecast && Object.keys(viewState.forecast).length) ? ( + {(state.weather && Object.keys(state.weather).length) || + (state.forecast && Object.keys(state.forecast).length) ? (
{ onLocationChange={handleLocationChange} />