-
-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5961afc
commit 478ee03
Showing
20 changed files
with
498 additions
and
87 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"presets": ["@babel/preset-env", "@babel/preset-react"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"tabWidth": 2, | ||
"singleQuote": true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
# @cra-express/router-prefetcher | ||
|
||
> Simple utility to map your routes and prefetch your data on server | ||
> You might want to wait for React Suspense, [demo](https://github.com/acdlite/suspense-ssr-demo/) | ||
> :warning: **Alpha** stage. API may change, don't use on production yet! | ||
## Prerequisites | ||
|
||
- React Router with array config | ||
- Promise support | ||
|
||
## Start | ||
|
||
``` | ||
npm i @cra-express/router-prefetcher | ||
``` | ||
|
||
## Setup and Usage | ||
|
||
- Add `{{SCRIPT}}` to public/index.html | ||
|
||
```html | ||
<div id="root"></div> | ||
{{SCRIPT}} | ||
``` | ||
|
||
```js | ||
// server/app.js | ||
|
||
import { createReactAppExpress } from '@cra-express/core'; | ||
import { getInitialData } from '@cra-express/router-prefetcher'; | ||
import routes from '../src/routes'; | ||
const path = require('path'); | ||
const React = require('react'); | ||
const { StaticRouter } = require('react-router'); | ||
|
||
const { default: App } = require('../src/App'); | ||
const clientBuildPath = path.resolve(__dirname, '../client'); | ||
let AppClass = App; | ||
let serverData; | ||
const app = createReactAppExpress({ | ||
clientBuildPath, | ||
universalRender: handleUniversalRender, | ||
onEndReplace(html) { | ||
const state = store.getState(); | ||
return html.replace( | ||
'{{SCRIPT}}', | ||
`<script> | ||
window.__INITIAL_DATA__ = ${JSON.stringify(serverData).replace( | ||
/</g, | ||
'\\u003c' | ||
)}; | ||
</script>` | ||
); | ||
} | ||
}); | ||
|
||
function handleUniversalRender(req, res) { | ||
const context = {}; | ||
return getInitialData(req, res, routes) | ||
.then(data => { | ||
serverData = data; | ||
const app = ( | ||
<StaticRouter location={req.url} context={context}> | ||
<AppClass routes={routes} initialData={data} /> | ||
</StaticRouter> | ||
); | ||
return app; | ||
}) | ||
.catch(err => { | ||
console.error(err); | ||
res.send(500); | ||
}); | ||
} | ||
|
||
export default app; | ||
``` | ||
|
||
```js | ||
// src/index.js | ||
|
||
import routes from './routes'; | ||
|
||
const data = window.__INITIAL_DATA__; | ||
ReactDOM.hydrate( | ||
<BrowserRouter> | ||
<App routes={routes} initialData={data} /> | ||
</BrowserRouter>, | ||
document.getElementById('root') | ||
); | ||
``` | ||
|
||
```js | ||
// src/App.js | ||
|
||
import React from 'react'; | ||
import { Route, Switch } from 'react-router'; | ||
|
||
const App = ({ routes, initialData }) => { | ||
return ( | ||
<Switch> | ||
{routes.map((route, index) => { | ||
return ( | ||
<Route | ||
key={index} | ||
path={route.path} | ||
exact={route.exact} | ||
render={props => | ||
React.createElement(route.component, { | ||
...props, | ||
routes: route.routes, | ||
initialData: initialData[index] || null | ||
}) | ||
} | ||
/> | ||
); | ||
})} | ||
</Switch> | ||
); | ||
}; | ||
``` | ||
|
||
```js | ||
// src/routes/DemoPage/AboutView.js | ||
|
||
import React from 'react'; | ||
|
||
import withSSR from '../../components/withSSR'; | ||
|
||
class AboutView extends React.Component { | ||
static getInitialData({ match, req, res }) { | ||
return new Promise((resolve, reject) => { | ||
setTimeout(() => { | ||
resolve({ | ||
article: ` | ||
This text is ALSO server rendered if and only if it's the initial render. | ||
`, | ||
currentRoute: match.pathname | ||
}); | ||
}, 500); | ||
}); | ||
} | ||
|
||
render() { | ||
const { isLoading, article, error } = this.props; | ||
return ( | ||
<div> | ||
<h1>About</h1> | ||
{isLoading && <div>Loading from client...</div>} | ||
{error && <div>{JSON.stringify(error, null, 2)}</div>} | ||
{article && <div>{article}</div>} | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
export default withSSR(AboutView); | ||
``` | ||
|
||
Please get the `withSSR` code [here](https://github.com/jaredpalmer/react-router-nextjs-like-data-fetching/blob/master/src/components/withSSR.js) | ||
|
||
## License | ||
|
||
MIT |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"testEnvironment": "node", | ||
"moduleFileExtensions": ["js"], | ||
"collectCoverageFrom": ["src/**/*.{js}"], | ||
"testRegex": "(src)/.*\\.spec\\.js$" | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
{ | ||
"name": "@cra-express/router-prefetcher", | ||
"version": "4.0.0-alpha.2", | ||
"description": "Router Prefetcher for prefetching on server", | ||
"main": "lib/index.js", | ||
"files": [ | ||
"lib" | ||
], | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"scripts": { | ||
"build": "babel src -d lib --ignore spec.js", | ||
"watch": "babel src -d lib --watch --ignore spec.js", | ||
"test": "jest --watch --config=jest-config.json", | ||
"test:ci": "jest --config=jest-config.json --coverage" | ||
}, | ||
"author": "Antony Budianto <antonybudianto@gmail.com>", | ||
"license": "MIT", | ||
"peerDependencies": { | ||
"react": "*", | ||
"react-router-dom": "*" | ||
}, | ||
"devDependencies": { | ||
"react": "^16.5.2", | ||
"react-router-dom": "^4.3.1" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { matchPath } from 'react-router-dom'; | ||
|
||
export function getInitialData(req, res, routes) { | ||
const matches = routes.map((route, index) => { | ||
const match = matchPath(req.url, route.path, route); | ||
if (match) { | ||
const obj = { | ||
route, | ||
match, | ||
promise: route.component.getInitialData | ||
? route.component.getInitialData({ | ||
match, | ||
req, | ||
res | ||
}) | ||
: Promise.resolve(null) | ||
}; | ||
return obj; | ||
} | ||
return null; | ||
}); | ||
|
||
const promises = matches.map(match => (match ? match.promise : null)); | ||
return Promise.all(promises); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { getInitialData } from './index'; | ||
|
||
jest.mock('react-router-dom'); | ||
|
||
const rrd = require('react-router-dom'); | ||
|
||
describe('getInitialData', () => { | ||
it('should handle not match path', done => { | ||
rrd.matchPath.mockReturnValue(false); | ||
getInitialData({}, {}, [{}]).then(res => { | ||
expect(res[0]).toBeNull(); | ||
done(); | ||
}); | ||
}); | ||
|
||
it('should handle matched path', done => { | ||
rrd.matchPath.mockReturnValue(true); | ||
getInitialData({}, {}, [ | ||
{ | ||
component: { | ||
getInitialData() { | ||
return Promise.resolve(1); | ||
} | ||
} | ||
} | ||
]).then(res => { | ||
expect(res[0]).toBe(1); | ||
done(); | ||
}); | ||
}); | ||
|
||
it('should handle matched path with no getInitialData', done => { | ||
rrd.matchPath.mockReturnValue(true); | ||
getInitialData({}, {}, [ | ||
{ | ||
component: {} | ||
} | ||
]).then(res => { | ||
expect(res[0]).toBeNull(); | ||
done(); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"tabWidth": 2, | ||
"singleQuote": true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.