ARTICLE AD BOX
I am currently working on an application that has a large amount of duplicated logic shared across its components. One of the primary reasons for this is the components are using a Context component for a large amount of their state. I have been trying to factor out the logic in the components instead into thunks so it can be shared, but I am wondering how I am supposed to share the vales from useContext, useLocation and useHistory in the thunks. We need the logic shared, it updates large amounts of state, and calls apis so it's deferred logic, so a thunk seems to be the correct approach. This is logic that is meant to coordinate application data flow, not render components, so HoCs/Contexts seem like an anti-pattern in this case.
Here are some of the ideas I had, but I don't know which one is correct:
Use consts at top of file holding thunks
const location = useLocation(); const history = useHistory(); const context = useContext(Context); export const myThunk(param) { return (dispatch, getState) { //assume code like this is what is meant by "//my logic" elsewhere dispatch(someMethod()); let p1 = callSomeApi(location.someParameter).then(res => { dispatch(someReducerActionWithTheResult(res)); history.push('/someNewHistory'); dispatch(evenMoreLogic(res)); }); let p2 = callAnotherApi().then(res => ...); return Promise.all([p1, p2]); } }Call useX inside thunk
export const myThunk(param) { const location = useLocation(); const history = useHistory(); const context = useContext(Context); return (dispatch, getState) { //my logic } }Call useX inside dispatch inside thunk
export const myThunk(param) { return (dispatch, getState) { const location = useLocation(); const history = useHistory(); const context = useContext(Context); //my logic } }Pass in values derived from useX to the thunk
// some other file, probably a component const location = useLocation(); const history = useHistory(); const context = useContext(Context); // more code dispatch(myThunk(param, location, history, context)); // more code //the thunk file export const myThunk(param, location, history, context) { return (dispatch, getState) { //my logic } }Rewrite the context component as a reducer
Of course, this eliminates the useContext call, but it does not eliminate the useHistory or useLocation calls. This may be what I want to do anyway, but I would like to know how to handle those two cases here as well.
export const myThunk(param) { return (dispatch, getState) { const state = getState(); const context = selectContext(state); //todo: what about location and history??? //my logic } }Factor out the calls to location/history
export const myThunk(param, hooksThatCallLocationAndHistory) { return (dispatch, getState) { const state = getState(); const context = selectContext(state); //other logic hooksThatCallLocationAndHistory(someParams); } }Put all Location and History into a reducer call and save them as constants at the top of the file
const state = store.getState(); const location = selectLocation(state); const history = useHistory(state); const context = useContext(state); export const myThunk(param) { return (dispatch, getState) { //my logic } }For reference, here is the import statement for the useHistory and useLocation calls:
import { useHistory, useLocation } from 'react-router'More or less, I am trying to keep the codebase clean and adhere to best practices, but I don't know the considerations for using useHistory/Location/Context inside the context of dispatch calls. Since react's own documentation is highly opinionated -- it strongly suggests certain practices, like using thunks -- if there is an officially supported best practice, I would like to know about that. Otherwise, which of these approaches would work? Are there bugs or problems that result from any of these or can I just put the constants at the top of the file and nothing will ever go wrong with that?
