-
Notifications
You must be signed in to change notification settings - Fork 23
Redux
TAPP uses redux as a state manager for the frontend. The redux store that keeps track of the application state has two main objects: ui
and model
. Below is a screenshot of what the redux store looks like:
model
: This object contains data that is fetched from the api backend. Each child within this object holds data from a single model class and contains the field _modelData
, which is a list of the fetched data. This allows all of the api data to reside in one location, so that if there are multiple components that all deal with the same mode class, they don’t need to individually manage their own api data. For example, below is an image where the the redux store holds 2 sessions fetched from the backend:
ui
: This object contains data that manages the ui state of various components. Each child within this object holds the ui data for a single connected component/view. Generally, data that is not from the api backend but is necessary for rendering the ui is stored in this store. Things that are stored here can be search box queries and rows that are selected in a table.
Redux stores keep a record of the state of our application. Whenever a change occurs, we need to update the store to represent the new state. Redux stores can only be updated through actions and reducers. Actions are objects that contain a string field type
, which describes what the action is, and an object field payload
, which contains information about how the state has changed. Reducers are functions that takes an action and a state as arguments, and returns a new state. Whenever a user performs an action that changes the model or the ui, such as creating a new position or expanding a ui element, we need to dispatch an action so that the reducer can generate a new state.
The actions and reducers for the model
store is handled slightly differently from the actions and reducers for the ui
store. All of the actions and reducers that manage api data is stored in src/api/
. Within each of the files in the actions directory, we define a set of action generator functions with actionFactory
and a set of dispatchers. Action generators are functions that return an action in a standardized format. Dispatchers are functions that perform some logic, and then dispatches an action based on the result. Let’s take a look at the action file applicant.js:
// actions
const fetchApplicantsSuccess = actionFactory(FETCH_APPLICANTS_SUCCESS);
const fetchOneApplicantSuccess = actionFactory(FETCH_ONE_APPLICANT_SUCCESS);
const upsertOneApplicantSuccess = actionFactory(UPSERT_ONE_APPLICANT_SUCCESS);
const deleteOneApplicantSuccess = actionFactory(DELETE_ONE_APPLICANT_SUCCESS);
// dispatchers
export const fetchApplicants = validatedApiDispatcher({
name: "fetchApplicants",
description: "Fetch applicants",
onErrorDispatch: e => fetchError(e.toString()),
dispatcher: () => async (dispatch, getState) => {
const { id: activeSessionId } = getState().model.sessions.activeSession;
const data = await apiGET(`/sessions/${activeSessionId}/applicants`);
dispatch(fetchApplicantsSuccess(data));
}
});
Here, we see a list of action generator functions defined on lines 2-5. fetchApplicantsSuccess
for example is a function that that takes api response data and returns an action of type
“FETCH_APPLICANTS_SUCCESS”. The dispatcher fetchApplicants
is the function that actually performs the api call that fetches the applicant data from the backend, and then passes this data into the fetchApplicantsSuccess
action generator. The returned action is then dispatched to the reducer that manages the applicants
store in the model
store.
In the reducers directory, each file defines a reducer for each of the model
stores. The reducer receives the actions dispatched by the dispatchers in the actions files, and returns a state with an updated _modelData
field that reflects the action. The reducers are then exported and imported by the rootReducer. The rootReducer aggregates all of the reducers from both the model
store and the ui
store, and sets up the final complete redux store.