Skip to content

Commit

Permalink
feat: Provided the ability to set a custom error hook in RouterStore
Browse files Browse the repository at this point in the history
fix #40
  • Loading branch information
nareshbhatia committed Sep 18, 2018
1 parent 2375cf5 commit 22861ad
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 8 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -453,11 +453,16 @@ export interface Route {
The `RouterStore` is the keeper of the `RouterState`. It allows transitioning between states using the `goTo()` method.
```jsx
export interface ErrorHook {
(err: Error): any;
}

export class RouterStore {
@observable.ref routerState: RouterState;
@observable isTransitioning: boolean = false;

constructor(rootStore: any, routes: Route[], notFoundState: RouterState, initialRoute: Route = INITIAL_ROUTE);
setErrorHook(onError: ErrorHook);
goTo(toState: RouterState): Promise<RouterState>;
goTo(routeName: string, params?: StringMap, queryParams?: Object): Promise<RouterState>;
goToNotFound(): Promise<RouterState>;
Expand All @@ -466,10 +471,14 @@ export class RouterStore {
}
```
The `goTo()` method calls the optional `TransitionHook`s (see above) before transitioning to the final state. Any of the hooks can prevent the transition to the final state by returning a `Promise.reject(someOtherState)`. In this case the router will immediately transition to `someOtherState` without calling the remaining hooks.
The `RouterStore` exposes two observable properties:
- routerState: the state of the router
- isTransitioning: set to true when the router is in the process of transitioning from one state to another. This property can be used, for example, to display a progress indicator during transitions.
The `RouterStore` allows you to set a custom error hook using the `setErrorHook()` method. This hook is called if any unexpected error occurs when transitioning states. You can set this hook, for example, to transition to an error page.
### HistoryAdapter
The `HistoryAdapter` is responsible for keeping the browser address bar and the `RouterState` in sync. It also provides a `goBack()` method to go back in history.
Expand Down
33 changes: 25 additions & 8 deletions src/router-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ export interface TransitionHook {
): Promise<void>;
}

export interface ErrorHook {
(err: Error): any;
}

/**
* A `Route` consists of a name, a URL matching pattern and optional
* enter/exit hooks. The `RouterStore` is initialized with an array
Expand Down Expand Up @@ -73,6 +77,7 @@ export class RouterStore {
rootStore: any;
routes: Route[];
notFoundState: RouterState;
onError?: ErrorHook;
@observable.ref
routerState: RouterState;
@observable
Expand All @@ -93,6 +98,10 @@ export class RouterStore {
this.routerState = new RouterState(initialRoute.name);
}

setErrorHook(onError: ErrorHook) {
this.onError = onError;
}

/**
* Requests a transition to a new state. Note that the actual transition
* may be different from the requested one based on enter and exit hooks.
Expand Down Expand Up @@ -186,17 +195,25 @@ export class RouterStore {
this.setRouterState(toState);
return toState;
} catch (err) {
if (err instanceof RouterState === false) {
throw new Error('toState is undefined');
// If error is an instance of RouterState then go to that state
if (err instanceof RouterState) {
const redirectState: RouterState = err;

if (redirectState.isEqual(toState)) {
this.setRouterState(redirectState);
return redirectState;
} else {
return this.goTo(redirectState);
}
}
const redirectState: RouterState = err;

if (redirectState.isEqual(toState)) {
this.setRouterState(redirectState);
return redirectState;
} else {
return this.goTo(redirectState);
// Else if error hook is specified, call it
if (this.onError) {
return this.onError(err);
}

// Else handle the error internally
throw new Error('toState is undefined');
} finally {
this.isTransitioning = false;
}
Expand Down
11 changes: 11 additions & 0 deletions test/router-store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,17 @@ describe('RouterStore', () => {
);
});

test('calls error hook if a transition function throws an error', () => {
expect.assertions(1);

const onError = jest.fn();
const routerStore = new RouterStore({}, routes, notFound);
routerStore.setErrorHook(onError);
return routerStore.goTo(errorState).then(() => {
expect(onError.mock.calls.length).toEqual(1);
});
});

test('sets a default initial route', () => {
const expectedRouterState = {
routeName: '__initial__',
Expand Down

0 comments on commit 22861ad

Please sign in to comment.