Skip to content

Commit

Permalink
extract React specific code into subapp-react module (#1485)
Browse files Browse the repository at this point in the history
  • Loading branch information
jchip authored Jan 3, 2020
1 parent 880d5e3 commit 599e4b7
Show file tree
Hide file tree
Showing 32 changed files with 597 additions and 4,902 deletions.
16 changes: 16 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,22 @@
],
"internalConsoleOptions": "openOnSessionStart"
},
{
"type": "node",
"request": "launch",
"name": "subapp-react Mocha Tests",
"cwd": "${workspaceFolder}/packages/subapp-react",
"program": "${workspaceFolder}/packages/subapp-react/node_modules/mocha/bin/_mocha",
"args": [
"-u",
"tdd",
"--timeout",
"999999",
"--colors",
"${workspaceFolder}/packages/subapp-react/test/spec"
],
"internalConsoleOptions": "openOnSessionStart"
},
{
"type": "node",
"request": "launch",
Expand Down
3 changes: 3 additions & 0 deletions packages/subapp-react/.babelrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
presets: [["@babel/env", { modules: "auto", targets: { node: "current" } }], "@babel/react"]
};
3 changes: 3 additions & 0 deletions packages/subapp-react/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
browser
dist
*-lock.*
91 changes: 91 additions & 0 deletions packages/subapp-react/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Electrode Subapp For React

This module mainly serve to setup subapp-web with Facebook React framework.

It basically re-exports the module subapp-web and sets it up with React specific APIs.

For convenience, it also exports the module `react`.

To use, a subapp's code should be doing:

```js
import { React, loadSubApp } from "subapp-react";

import Component from "./component";

export default loadSubApp({ name: "MyComponent", Component });
```

For all pratical purposes, if there's code somewhere else that ensures subapp-web is setup with the proper React framework, then it's equivalent to the following:

```js
import React from "react";
import { loadSubApp } from "subapp-web";

import Component from "./component";

export default loadSubApp({ name: "MyComponent", Component });
```

`react` and `react-dom` are specified as peerDependencies, so you must install them as part of your `package.json` dependencies.

## SSR App Context

This module also exports a default React context that SSR uses to pass in server `request` object to your React component.

ie:

```js
import { AppContext } from "subapp-react";

const Component = () => {
return (
<AppContext.Consumer>
{({ isSsr, ssr, subApp }) => {
return (
<div>
IS_SSR: {`${Boolean(isSsr)}`} HAS_REQUEST: {ssr && ssr.request ? "yes" : "no"}
</div>
);
}}
</AppContext.Consumer>
);
};
```

## Support for React Router

If you want to use [react-router] in your application, then you need to install the dependencies:

- [react-router] and [react-router-dom]

ie:

```bash
npm i react-router react-router-dom
```

And then set the `useReactRouter` flag to true in your subapp:

```js
import { React, loadSubApp } from "subapp-react";

export default loadSubApp({ name: "MySubapp", Component, useReactRouter: true });
```

## Support for SSR with Suspense

async server side rendering with React suspense is enabled with [react-async-ssr]

- First, you have to install [react-async-ssr] with `npm i react-async-ssr`

## License

Copyright (c) 2016-present, WalmartLabs

Licensed under the [Apache License, Version 2.0].

[apache license, version 2.0]: https://www.apache.org/licenses/LICENSE-2.0
[react-router]: https://www.npmjs.com/package/react-router
[react-router-dom]: https://www.npmjs.com/package/react-router-dom
[react-async-ssr]: https://www.npmjs.com/package/react-async-ssr
9 changes: 9 additions & 0 deletions packages/subapp-react/babelrc-node
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"presets": [
["env", {
"targets": {
"node": "8.15.0"
}
} ], "react"
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,6 @@

/* eslint-disable max-statements */

/*
In order to support different UI framework like React/Preact,
first extract the framework specific code from subapp lib into
their own file.
Next allow a separate module to DI this into the subapp lib
in order to allow apps to use the framework they choose
*/

const assert = require("assert");
const optionalRequire = require("optional-require")(require);
const React = require("react");
Expand All @@ -26,7 +18,7 @@ class FrameworkLib {
}

async handleSSR() {
const { subApp, subAppServer, props } = this.ref;
const { subApp, subAppServer, options } = this.ref;
// If subapp wants to use react router and server didn't specify a StartComponent,
// then create a wrap StartComponent that uses react router's StaticRouter
if (subApp.useReactRouter && !subAppServer.StartComponent) {
Expand All @@ -39,14 +31,14 @@ class FrameworkLib {
return `<!-- serverSideRendering ${subApp.name} has no StartComponent -->`;
} else if (subApp.__redux) {
return await this.doReduxSSR();
} else if (props.serverSideRendering === true) {
} else if (options.serverSideRendering === true) {
return await this.doSSR();
}

return "";
}

renderTo(element, options = {}) {
renderTo(element, options) {
if (options.streaming) {
assert(!options.suspenseSsr, "streaming and suspense SSR together are not supported");
if (options.hydrateServerData) {
Expand Down Expand Up @@ -108,11 +100,11 @@ class FrameworkLib {
initialProps = await prepare({ request, context });
}

return await this.renderTo(this.createTopComponent(initialProps), this.ref.props);
return await this.renderTo(this.createTopComponent(initialProps), this.ref.options);
}

async doReduxSSR() {
const { subApp, subAppServer, context, props } = this.ref;
const { subApp, subAppServer, context, options } = this.ref;
const { request } = context.user;
// subApp.reduxReducers || subApp.reduxCreateStore) {
// if sub app has reduxReducers or reduxCreateStore then assume it's using
Expand Down Expand Up @@ -145,7 +137,7 @@ class FrameworkLib {
this.store,
`redux subapp ${subApp.name} didn't provide store, reduxCreateStore, or reducers`
);
if (props.serverSideRendering === true) {
if (options.serverSideRendering === true) {
assert(Provider, "subapp-web: react-redux Provider not available");
// finally render the element with Redux Provider and the store created
return await this.renderTo(
Expand All @@ -159,7 +151,7 @@ class FrameworkLib {
assert(
ReactRouterDom && ReactRouterDom.StaticRouter,
`subapp ${this.ref.subApp.name} specified useReactRouter without a StartComponent, \
and can't generate it because module react-dom-router with StaticRouter is not found`
and can't generate it because module react-router-dom with StaticRouter is not found`
);
return props2 =>
React.createElement(
Expand Down
15 changes: 15 additions & 0 deletions packages/subapp-react/lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"use strict";

const subappWeb = require("subapp-web");
const React = require("react");
const FrameworkLib = require("./framework-lib");
const { default: AppContext } = require("../browser/app-context");

subappWeb.setupFramework(FrameworkLib);

module.exports = {
...subappWeb,
AppContext,
FrameworkLib,
React
};
87 changes: 87 additions & 0 deletions packages/subapp-react/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
{
"name": "subapp-react",
"version": "0.0.1",
"description": "Electrode subapp support for React/Redux/React Router",
"browser": "browser/index.js",
"main": "lib/index.js",
"scripts": {
"test": "clap check",
"coverage": "clap check",
"build": "clap -x -n compile minify",
"compile": "babel src -d browser --source-maps"
},
"keywords": [
"web",
"react",
"subapp",
"redux",
"react-router"
],
"author": "Electrode (http://www.electrode.io/)",
"contributors": [
"Joel Chen <[email protected]>"
],
"license": "Apache-2.0",
"files": [
"lib",
"browser",
"dist"
],
"dependencies": {
"subapp-web": "^1.0.0",
"subapp-util": "^1.0.2"
},
"devDependencies": {
"@babel/cli": "^7.2.3",
"@babel/core": "^7.2.2",
"@babel/preset-env": "^7.3.1",
"@babel/preset-react": "^7.0.0",
"@babel/register": "^7.7.7",
"babel-preset-minify": "^0.5.1",
"electrode-archetype-njs-module-dev": "^3.0.0",
"react": "^16.8.3",
"react-async-ssr": "^0.6.0",
"react-dom": "^16.8.3",
"react-redux": "^6.0.1",
"react-router": "^5.1.2",
"react-router-dom": "^5.1.2",
"redux": "^4.0.1"
},
"peerDependencies": {
"react": "*",
"react-dom": "*"
},
"fyn": {
"dependencies": {
"subapp-web": "../subapp-web",
"subapp-util": "../subapp-util"
}
},
"nyc": {
"all": true,
"require": [
"@babel/register",
"mocha"
],
"reporter": [
"lcov",
"text",
"text-summary"
],
"exclude": [
"coverage",
"*clap.js",
"gulpfile.js",
"dist",
"test",
"browser",
"**/.babelrc.js"
],
"check-coverage": true,
"statements": 0,
"branches": 0,
"functions": 0,
"lines": 0,
"cache": true
}
}
7 changes: 7 additions & 0 deletions packages/subapp-react/src/.babelrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
presets: [
["@babel/env", { modules: "auto" }],
"@babel/react",
process.env.MINIFY ? "minify" : undefined
].filter(x => x)
};
File renamed without changes.
File renamed without changes.
11 changes: 11 additions & 0 deletions packages/subapp-react/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import FrameworkLib from "./fe-framework-lib";

import { setupFramework } from "subapp-web";

setupFramework(FrameworkLib);

export * from "subapp-web";

export { default as React } from "react";

export { default as AppContext } from "./app-context";
3 changes: 3 additions & 0 deletions packages/subapp-react/test/mocha.opts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
--require node_modules/electrode-archetype-njs-module-dev/config/test/setup.js
--require @babel/register
--recursive
Loading

0 comments on commit 599e4b7

Please sign in to comment.