Skip to content

Commit a580f32

Browse files
committed
Upgrades to react-tree-walker v4.
Adds the ability to specify a React Context that will be available to your Components within the bootstrapping process. Deprecates "asyncBootstrap" method on your components in favour of "bootstrap" alternative name. Your "asyncBootstrap" methods will still work, however you will get a deprecation warning printed. Adds a bunch more documentation.
1 parent 7704326 commit a580f32

File tree

5 files changed

+747
-390
lines changed

5 files changed

+747
-390
lines changed

Diff for: README.md

+63-30
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# react-async-bootstrapper 👢
22

3-
Provides the ability to execute async bootstrapper functions within your React element tree.
3+
Execute a bootstrap method on your React/Preact components. Useful for data prefetching and other activities.
44

55
[![npm](https://img.shields.io/npm/v/react-async-bootstrapper.svg?style=flat-square)](http://npm.im/react-async-bootstrapper)
66
[![MIT License](https://img.shields.io/npm/l/react-async-bootstrapper.svg?style=flat-square)](http://opensource.org/licenses/MIT)
@@ -9,19 +9,19 @@ Provides the ability to execute async bootstrapper functions within your React e
99

1010
## TOCs
1111

12-
- [Introduction](#introduction)
13-
- [FAQs](#faqs)
12+
* [Introduction](#introduction)
13+
* [Simple Example](#simple-example)
1414

1515
## Introduction
1616

17-
This library is an abstraction of [`react-tree-walker`](https://github.com/ctrlplusb/react-tree-walker), that allows you to attach an `asyncBootstrap` method to your React "class" components.
17+
This library is a simple implementation of [`react-tree-walker`](https://github.com/ctrlplusb/react-tree-walker), allowing you to attach a `bootstrap` method to your React/Preact "class" components. I would highly recommend you review `react-tree-walkers` documentation so as to gain more familiarity with what is being wrapped up by `react-bootstrapper`.
1818

19-
Within the `asyncBootstrap` you can do any asynchronous work you like (e.g. fetching data) that you like, returning a `Promise` to indicate when the asynchronous work has completed.
19+
I have created this implementation that responds to a `bootstrap` method to allow me to have a standard implementation that would allow for interop between multiple activities requiring a bootstrapping process. For example I have create [`react-async-component`](https://github.com/ctrlplusb/react-async-component) which provides code splitting support, and [`react-jobs`](https://github.com/ctrlplusb/react-jobs) which provides a mechanism to executed data fetching. Both use this library allowing for a single bootstrapping parse to be executed for the needs of both.
2020

21-
## Naive Example
21+
## Simple Example
2222

2323
```jsx
24-
import asyncBootstrapper from 'react-async-bootstrapper'
24+
import bootstrapper from 'react-async-bootstrapper'
2525

2626
// Our super naive global state. Don't copy this, it's just illustrative. You'd
2727
// likely want to use something
@@ -30,29 +30,20 @@ const globalStateManager = {
3030
}
3131

3232
class Product extends Component {
33-
 // 👇
34-
 asyncBootstrap() {
35-
if (globalStateManager.products[this.props.productId]) {
36-
// Already have data
37-
return
38-
}
39-
33+
// 👇
34+
bootstrap() {
4035
// Fetch our product and load up our state
41-
return fetch(`/api/products/${this.props.productId}`)
42-
.then((response) => {
43-
// store in our global state
44-
globalStateManager.products[this.props.productId] = response.json()
45-
// Indicates our desire to allow for nested asyncBootstrap instances
46-
// to be located/resolved
47-
return true
48-
})
36+
return fetch(`/api/products/${this.props.productId}`).then(response => {
37+
// store in our global state
38+
globalStateManager.products[this.props.productId] = response.json()
39+
})
4940
}
5041

5142
render() {
5243
const product = globalStateManager.products[this.props.productId]
5344
return (
5445
<div>
55-
{product.name} - {product.price}
46+
{product.name} - {product.price}
5647
</div>
5748
)
5849
}
@@ -66,14 +57,56 @@ const app = (
6657
)
6758

6859
// Now for the bootstrapping/rendering process (on a client/server)
69-
asyncBootstrapper(app).then(() => {
70-
// bootstrapping complete
71-
ReactDOM.render(app, document.getElementById('app'))
72-
})
60+
bootstrapper(app)
61+
.then(() => {
62+
// Bootstrapping is complete, now when we render our application to the DOM
63+
// the global products state will be populated and so our components
64+
// should render immediately with the data.
65+
ReactDOM.render(app, document.getElementById('app'))
66+
})
67+
.catch(err => console.log('Eek, error!', err))
68+
```
69+
70+
Yep, not a particularly useful idea in the context of executing on the front end only, but when doing server side rendering of your react application this pattern can be extremely useful.
71+
72+
## API
73+
74+
The API is very simple at the moment, only exposing a single function.
75+
76+
### **bootstrapper**
77+
78+
The default export of the library. The function that performs the magic.
79+
80+
```javascript
81+
const bootstrapper = require('react-async-bootstrapper')
82+
```
83+
84+
_or_
85+
86+
```javascript
87+
import bootstrapper from 'react-async-bootstrapper'
7388
```
7489

75-
Zing. You can do anything you like. And interplay with other libaries that support `react-async-bootstrapper`, such as [`react-async-component`](https://github.com/ctrlplusb/react-async-component) which provides code splitting support.
90+
**Paramaters**
91+
92+
* **app** (React/Preact application/element, _required_)
93+
94+
The react application you wish to walk.
95+
96+
e.g. `<div>Hello world</div>`
97+
98+
* **options** (`Object`, _optional_)
99+
100+
Additional options/configuration. It currently supports the following values:
101+
102+
* _componentWillUnmount_: Enable this to have the `componentWillUnmount` lifecycle event be executed during the bootstrapping process. Defaults to `false`. This was added as an experimental additional flag to help with applications where they have critical disposal logic being executed within the `componentWillUnmount` lifecycle event.
103+
104+
* **context** (`Object`, _optional_)
105+
106+
Any context you wish to expose to your application. This will become available to the entire application and could be useful for exposing configuration to your `bootstrap` methods.
107+
108+
e.g. `{ myContextItem: 'foo' }`
76109

77-
## FAQs
110+
**Returns**
78111

79-
> Let me know if you have any questions
112+
A `Promise` that resolves when the bootstrapping has completed.

Diff for: package.json

+9-10
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
22
"name": "react-async-bootstrapper",
3-
"version": "1.1.2",
3+
"version": "2.0.0",
44
"description":
5-
"Provides the ability to execute async bootstrapper functions within your React element tree.",
5+
"Execute a bootstrap method on your React/Preact components. Useful for data prefetching and other activities.",
66
"license": "MIT",
7-
"main": "commonjs/index.js",
7+
"main": "dist/react-async-bootstrapper.js",
88
"files": ["*.js", "*.md", "dist"],
99
"repository": {
1010
"type": "git",
@@ -15,8 +15,7 @@
1515
"keywords": ["library"],
1616
"scripts": {
1717
"build": "node ./tools/scripts/build.js",
18-
"clean":
19-
"rimraf ./commonjs && rimraf ./umd && rimraf ./coverage && rimraf ./umd",
18+
"clean": "rimraf ./dist && rimraf ./coverage",
2019
"lint": "eslint src",
2120
"precommit": "lint-staged && npm run test",
2221
"prepublish": "npm run build",
@@ -56,22 +55,22 @@
5655
"gzip-size": "^4.0.0",
5756
"husky": "^0.14.3",
5857
"in-publish": "2.0.0",
59-
"jest": "^21.2.1",
60-
"lint-staged": "^4.2.3",
58+
"jest": "^22.4.2",
59+
"lint-staged": "^7.0.0",
6160
"prettier": "^1.7.4",
6261
"pretty-bytes": "4.0.2",
6362
"ramda": "^0.25.0",
6463
"react": "^16.0.0",
6564
"react-addons-test-utils": "^15.6.2",
6665
"react-dom": "^16.0.0",
67-
"readline-sync": "1.4.7",
66+
"readline-sync": "1.4.9",
6867
"rimraf": "^2.6.2",
69-
"rollup": "^0.56.5",
68+
"rollup": "^0.57.1",
7069
"rollup-plugin-babel": "^3.0.3",
7170
"rollup-plugin-uglify": "^3.0.0"
7271
},
7372
"dependencies": {
74-
"react-tree-walker": "^2.1.3"
73+
"react-tree-walker": "^4.0.2"
7574
},
7675
"jest": {
7776
"collectCoverageFrom": ["src/**/*.{js,jsx}"],

Diff for: src/__tests__/asyncBootstrapper.test.js

+48-25
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,60 @@
11
/* eslint-disable react/prop-types */
2+
/* eslint-disable react/no-multi-comp */
23

34
import React, { Component } from 'react'
45
import asyncBootstrapper from '../'
56

67
describe('asyncBootstrapper()', () => {
7-
it('works', () => {
8-
const values = []
9-
10-
class Foo extends Component {
11-
asyncBootstrap() {
12-
values.push(this.props.id)
13-
return true
14-
}
15-
16-
render() {
17-
return <div>{this.props.children}</div>
18-
}
8+
let values = []
9+
let actualContext
10+
11+
class DeprecatedAPI extends Component {
12+
asyncBootstrap() {
13+
values.push(this.props.id)
14+
return true
15+
}
16+
17+
render() {
18+
return <div>{this.props.children}</div>
1919
}
20+
}
2021

21-
const app = (
22-
<Foo id={1}>
23-
<div>
24-
<h1>Test</h1>
25-
</div>
26-
<Foo id={2}>
27-
<Foo id={4} />
28-
</Foo>
29-
<Foo id={3} />
22+
class NewAPI extends Component {
23+
bootstrap() {
24+
values.push(this.props.id)
25+
actualContext = this.context.isBootstrapping
26+
return true
27+
}
28+
29+
render() {
30+
return <div>{this.props.children}</div>
31+
}
32+
}
33+
34+
const app = Foo => (
35+
<Foo id={1}>
36+
<div>
37+
<h1>Test</h1>
38+
</div>
39+
<Foo id={2}>
40+
<Foo id={4} />
3041
</Foo>
31-
)
42+
<Foo id={3} />
43+
</Foo>
44+
)
3245

33-
return asyncBootstrapper(app).then(() =>
34-
expect(values).toEqual([1, 2, 4, 3]),
35-
)
46+
beforeEach(() => {
47+
values = []
3648
})
49+
50+
it('deprecated API', () =>
51+
asyncBootstrapper(app(DeprecatedAPI)).then(() =>
52+
expect(values).toEqual([1, 2, 4, 3]),
53+
))
54+
55+
it('new API', () =>
56+
asyncBootstrapper(app(NewAPI), null, { isBootstrapping: true }).then(() => {
57+
expect(values).toEqual([1, 2, 4, 3])
58+
expect(actualContext).toBe(true)
59+
}))
3760
})

Diff for: src/index.js

+13-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
import reactTreeWalker from 'react-tree-walker'
22

3-
export default function asyncBootstrapper(app, options) {
3+
const warnmsg =
4+
'Deprecation notice: you are using the deprecated "asyncBootsrap" for "react-async-bootstrapper", please rename these to "bootstrap"'
5+
6+
export default function asyncBootstrapper(app, options, context = {}) {
47
const visitor = (element, instance) => {
5-
if (instance && typeof instance.asyncBootstrap === 'function') {
6-
return instance.asyncBootstrap()
8+
if (
9+
instance &&
10+
(typeof instance.asyncBootstrap === 'function' ||
11+
typeof instance.bootstrap === 'function')
12+
) {
13+
return typeof instance.bootstrap === 'function'
14+
? instance.bootstrap()
15+
: console.log(warnmsg) || instance.asyncBootstrap()
716
}
817
}
918

10-
return reactTreeWalker(app, visitor, {}, options)
19+
return reactTreeWalker(app, visitor, context, options)
1120
}

0 commit comments

Comments
 (0)