Skip to content

Improve router handlers architecture in Coach #11219

@MisRob

Description

@MisRob

Blocks

#11422

Observed behavior

For majority of Coach pages, in addition to fetching data in their individual route handlers (e.g. showExamsPage handler), we fetch some of their data in the global beforeRoute handler, which is run before individual route handlers:

if (
to.name &&
!['CoachClassListPage', 'StatusTestPage', 'CoachPrompts', 'AllFacilitiesPage'].includes(
to.name
)
) {
promises.push(this.store.dispatch('initClassInfo', to.params.classId));
} else {
this.store.dispatch('coachNotifications/stopPolling');
}
if (this.store.getters.isSuperuser && this.store.state.core.facilities.length === 0) {
promises.push(this.store.dispatch('getFacilities').catch(() => {}));
}
if (promises.length > 0) {
Promise.all(promises).then(next, error => {
this.store.dispatch('handleApiError', { error });
});
} else {
next();
}

This approach has the following problems:

1. Slows down navigation (user-facing)
Particularly observable on slower networks when after clicking a coach page, until all global beforeRoute promises are finished, user is not even redirected to a new page.

coach-loading

2. Limits individual pages ability to control when and how to display loading states (user-facing)
Many pages have their individual handlers that are sometimes connected to local loading states, however there is no way to factor in waiting time for this global beforeRoute which often seem to take even more time than promises in individual handlers, causing inaccuracy and discrepancy of loading states.

3. It's difficult to build a mental picture of what requests are performed when a page is visited (developer-facing)
Promises in the global beforeRoute are run for majority of pages except of few pages listed here

!['CoachClassListPage', 'StatusTestPage', 'CoachPrompts', 'AllFacilitiesPage'].includes(

Then, many pages have their separate handlers that have no information or control over the common logic, and are placed in different files.

Expected behavior

  • Users are redirected immediately after navigating to another page and appropriate loaders are displayed on the page
  • Individual pages have information on what data is being fetched, when it starts, ends and can control loading states in a flexible and simple manner

Steps to reproduce

Navigate Coach main pages and tabs on slower network

Guidance

To be able to achieve the expected behavior, we need to move promises from the global beforeRoute handler

router.beforeEach((to, from, next) => {

to individual page handlers.

For example, for showExamsPage page handler the steps could be:

  1. Search the codebase to locate the name of a page that uses showExamsPage: PageNames.EXAMS

name: PageNames.EXAMS,
path: '/:classId/plan/quizzes',
component: CoachExamsPage,
handler(toRoute) {
showExamsPage(store, toRoute.params.classId);
},
meta: {
titleParts: ['quizzesLabel', 'CLASS_NAME'],
},
},
{

  1. Add a condition to the global beforeRoute handler instructing it to not run code containing promises fetching data if user is navigating to a page with name PageNames.EXAMS

  2. And instead, copy all those promises to showExamsPage

  • This needs to be done for all individual page handlers in Coach. In this manner, it can be done gradually. I would recommend opening one pull request for one handler so we can review and QA loading states easily.

  • After all individual handlers are updated to have the promises, we can finally remove the promises completely from the global beforeRoute handlers.

Acceptance criteria

  • The global beforeRoute handler doesn't contain any promises fetching data
  • The promises are instead in individual page handlers
  • No regressions in regards to fetching data for Coach pages
  • No regressions in the existing loading states

Tasks

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions