Add react-router-dom, TODO fix SSR

This commit is contained in:
Jerko Steiner 2019-03-16 12:20:55 +05:00
parent f5686c7dc9
commit 10b034e7f0
7 changed files with 155 additions and 9 deletions

131
package-lock.json generated
View File

@ -1479,6 +1479,12 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/history": {
"version": "4.7.2",
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.2.tgz",
"integrity": "sha512-ui3WwXmjTaY73fOQ3/m3nnajU/Orhi6cEu5rzX+BrAAJxa3eITXZ5ch9suPqtM03OWhAHhPSyBGCN4UKoxO20Q==",
"dev": true
},
"@types/http-errors": { "@types/http-errors": {
"version": "1.6.1", "version": "1.6.1",
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.6.1.tgz", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.6.1.tgz",
@ -1585,6 +1591,27 @@
"redux": "^4.0.0" "redux": "^4.0.0"
} }
}, },
"@types/react-router": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-4.4.5.tgz",
"integrity": "sha512-12+VOu1+xiC8RPc9yrgHCyLI79VswjtuqeS2gPrMcywH6tkc8rGIUhs4LaL3AJPqo5d+RPnfRpNKiJ7MK2Qhcg==",
"dev": true,
"requires": {
"@types/history": "*",
"@types/react": "*"
}
},
"@types/react-router-dom": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-4.3.1.tgz",
"integrity": "sha512-GbztJAScOmQ/7RsQfO4cd55RuH1W4g6V1gDW3j4riLlt+8yxYLqqsiMzmyuXBLzdFmDtX/uU2Bpcm0cmudv44A==",
"dev": true,
"requires": {
"@types/history": "*",
"@types/react": "*",
"@types/react-router": "*"
}
},
"@types/serve-static": { "@types/serve-static": {
"version": "1.13.2", "version": "1.13.2",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.2.tgz", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.2.tgz",
@ -4134,6 +4161,16 @@
"object-assign": "^4.1.1" "object-assign": "^4.1.1"
} }
}, },
"create-react-context": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.2.3.tgz",
"integrity": "sha512-CQBmD0+QGgTaxDL3OX1IDXYqjkp2It4RIbcb99jS6AEg27Ga+a9G3JtK6SIu0HBwPLZlmwt9F7UwWA4Bn92Rag==",
"dev": true,
"requires": {
"fbjs": "^0.8.0",
"gud": "^1.0.0"
}
},
"cross-spawn": { "cross-spawn": {
"version": "6.0.5", "version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
@ -6420,6 +6457,12 @@
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=",
"dev": true "dev": true
}, },
"gud": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz",
"integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==",
"dev": true
},
"handlebars": { "handlebars": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.0.tgz", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.0.tgz",
@ -6544,6 +6587,20 @@
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.13.1.tgz", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.13.1.tgz",
"integrity": "sha512-Sc28JNQNDzaH6PORtRLMvif9RSn1mYuOoX3omVjnb0+HbpPygU2ALBI0R/wsiqCb4/fcp07Gdo8g+fhtFrQl6A==" "integrity": "sha512-Sc28JNQNDzaH6PORtRLMvif9RSn1mYuOoX3omVjnb0+HbpPygU2ALBI0R/wsiqCb4/fcp07Gdo8g+fhtFrQl6A=="
}, },
"history": {
"version": "4.9.0",
"resolved": "https://registry.npmjs.org/history/-/history-4.9.0.tgz",
"integrity": "sha512-H2DkjCjXf0Op9OAr6nJ56fcRkTSNrUiv41vNJ6IswJjif6wlpZK0BTfFbi7qK9dXLSYZxkq5lBsj3vUjlYBYZA==",
"dev": true,
"requires": {
"@babel/runtime": "^7.1.2",
"loose-envify": "^1.2.0",
"resolve-pathname": "^2.2.0",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0",
"value-equal": "^0.4.0"
}
},
"hmac-drbg": { "hmac-drbg": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
@ -10530,6 +10587,56 @@
"react-is": "^16.6.3" "react-is": "^16.6.3"
} }
}, },
"react-router": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-4.4.0.tgz",
"integrity": "sha512-qTGsOSF2b02zOsUfcnHjw7muI0Ejx+yA2e4P9qqzB2O+N3Icpca4epViXRgkBIvBjagXBtroxXqH0RJhYDMUbg==",
"dev": true,
"requires": {
"@babel/runtime": "^7.1.2",
"create-react-context": "^0.2.2",
"history": "^4.9.0",
"hoist-non-react-statics": "^3.1.0",
"loose-envify": "^1.3.1",
"path-to-regexp": "^1.7.0",
"prop-types": "^15.6.2",
"react-is": "^16.6.0",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0"
},
"dependencies": {
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
"dev": true
},
"path-to-regexp": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
"integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
"dev": true,
"requires": {
"isarray": "0.0.1"
}
}
}
},
"react-router-dom": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.4.0.tgz",
"integrity": "sha512-r4knbi8lanTGrwoUXFaWALrJZOAl3h9bdFUz4woHgEm7/bYcpBGfnYhPU82xjXrPeJyWF6OmIxpwXjxos30gOQ==",
"dev": true,
"requires": {
"@babel/runtime": "^7.1.2",
"history": "^4.8.0-beta.0",
"loose-envify": "^1.3.1",
"prop-types": "^15.6.2",
"react-router": "^4.4.0",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0"
}
},
"read": { "read": {
"version": "1.0.7", "version": "1.0.7",
"resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
@ -10880,6 +10987,12 @@
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true "dev": true
}, },
"resolve-pathname": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz",
"integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg==",
"dev": true
},
"resolve-url": { "resolve-url": {
"version": "0.2.1", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
@ -12271,6 +12384,18 @@
"process": "~0.11.0" "process": "~0.11.0"
} }
}, },
"tiny-invariant": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.3.tgz",
"integrity": "sha512-ytQx8T4DL8PjlX53yYzcIC0WhIZbpR0p1qcYjw2pHu3w6UtgWwFJQ/02cnhOnBBhlFx/edUIfcagCaQSe3KMWg==",
"dev": true
},
"tiny-warning": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.2.tgz",
"integrity": "sha512-rru86D9CpQRLvsFG5XFdy0KdLAvjdQDyZCsRcuu60WtzFylDM3eAWSxEVz5kzL2Gp544XiUvPbVKtOA/txLi9Q==",
"dev": true
},
"tmp": { "tmp": {
"version": "0.0.33", "version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
@ -13247,6 +13372,12 @@
"builtins": "^1.0.3" "builtins": "^1.0.3"
} }
}, },
"value-equal": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz",
"integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw==",
"dev": true
},
"vary": { "vary": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",

View File

@ -15,6 +15,7 @@
"@types/es6-shim": "^0.31.39", "@types/es6-shim": "^0.31.39",
"@types/express": "^4.16.0", "@types/express": "^4.16.0",
"@types/express-session": "^1.15.11", "@types/express-session": "^1.15.11",
"@types/history": "^4.7.2",
"@types/http-errors": "^1.6.1", "@types/http-errors": "^1.6.1",
"@types/jest": "^23.3.12", "@types/jest": "^23.3.12",
"@types/js-yaml": "^3.12.0", "@types/js-yaml": "^3.12.0",
@ -24,6 +25,7 @@
"@types/react": "^16.7.18", "@types/react": "^16.7.18",
"@types/react-dom": "^16.0.11", "@types/react-dom": "^16.0.11",
"@types/react-redux": "^6.0.12", "@types/react-redux": "^6.0.12",
"@types/react-router-dom": "^4.3.1",
"@types/shortid": "0.0.29", "@types/shortid": "0.0.29",
"@types/std-mocks": "^1.0.0", "@types/std-mocks": "^1.0.0",
"@types/supertest": "^2.0.7", "@types/supertest": "^2.0.7",
@ -33,6 +35,7 @@
"browserify": "^16.2.3", "browserify": "^16.2.3",
"buildfile": "^1.2.20", "buildfile": "^1.2.20",
"bulma": "^0.7.4", "bulma": "^0.7.4",
"history": "^4.9.0",
"jest": "^24.5.0", "jest": "^24.5.0",
"lerna": "^3.13.1", "lerna": "^3.13.1",
"loose-envify": "^1.4.0", "loose-envify": "^1.4.0",
@ -43,6 +46,7 @@
"react-dom": "^16.7.0", "react-dom": "^16.7.0",
"react-icons": "^3.5.0", "react-icons": "^3.5.0",
"react-redux": "^6.0.0", "react-redux": "^6.0.0",
"react-router-dom": "^4.4.0",
"redux": "^4.0.1", "redux": "^4.0.1",
"std-mocks": "^1.0.1", "std-mocks": "^1.0.1",
"supertest": "^3.3.0", "supertest": "^3.3.0",

View File

@ -1,31 +1,35 @@
import React from 'react' import React from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import {Action} from 'redux' import {Action} from 'redux'
import {IClientConfig} from './IClientConfig'
import {IRenderer} from './IRenderer' import {IRenderer} from './IRenderer'
import {IStoreFactory} from './IStoreFactory' import {IStoreFactory} from './IStoreFactory'
import {Provider} from 'react-redux' import {Provider} from 'react-redux'
export interface IClientRendererParams<State, A extends Action> { export interface IClientRendererParams<State, A extends Action> {
readonly createStore: IStoreFactory<State, A | any>, readonly createStore: IStoreFactory<State, A | any>,
readonly RootComponent: React.ComponentType, readonly RootComponent: React.ComponentType<{config: IClientConfig}>,
readonly target?: HTMLElement readonly target?: HTMLElement
} }
export class ClientRenderer<State, A extends Action> implements IRenderer { export class ClientRenderer<State, A extends Action> implements IRenderer {
constructor(readonly params: IClientRendererParams<State, A>) {} constructor(readonly params: IClientRendererParams<State, A>) {}
render(state = (window as any).__PRELOADED_STATE__) { render(
config = (window as any).__APP_CONFIG__ as IClientConfig,
state = (window as any).__PRELOADED_STATE__,
) {
const { const {
RootComponent, RootComponent,
createStore, createStore,
target = document.body, target = document.getElementById('container'),
} = this.params } = this.params
if (state) { if (state) {
const store = createStore(state) const store = createStore(state)
ReactDOM.hydrate( ReactDOM.hydrate(
<Provider store={store}> <Provider store={store}>
<RootComponent /> <RootComponent config={config} />
</Provider>, </Provider>,
target, target,
) )
@ -33,7 +37,7 @@ export class ClientRenderer<State, A extends Action> implements IRenderer {
const store = createStore() const store = createStore()
ReactDOM.render( ReactDOM.render(
<Provider store={store}> <Provider store={store}>
<RootComponent /> <RootComponent config={config} />
</Provider>, </Provider>,
target, target,
) )

View File

@ -0,0 +1,3 @@
export interface IClientConfig {
readonly baseUrl: string
}

View File

@ -1,3 +1,5 @@
import {IClientConfig} from './IClientConfig'
export interface IRenderer { export interface IRenderer {
render(state?: any): any render(config: IClientConfig, state?: any): any
} }

View File

@ -1,5 +1,6 @@
import React from 'react' import React from 'react'
import {Action} from 'redux' import {Action} from 'redux'
import {IClientConfig} from './IClientConfig'
import {IRenderer} from './IRenderer' import {IRenderer} from './IRenderer'
import {IStoreFactory} from './IStoreFactory' import {IStoreFactory} from './IStoreFactory'
import {Provider} from 'react-redux' import {Provider} from 'react-redux'
@ -8,15 +9,15 @@ import {renderToNodeStream} from 'react-dom/server'
export class ServerRenderer<State, A extends Action> implements IRenderer { export class ServerRenderer<State, A extends Action> implements IRenderer {
constructor( constructor(
readonly createStore: IStoreFactory<State, A | any>, readonly createStore: IStoreFactory<State, A | any>,
readonly RootComponent: React.ComponentType, readonly RootComponent: React.ComponentType<{config: IClientConfig}>,
) {} ) {}
render(state?: any) { render(config: IClientConfig, state?: any) {
const {RootComponent} = this const {RootComponent} = this
const store = this.createStore(state) const store = this.createStore(state)
const stream = renderToNodeStream( const stream = renderToNodeStream(
<Provider store={store}> <Provider store={store}>
<RootComponent /> <RootComponent config={config} />
</Provider>, </Provider>,
) )
return stream return stream

View File

@ -1,3 +1,4 @@
export * from './ClientRenderer' export * from './ClientRenderer'
export * from './IClientConfig'
export * from './IRenderer' export * from './IRenderer'
export * from './IStoreFactory' export * from './IStoreFactory'