Skip to content

Commit

Permalink
Parcels, SingleSpaContext, and loadRootComponent
Browse files Browse the repository at this point in the history
  • Loading branch information
joeldenning committed Apr 13, 2018
1 parent 7b65bdf commit ffb1a02
Show file tree
Hide file tree
Showing 14 changed files with 1,417 additions and 335 deletions.
4 changes: 2 additions & 2 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"presets": ["es2015"],
"plugins": ["transform-object-rest-spread"],
"presets": ["es2015", "react"],
"plugins": ["transform-object-rest-spread", "transform-class-properties"],
}
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
# not lib
coverage
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ language: node_js
node_js:
- "node"
script:
- yarn build
- yarn coverage
72 changes: 70 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# single-spa-react

Generic lifecycle hooks for React applications that are registered as [applications](https://github.com/CanopyTax/single-spa/blob/master/docs/applications.md#registered-applications) of [single-spa](https://github.com/CanopyTax/single-spa).
Generic lifecycle hooks for React applications that are registered as either [single-spa applications](https://github.com/CanopyTax/single-spa/blob/master/docs/applications.md#registered-applications) of [single-spa](https://github.com/CanopyTax/single-spa) parcels.

## Example
In addition to this Readme, example usage of single-spa-react can be found in the [single-spa-examples](https://github.com/CanopyTax/single-spa-examples/blob/master/src/react/react.app.js) project.
Expand Down Expand Up @@ -41,15 +41,83 @@ All options are passed to single-spa-react via the `opts` parameter when calling

- `React`: (required) The main React object, which is generally either exposed onto the window or is available via `require('react')` `import React from 'react'`.
- `ReactDOM`: (required) The main ReactDOMbject, which is available via `require('react-dom')` `import ReactDOM from 'react-dom'`.
- `rootComponent`: (required) The top level React component which will be rendered
- `rootComponent`: (required) The top level React component which will be rendered. Can be omitted only if `loadRootComponent` is provided.
- `loadRootComponent`: (optional) A loading function that returns a promise that resolves with the parcel. This takes the place of the `rootComponent` opt, when provided. It is intended to help people
who want to lazy load the source code for their root component. The source code will be lazy loaded during the bootstrap lifecycle.
- `suppressComponentDidCatchWarning`: (optional) A boolean that indicates if single-spa-react should warn when the rootComponent does not implement componentDidCatch. Defaults to false.
- `domElementGetter`: (optional) A function that takes in no arguments and returns a DOMElement. This dom element is where the React application will be bootstrapped, mounted, and unmounted.
Note that this opt can only be omitted when domElementGetter is passed in as a [custom prop](https://github.com/CanopyTax/single-spa/blob/master/docs/applications.md#custom-props) or if `domElement`
is passed as prop. So you must either do `singleSpaReact({..., domElementGetter: function() {return ...}})`, `singleSpa.registerApplication(name, app, activityFn, {domElementGetter: function() {...}})`,
or `singleSpaReact({..., domElement})`.
- `parcelCanUpdate`: (optional) A boolean that controls whether an update lifecycle will be created for the returned parcel. Note that option does not impact single-spa applications, but only parcels.
It is true by default.

## Notes

For react@>=16, it is best practice to have each single-spa application's root application implement componentDidCatch in order to avoid
the entire application unmounting unexpectedly when an error occurs. single-spa-react will warn to the console if componentDidCatch is not
implemented. See https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html for more details.

## SingleSpaContext

## Parcels
single-spa-react can also be used to create a single-spa parcel (instead of a single-spa application). To do so, simply call singleSpaReact() the same as for an application, except without a
domElementGetter (since those are provided by the code that will mount the parcel).

Additionally, single-spa-react provides a `<Parcel>` component to make using framework agnostic single-spa parcels easier. This allows you to put the parcel into your render method's jsx, instead of having to implement componentDidMount and componentWillUnmount.

#### Parcel props
- `config` (required): Either a single-spa parcel config object, or a "loading function" that returns a Promise that resolves with the parcel config.
- `wrapWith` (optional): A string [tagName](https://developer.mozilla.org/en-US/docs/Web/API/Element/tagName).`<Parcel>` will create a dom node of that type for the parcel to be mounted into. Defaults to `div`
- `appendTo` (optional): A dom element to append the parcel to. By default, this is not needed because the parcel will be mounted in the DOM that the `<Parcel>` component was rendered into. Useful for appending parcels to document.body or other separate parts of the dom.
- `mountParcel` (sometimes required, sometimes not): The `mountParcel` function provided by single-spa. In general, it is preferred to use an application's mountParcel function instead of the
single-spa's root mountParcel function, so that single-spa can keep track of the parent-child relationship and automatically unmount the application's parcels when the application unmounts.
Note that if the `<Parcel>` component is being rendered by a single-spa application that uses single-spa-react, it is **unnecessary** to pass in the prop, since `<Parcel>` can get the prop
from [SingleSpaContext](#singlespacontext)
- `handleError` (optional): A function that will be called with errors thrown by the parcel. If not provided, errors will be thrown on the window, by default.

#### Examples
```jsx
import Parcel from 'single-spa-react/parcel'
import * as parcelConfig from './my-parcel.js'

// config and wrapWith are required. The parcel will be mounted inside of the
// of a div inside of the react component tree
<Parcel
config={parcelConfig}
wrapWith="div"

singleRender={false}
handleError={err => console.error(err)}

customProp1="customPropValue2"
customProp2="customPropValue2"
/>

// If you pass in an appendTo prop, the parcel will be mounted there instead of
// to a dom node inside of the current react component tree
<Parcel>
config={parcelConfig}
wrapWith="div"
appendTo={document.body}
/>

// You can also pass in a "loading function" as the config.
// The loading function must return a promise that resolves with the parcel config.
// The parcel will be mounted once the promise resolves.
<Parcel
config={() => import('./my-parcel.js')}
wrapWith="div"
/>

// If you are rendering the Parcel component from a single-spa application, you do not need to pass a mountParcel prop.
// But if you have a separate react component tree that is not rendered by single-spa-react, you **must** pass in a mountParcel prop
// In general, it is preferred to use an application's mountParcel function instead of the single-spa's root mountParcel function,
// so that single-spa can keep track of the parent-child relationship and automatically unmount the application's parcels when the application
// unmounts
<Parcel
mountParcel={singleSpa.mountParcel}
config={parcelConfig}
wrapWith="div"
/>
```
6 changes: 5 additions & 1 deletion jest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
".*": "./node_modules/babel-jest"
},
"rootDir": "./",
"modulePaths": ["./"]
"modulePaths": ["./"],
"globals": {
"window": true
},
"setupFiles": ["<rootDir>/src/test-setup.js"]
}

15 changes: 13 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
"description": "A single spa plugin for React apps",
"main": "lib/single-spa-react.js",
"scripts": {
"build": "rimraf lib && babel src --out-dir lib --source-maps",
"build": "rimraf lib parcel && mkdirp lib parcel && babel src/single-spa-react.js --source-maps --out-file lib/single-spa-react.js && babel src/parcel.js --source-maps --out-file parcel/index.js",
"test": "jest --config jest.json",
"watch-tests": "jest --watch --config jest.json",
"coverage": "jest --coverage --config jest.json",
"prepublish": "yarn build"
"prepublish": "in-publish && yarn build || not-in-publish"
},
"repository": {
"type": "git",
Expand All @@ -30,10 +30,21 @@
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-jest": "^23.0.0-alpha.0",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1",
"in-publish": "^2.0.0",
"jest": "^22.4.2",
"jest-cli": "^23.0.0-alpha.0",
"mkdirp": "^0.5.1",
"react": "^16.3.1",
"react-dom": "^16.3.1",
"rimraf": "^2.6.2"
},
"peerDependencies": {
"react": "*"
}
}
208 changes: 208 additions & 0 deletions parcel/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit ffb1a02

Please sign in to comment.