Last active
October 30, 2022 17:55
-
-
Save codingwithchris/4c1713bd69884e4591dd5b7272d7252e to your computer and use it in GitHub Desktop.
App State Reducer Context Example -- Easily manage the state of a small application by passing userReducer into Context.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { createContext, useContext, useReducer, Dispatch } from 'react'; | |
/** | |
* Manages the state for our entire app | |
* | |
** For larger, more complex applications, this pattern can be split into multiiple contexts. | |
** For example: `form`, and `user` would have theor own contexts, each with their own reducer. | |
*/ | |
// Init an empty context | |
export const AppStateContext = createContext({} as AppStateContext); | |
/** | |
* Define default application state | |
*/ | |
export const defaultState: AppState = { | |
view: 'initial', | |
form: { | |
processing: false, | |
error: false, | |
errorMessage: '', | |
}, | |
test: { | |
result: undefined, | |
number: undefined, | |
resultText: undefined, | |
resultExplanation: undefined, | |
}, | |
user: { | |
loggedIn: false, | |
}, | |
}; | |
/** | |
* Build a simple reducer to handle state updates | |
*/ | |
const stateReducer = (state: AppState, action: StateAction): AppState => { | |
switch (action.type) { | |
case 'REQUEST_PROCESSING': | |
return { | |
...state, | |
form: { | |
error: false, | |
processing: true, | |
errorMessage: '', | |
}, | |
}; | |
case 'RESULTS_FOUND': | |
return { | |
...state, | |
view: 'results', | |
test: action.test, | |
form: { | |
error: false, | |
processing: false, | |
errorMessage: '', | |
}, | |
}; | |
case 'RESULTS_NOT_FOUND': | |
case 'RATE_LIMIT_REACHED': | |
case 'UNKNOWN_REQUEST_ERROR': | |
return { | |
...state, | |
form: { | |
processing: false, | |
error: true, | |
errorMessage: action.errorMessage, | |
}, | |
}; | |
case 'RESET_STATE': | |
return { | |
...state, | |
}; | |
case 'LOGOUT_USER': | |
return { | |
...state, | |
}; | |
default: | |
return { | |
...state, | |
}; | |
} | |
}; | |
/** | |
* Put useReducer into state so we can use it to read from state and | |
* dispatch actions seamlessly across out entire app. | |
*/ | |
export const AppStateProvider: React.FC = ({ children }) => { | |
const [state, dispatch] = useReducer(stateReducer, defaultState); | |
return ( | |
<AppStateContext.Provider value={{ state, dispatch }}> | |
{children} | |
</AppStateContext.Provider> | |
); | |
}; | |
/** | |
* Make our context with state and dispatch globally available as a simple hook | |
*/ | |
export const useAppStateContext = () => useContext(AppStateContext); | |
/** | |
* Typings | |
*/ | |
export interface AppState { | |
view: 'initial' | 'results'; | |
form: { | |
processing: boolean; | |
error: boolean; | |
errorMessage: string; | |
}; | |
test: { | |
result: 'positive' | 'negative' | 'pending' | undefined; | |
number: string | undefined; | |
resultText: string | undefined; | |
resultExplanation: string | undefined; | |
}; | |
user: { | |
loggedIn: boolean; | |
}; | |
} | |
type StateAction = | |
| { type: 'RESULTS_FOUND'; test: AppState['test'] } | |
| { | |
type: 'REQUEST_PROCESSING' | 'RESET_STATE' | 'LOGOUT_USER'; | |
} | |
| { | |
type: | |
| 'RESULTS_NOT_FOUND' | |
| 'RATE_LIMIT_REACHED' | |
| 'UNKNOWN_REQUEST_ERROR'; | |
errorMessage: string; | |
}; | |
interface AppStateContext { | |
state: AppState; | |
dispatch: Dispatch<StateAction>; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment