Skip to content

Commit

Permalink
Renamed blocking => beforeTransition and defer => afterTransition
Browse files Browse the repository at this point in the history
Closes #10
dlmr authored and PAkerstrand committed Oct 18, 2016
1 parent f4f601b commit be3a7f4
Showing 5 changed files with 66 additions and 65 deletions.
34 changes: 17 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -66,32 +66,32 @@ isAborted Function that returns if the hooks has been aborted, can be us

### Default props available to decorated components
```
loading Will be true when blocking hooks are not yet completed
deferredLoading Will be true when deferred hooks are not yet completed
reload Function that can be invoked to re-trigger the hooks for the current component
abort Function that can be invoked to abort current running hooks
loading Will be true when beforeTransition hooks are not yet completed
afterTransitionLoading Will be true when afterTransition hooks are not yet completed
reload Function that can be invoked to re-trigger the hooks for the current component
abort Function that can be invoked to abort current running hooks
```
Additionally components will have access to properties that has been set using `setProps`.

## Client API
The custom redial router middleware `useRedial` makes it easy to add support for redial on the client side using the `render` property from `Router` in React Router. It provides the following properties as a way to configure how the data loading should behave.
```
locals Extra locals that should be provided to the hooks other than the default ones
blocking Hooks that should be completed before a route transition is completed
defer Hooks that are not needed before making a route transition
parallel If set to true the deferred hooks will run in parallel with the blocking ones
beforeTransition Hooks that should be completed before a route transition is completed
afterTransition Hooks that are not needed before making a route transition
parallel If set to true the afterTransition hooks will run in parallel with the beforeTransition ones
initialLoading Component should be shown on initial client load, useful if server rendering is not used
onStarted(force) Invoked when a route transition has been detected and when redial hooks will be invoked
onError(error, metaData) Invoked when an error happens, see below for more info
onAborted(becauseError) Invoked if it was prematurely aborted through manual interaction or an error
onCompleted(type) Invoked if everything was completed successfully, with type being either "blocking" or "deferred"
onCompleted(type) Invoked if everything was completed successfully, with type being either "beforeTransition" or "afterTransition"
```

### `onError(error, metaData)`
__`metaData`__
```
abort() Function that can be used to abort current loading
blocking If the error originated from a blocking hook or not
beforeTransition If the error originated from a beforeTransition hook or not
reason The reason for the error, can be either a "location-changed", "aborted" or "other"
router React Router instance https://github.com/ReactTraining/react-router/blob/master/docs/API.md#contextrouter
```
@@ -104,13 +104,13 @@ const forcePageReloadOnError = true;
const goBackOnError = false;

// Function that can be used as a setting for useRedial
function onError(err, { abort, blocking, reason, router }) {
function onError(err, { abort, beforeTransition, reason, router }) {
if (process.env.NODE_ENV !== 'production') {
console.error(reason, err);
}

// We only what to do this if it was a blocking hook that failed
if (blocking) {
// We only what to do this if it was a beforeTransition hook that failed
if (beforeTransition) {
if (forcePageReloadOnError && reason === 'other') {
window.location.reload();
} else if (goBackOnError && reason !== 'location-changed') {
@@ -133,8 +133,8 @@ import { applyRouterMiddleware } from 'react-router';
render={ applyRouterMiddleware(
useRedial({
locals,
blocking: ['fetch'],
defer: ['defer', 'done'],
beforeTransition: ['fetch'],
afterTransition: ['defer', 'done'],
parallel: true,
initialLoading: () => <div>Loading…</div>,
})
@@ -175,7 +175,7 @@ triggerHooks({
```

## Hooks
react-router-redial provides a simple way to define in what order certain hooks should run and if they can run in parallel. The same syntax is used for both `hooks` when used on the server with `triggerHooks` and `blocking` + `defer` on the client with `RedialContext`. The hooks are expected to be an array the can contain either single hooks or arrays of hooks. Each individual element in the array will run in parallel and after it has been completed the next element will be managed. This means that you can run some hooks together and others after they have been completed. This is useful if you for instance want to run some hook that should have access to some data that other hooks before it should have defined.
react-router-redial provides a simple way to define in what order certain hooks should run and if they can run in parallel. The same syntax is used for both `hooks` when used on the server with `triggerHooks` and `beforeTransition` + `afterTransition` on the client with `RedialContext`. The hooks are expected to be an array the can contain either single hooks or arrays of hooks. Each individual element in the array will run in parallel and after it has been completed the next element will be managed. This means that you can run some hooks together and others after they have been completed. This is useful if you for instance want to run some hook that should have access to some data that other hooks before it should have defined.

### Example
Let's look at an example to understand this a bit better. Say that we have the following hooks defined on the server:
@@ -215,8 +215,8 @@ export default (container, store) => {
render={ applyRouterMiddleware(
useRedial({
locals,
blocking: ['fetch'],
defer: ['defer', 'done'],
beforeTransition: ['fetch'],
afterTransition: ['defer', 'done'],
parallel: true,
initialLoading: () => <div>Loading…</div>,
})
4 changes: 2 additions & 2 deletions examples/redux/render/client.js
Original file line number Diff line number Diff line change
@@ -19,8 +19,8 @@ export default (container, routes, store) => {
routes={routes}
render={applyRouterMiddleware(useRedial({
locals,
blocking: ['fetch', 'blockingDone'],
defer: ['defer', 'deferDone'],
beforeTransition: ['fetch', 'blockingDone'],
afterTransition: ['defer', 'deferDone'],
parallel: false,
}))}
/>
6 changes: 3 additions & 3 deletions examples/simple-with-scroll/render/client.js
Original file line number Diff line number Diff line change
@@ -12,10 +12,10 @@ export default (container, routes) => {
history={browserHistory}
routes={routes}
render={applyRouterMiddleware(
useScroll(),
useScroll(),
useRedial({
blocking: ['fetch'],
defer: ['defer', 'done'],
beforeTransition: ['fetch'],
afterTransition: ['defer', 'done'],
parallel: false,
initialLoading: () => <div>Loading…</div>,
})
10 changes: 5 additions & 5 deletions examples/simple/render/client.js
Original file line number Diff line number Diff line change
@@ -10,13 +10,13 @@ export default (container, routes) => {
const goBackOnError = false;

// Function that can be used as a setting for useRedial
function onError(err, { abort, blocking, reason, router }) {
function onError(err, { abort, beforeTransition, reason, router }) {
if (process.env.NODE_ENV !== 'production') {
console.error(reason, err);
}

// We only what to do this if it was a blocking hook that failed
if (blocking) {
if (beforeTransition) {
if (forcePageReloadOnError && reason === 'other') {
window.location.reload();
} else if (goBackOnError && reason !== 'location-changed') {
@@ -32,9 +32,9 @@ export default (container, routes) => {
history={browserHistory}
routes={routes}
render={applyRouterMiddleware(useRedial({
blocking: ['fetch'],
defer: ['defer', 'done'],
parallel: false,
beforeTransition: ['fetch'],
afterTransition: ['defer', 'done'],
parallel: true,
initialLoading: () => <div>Loading…</div>,
onError,
}))}
77 changes: 39 additions & 38 deletions src/RedialContext.js
Original file line number Diff line number Diff line change
@@ -27,8 +27,8 @@ export default class RedialContext extends Component {

// Custom
locals: PropTypes.object,
blocking: PropTypes.array,
defer: PropTypes.array,
beforeTransition: PropTypes.array,
afterTransition: PropTypes.array,
parallel: PropTypes.bool,
initialLoading: PropTypes.func,
onError: PropTypes.func,
@@ -41,8 +41,8 @@ export default class RedialContext extends Component {
};

static defaultProps = {
blocking: [],
defer: [],
beforeTransition: [],
afterTransition: [],
parallel: false,

onError(err, { type }) {
@@ -82,21 +82,21 @@ export default class RedialContext extends Component {
super(props, context);
this.state = {
loading: false,
deferredLoading: false,
afterTransitionLoading: false,
aborted: () => false,
abort: () => {},
prevRenderProps: undefined,
redialMap: props.redialMap || hydrate(props.renderProps),
initial: props.blocking.length > 0,
initial: props.beforeTransition.length > 0,
};
}

getChildContext() {
const { loading, deferredLoading, redialMap } = this.state;
const { loading, afterTransitionLoading, redialMap } = this.state;
return {
redialContext: {
loading,
deferredLoading,
afterTransitionLoading,
redialMap,
reloadComponent: (component) => {
this.reloadComponent(component);
@@ -133,12 +133,12 @@ export default class RedialContext extends Component {
if (!abort || this.state.abort === abort) {
// We need to be in a loading state for it to make sense
// to abort something
if (this.state.loading || this.state.deferredLoading) {
if (this.state.loading || this.state.afterTransitionLoading) {
this.state.abort();

this.setState({
loading: false,
deferredLoading: false,
afterTransitionLoading: false,
});

if (this.props.onAborted) {
@@ -174,39 +174,39 @@ export default class RedialContext extends Component {
aborted,
abort,
loading: true,
blockingCompleted: false,
beforeTransitionCompleted: false,
prevRenderProps: this.state.aborted() ? this.state.prevRenderProps : this.props.renderProps,
});

if (this.props.parallel) {
this.runDeferred(
this.props.defer,
this.runAfterTransition(
this.props.afterTransition,
components,
renderProps,
force,
bail
)
.then(() => {
if (this.state.blockingCompleted) {
this.props.onCompleted('deferred');
if (this.state.beforeTransitionCompleted) {
this.props.onCompleted('afterTransition');
}
})
.catch((err) => {
// We will only propagate this error if blocking have been completed
// This because the blocking error is more critical
if (this.state.blockingCompleted) {
// We will only propagate this error if beforeTransition have been completed
// This because the beforeTransition error is more critical
if (this.state.beforeTransitionCompleted) {
this.props.onError(err, {
reason: bail() || 'other',
blocking: false,
beforeTransition: false,
router: this.props.renderProps.router,
abort: () => this.abort(true, abort),
});
}
});
}

this.runBlocking(
this.props.blocking,
this.runBeforeTransition(
this.props.beforeTransition,
components,
renderProps,
force,
@@ -215,18 +215,19 @@ export default class RedialContext extends Component {
.catch((error) => {
this.props.onError(error, {
reason: bail() || 'other',
blocking: error.deferred === undefined, // If not defined before it's a blocking error
// If not defined before it's a beforeTransition error
beforeTransition: error.afterTransition === undefined,
router: this.props.renderProps.router,
abort: () => this.abort(true, abort),
});
});
}

runDeferred(hooks, components, renderProps, force = false, bail) {
// Get deferred data, will not block route transitions
runAfterTransition(hooks, components, renderProps, force = false, bail) {
// Get afterTransition data, will not block route transitions
this.setState({
deferredLoading: true,
deferredCompleted: false,
afterTransitionLoading: true,
afterTransitionCompleted: false,
});

return triggerHooks({
@@ -239,44 +240,44 @@ export default class RedialContext extends Component {
bail,
}).then(({ redialMap }) => {
this.setState({
deferredLoading: false,
afterTransitionLoading: false,
redialMap,
deferredCompleted: true,
afterTransitionCompleted: true,
});
});
}

runBlocking(hooks, components, renderProps, force = false, bail) {
runBeforeTransition(hooks, components, renderProps, force = false, bail) {
const completeRouteTransition = (redialMap) => {
if (!bail() && !this.unmounted) {
this.setState({
loading: false,
blockingCompleted: true,
beforeTransitionCompleted: true,
redialMap,
prevRenderProps: undefined,
initial: false,
});

this.props.onCompleted('blocking');
this.props.onCompleted('beforeTransition');

// Start deferred if we are not in parallel
// Start afterTransition if we are not in parallel
if (!this.props.parallel) {
return this.runDeferred(
this.props.defer,
return this.runAfterTransition(
this.props.afterTransition,
components,
renderProps,
force,
bail
)
.then(() => {
this.props.onCompleted('deferred');
this.props.onCompleted('afterTransition');
})
.catch((error) => {
error.deferred = true; // eslint-disable-line
error.afterTransition = true; // eslint-disable-line
return Promise.reject(error);
});
} else if (this.state.deferredCompleted) {
this.props.onCompleted('deferred');
} else if (this.state.afterTransitionCompleted) {
this.props.onCompleted('afterTransition');
}
}

0 comments on commit be3a7f4

Please sign in to comment.