mirror of
https://github.com/vale981/Vulcan
synced 2025-03-06 18:11:40 -05:00
Merge pull request #1543 from comus/patch-1
separate client side and server side routing
This commit is contained in:
commit
fcdf8505f2
9 changed files with 143 additions and 78 deletions
|
@ -9,12 +9,6 @@ const ReactRouterSSR = {
|
|||
clientOptions = {};
|
||||
}
|
||||
|
||||
let history = browserHistory;
|
||||
|
||||
if(typeof clientOptions.historyHook === 'function') {
|
||||
history = clientOptions.historyHook(history);
|
||||
}
|
||||
|
||||
Meteor.startup(function() {
|
||||
const rootElementName = clientOptions.rootElement || 'react-app';
|
||||
const rootElementType = clientOptions.rootElementType || 'div';
|
||||
|
@ -47,6 +41,12 @@ const ReactRouterSSR = {
|
|||
});
|
||||
}
|
||||
|
||||
let history = browserHistory;
|
||||
|
||||
if(typeof clientOptions.historyHook === 'function') {
|
||||
history = clientOptions.historyHook(history);
|
||||
}
|
||||
|
||||
let app = (
|
||||
<Router
|
||||
history={history}
|
||||
|
|
|
@ -79,7 +79,7 @@ ReactRouterSSR.Run = function(routes, clientOptions, serverOptions) {
|
|||
let history = createMemoryHistory(req.url);
|
||||
|
||||
if (typeof serverOptions.historyHook === 'function') {
|
||||
history = serverOptions.historyHook(history);
|
||||
history = serverOptions.historyHook(req, res, history);
|
||||
}
|
||||
|
||||
ReactRouterMatch({ history, routes, location: req.url }, Meteor.bindEnvironment((err, redirectLocation, renderProps) => {
|
||||
|
@ -194,6 +194,7 @@ function generateSSRData(clientOptions, serverOptions, req, res, renderProps) {
|
|||
|
||||
global.__STYLE_COLLECTOR_MODULES__ = [];
|
||||
global.__STYLE_COLLECTOR__ = '';
|
||||
req.css = '';
|
||||
|
||||
renderProps = {
|
||||
...renderProps,
|
||||
|
@ -203,9 +204,9 @@ function generateSSRData(clientOptions, serverOptions, req, res, renderProps) {
|
|||
// fetchComponentData(serverOptions, renderProps);
|
||||
let app = <RouterContext {...renderProps} />;
|
||||
|
||||
if (typeof clientOptions.wrapperHook === 'function') {
|
||||
if (typeof serverOptions.wrapperHook === 'function') {
|
||||
const loginToken = req.cookies['meteor_login_token'];
|
||||
app = clientOptions.wrapperHook(app, loginToken);
|
||||
app = serverOptions.wrapperHook(req, res, app, loginToken);
|
||||
}
|
||||
|
||||
if (serverOptions.preRender) {
|
||||
|
@ -221,7 +222,7 @@ function generateSSRData(clientOptions, serverOptions, req, res, renderProps) {
|
|||
css = global.__STYLE_COLLECTOR__;
|
||||
|
||||
if (typeof serverOptions.dehydrateHook === 'function') {
|
||||
const data = serverOptions.dehydrateHook();
|
||||
const data = serverOptions.dehydrateHook(req, res);
|
||||
InjectData.pushData(res, 'dehydrated-initial-data', JSON.stringify(data));
|
||||
}
|
||||
|
||||
|
@ -229,6 +230,8 @@ function generateSSRData(clientOptions, serverOptions, req, res, renderProps) {
|
|||
serverOptions.postRender(req, res);
|
||||
}
|
||||
|
||||
css = css + req.css;
|
||||
|
||||
// I'm pretty sure this could be avoided in a more elegant way?
|
||||
const context = FastRender.frContext.get();
|
||||
const data = context.getData();
|
||||
|
|
|
@ -4,7 +4,7 @@ HoC that provides access to flash messages stored in Redux state and actions to
|
|||
|
||||
*/
|
||||
|
||||
import { Actions, addAction, addReducer } from 'meteor/nova:lib';
|
||||
import { getActions, addAction, addReducer } from 'meteor/nova:lib';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
|
@ -43,8 +43,8 @@ addReducer({
|
|||
messages: (state = [], action) => {
|
||||
// default values
|
||||
const flashType = typeof action.flashType === 'undefined' ? 'error' : action.flashType;
|
||||
const currentMsg = typeof action.i === 'undefined' ? {} : state[action.i];
|
||||
|
||||
const currentMsg = typeof action.i === 'undefined' ? {} : state[action.i];
|
||||
|
||||
switch(action.type) {
|
||||
case 'FLASH':
|
||||
return [
|
||||
|
@ -57,7 +57,7 @@ addReducer({
|
|||
{ ...currentMsg, seen: true },
|
||||
...state.slice(action.i + 1),
|
||||
];
|
||||
case 'CLEAR':
|
||||
case 'CLEAR':
|
||||
return [
|
||||
...state.slice(0, action.i),
|
||||
{ ...currentMsg, show: false },
|
||||
|
@ -72,7 +72,7 @@ addReducer({
|
|||
});
|
||||
|
||||
const mapStateToProps = state => ({ messages: state.messages, });
|
||||
const mapDispatchToProps = dispatch => bindActionCreators(Actions.messages, dispatch);
|
||||
const mapDispatchToProps = dispatch => bindActionCreators(getActions().messages, dispatch);
|
||||
|
||||
const withMessages = component => connect(mapStateToProps, mapDispatchToProps)(component);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// import and re-export
|
||||
export { Components, registerComponent, replaceComponent, getRawComponent, getComponent, copyHoCs, populateComponentsApp, createCollection, Callbacks, addCallback, removeCallback, runCallbacks, runCallbacksAsync, GraphQLSchema, Routes, addRoute, getRoute, populateRoutesApp, Utils, getSetting, Strings, addStrings, configureStore, Actions, addAction, Reducers, addReducer, Middleware, addMiddleware, Headtags } from 'meteor/nova:lib';
|
||||
export { Components, registerComponent, replaceComponent, getRawComponent, getComponent, copyHoCs, populateComponentsApp, createCollection, Callbacks, addCallback, removeCallback, runCallbacks, runCallbacksAsync, GraphQLSchema, Routes, addRoute, getRoute, populateRoutesApp, Utils, getSetting, Strings, addStrings, configureStore, getActions, addAction, getReducers, addReducer, getMiddleware, addMiddleware, Headtags } from 'meteor/nova:lib';
|
||||
|
||||
export { default as App } from "./components/App.jsx";
|
||||
export { default as Layout } from "./components/Layout.jsx";
|
||||
|
|
|
@ -18,7 +18,7 @@ export { Routes, addRoute, getRoute, populateRoutesApp } from './routes.js';
|
|||
export { Utils } from './utils.js';
|
||||
export { getSetting } from './settings.js';
|
||||
export { Strings, addStrings } from './strings.js';
|
||||
export { configureStore, Actions, addAction, Reducers, addReducer, Middleware, addMiddleware } from './redux.js';
|
||||
export { configureStore, getActions, addAction, getReducers, addReducer, getMiddleware, addMiddleware } from './redux.js';
|
||||
export { Headtags } from './headtags.js';
|
||||
|
||||
export default Telescope;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
|
||||
|
||||
export const configureStore = (reducers, initialState = {}, middleware) => createStore(
|
||||
// reducers
|
||||
// reducers
|
||||
combineReducers(reducers),
|
||||
// initial state
|
||||
initialState,
|
||||
|
@ -12,25 +12,28 @@ export const configureStore = (reducers, initialState = {}, middleware) => creat
|
|||
)
|
||||
);
|
||||
|
||||
export let Actions = {};
|
||||
let actions = {};
|
||||
export const addAction = addedAction => {
|
||||
Actions = {...Actions, ...addedAction};
|
||||
|
||||
return Actions;
|
||||
};
|
||||
actions = {...actions, ...addedAction};
|
||||
|
||||
export let Reducers = {};
|
||||
return actions;
|
||||
};
|
||||
export const getActions = () => actions;
|
||||
|
||||
let reducers = {};
|
||||
export const addReducer = addedReducer => {
|
||||
Reducers = {...Reducers, ...addedReducer};
|
||||
|
||||
return Reducers;
|
||||
};
|
||||
reducers = {...reducers, ...addedReducer};
|
||||
|
||||
export let Middleware = [];
|
||||
return reducers;
|
||||
};
|
||||
export const getReducers = () => reducers;
|
||||
|
||||
let middleware = [];
|
||||
export const addMiddleware = middlewareOrMiddlewareArray => {
|
||||
const addedMiddleware = Array.isArray(middlewareOrMiddlewareArray) ? middlewareOrMiddlewareArray : [middlewareOrMiddlewareArray];
|
||||
|
||||
Middleware = [...Middleware, ...addedMiddleware];
|
||||
|
||||
return Middleware;
|
||||
|
||||
middleware = [...middleware, ...addedMiddleware];
|
||||
|
||||
return middleware;
|
||||
};
|
||||
export const getMiddleware = () => middleware;
|
||||
|
|
|
@ -1,23 +1,22 @@
|
|||
import React from 'react';
|
||||
import { ReactRouterSSR } from 'meteor/reactrouter:react-router-ssr';
|
||||
import Helmet from 'react-helmet';
|
||||
import Cookie from 'react-cookie';
|
||||
import ReactDOM from 'react-dom';
|
||||
import ApolloClient from 'apollo-client';
|
||||
import { getDataFromTree, ApolloProvider } from 'react-apollo';
|
||||
import { meteorClientConfig } from 'meteor/nova:apollo';
|
||||
import { Components, populateComponentsApp, Actions, runCallbacks, addRoute, Routes, populateRoutesApp, configureStore, addReducer, addMiddleware } from 'meteor/nova:core';
|
||||
import { ApolloProvider } from 'react-apollo';
|
||||
import { applyRouterMiddleware } from 'react-router';
|
||||
import { useScroll } from 'react-router-scroll';
|
||||
|
||||
import { ReactRouterSSR } from 'meteor/reactrouter:react-router-ssr';
|
||||
|
||||
import { meteorClientConfig } from 'meteor/nova:apollo';
|
||||
import { Components, populateComponentsApp, getActions, runCallbacks, addRoute, Routes, populateRoutesApp, configureStore, addReducer, addMiddleware } from 'meteor/nova:core';
|
||||
|
||||
Meteor.startup(function initNovaRoutesAndApollo() {
|
||||
|
||||
|
||||
// note: route defined here because it "shouldn't be removable"
|
||||
addRoute({name:"app.notfound", path:"*", componentName: 'Error404'});
|
||||
|
||||
|
||||
// uncomment for debug
|
||||
// console.log('// --> starting routing');
|
||||
|
||||
|
||||
// init the application components and routes, including components & routes from 3rd-party packages
|
||||
populateComponentsApp();
|
||||
populateRoutesApp();
|
||||
|
@ -34,64 +33,45 @@ Meteor.startup(function initNovaRoutesAndApollo() {
|
|||
};
|
||||
|
||||
/*
|
||||
Hooks client side and server side definition
|
||||
Hooks client side definition
|
||||
*/
|
||||
|
||||
|
||||
let history;
|
||||
let initialState;
|
||||
let store;
|
||||
let client;
|
||||
|
||||
// Use history hook to get a reference to the history object
|
||||
const historyHook = newHistory => history = newHistory;
|
||||
|
||||
const clientOptions = {
|
||||
historyHook,
|
||||
rehydrateHook: state => {
|
||||
// console.log('rehydrated state', state);
|
||||
initialState = state
|
||||
},
|
||||
wrapperHook(app, loginToken) {
|
||||
// console.log('wrapper hook initial state', initialState);
|
||||
|
||||
// configure apollo
|
||||
client = new ApolloClient(meteorClientConfig({cookieLoginToken: loginToken}));
|
||||
client = new ApolloClient(meteorClientConfig());
|
||||
const reducers = addReducer({apollo: client.reducer()});
|
||||
const middleware = addMiddleware(client.middleware());
|
||||
|
||||
|
||||
// configure the redux store
|
||||
store = configureStore(reducers, initialState, middleware);
|
||||
|
||||
return <ApolloProvider store={store} client={client}>{app}</ApolloProvider>
|
||||
},
|
||||
historyHook(newHistory) {
|
||||
// Use history hook to get a reference to the history object
|
||||
history = newHistory
|
||||
return history;
|
||||
},
|
||||
props: {
|
||||
onUpdate: () => {
|
||||
runCallbacks('router.onUpdate');
|
||||
// clear all previous messages
|
||||
store.dispatch(Actions.messages.clearSeen());
|
||||
store.dispatch(getActions().messages.clearSeen());
|
||||
},
|
||||
render: applyRouterMiddleware(useScroll())
|
||||
},
|
||||
wrapperHook(app, loginToken) {
|
||||
// console.log('wrapper hook initial state', initialState);
|
||||
return <ApolloProvider store={store} client={client}>{app}</ApolloProvider>
|
||||
},
|
||||
};
|
||||
|
||||
const serverOptions = {
|
||||
historyHook,
|
||||
htmlHook: (html) => {
|
||||
const head = Helmet.rewind();
|
||||
return html.replace('<head>', '<head>'+ head.title + head.meta + head.link);
|
||||
},
|
||||
preRender: (req, res, app) => {
|
||||
Cookie.plugToRequest(req, res);
|
||||
//console.log('preRender hook', app);
|
||||
// console.log(req.cookies);
|
||||
return Promise.await(getDataFromTree(app));
|
||||
},
|
||||
dehydrateHook: () => {
|
||||
// console.log(client.store.getState());
|
||||
return client.store.getState();
|
||||
},
|
||||
// fetchDataHook: (components) => components,
|
||||
};
|
||||
|
||||
ReactRouterSSR.Run(AppRoutes, clientOptions, serverOptions);
|
||||
ReactRouterSSR.Run(AppRoutes, clientOptions, {});
|
||||
});
|
78
packages/nova-routing/lib/routing-server.jsx
Normal file
78
packages/nova-routing/lib/routing-server.jsx
Normal file
|
@ -0,0 +1,78 @@
|
|||
import React from 'react';
|
||||
import Helmet from 'react-helmet';
|
||||
import Cookie from 'react-cookie';
|
||||
import ApolloClient from 'apollo-client';
|
||||
import { getDataFromTree, ApolloProvider } from 'react-apollo';
|
||||
|
||||
import { ReactRouterSSR } from 'meteor/reactrouter:react-router-ssr';
|
||||
|
||||
import { meteorClientConfig } from 'meteor/nova:apollo';
|
||||
import { Components, populateComponentsApp, addRoute, Routes, populateRoutesApp, configureStore, getReducers, getMiddleware } from 'meteor/nova:core';
|
||||
|
||||
Meteor.startup(function initNovaRoutesAndApollo() {
|
||||
|
||||
// note: route defined here because it "shouldn't be removable"
|
||||
addRoute({name:"app.notfound", path:"*", componentName: 'Error404'});
|
||||
|
||||
// uncomment for debug
|
||||
// console.log('// --> starting routing');
|
||||
|
||||
// init the application components and routes, including components & routes from 3rd-party packages
|
||||
populateComponentsApp();
|
||||
populateRoutesApp();
|
||||
|
||||
const indexRoute = _.filter(Routes, route => route.path === '/')[0];
|
||||
const childRoutes = _.reject(Routes, route => route.path === '/');
|
||||
delete indexRoute.path; // delete the '/' path to avoid warning
|
||||
|
||||
const AppRoutes = {
|
||||
path: '/',
|
||||
component: Components.App,
|
||||
indexRoute,
|
||||
childRoutes,
|
||||
};
|
||||
|
||||
/*
|
||||
Hooks server side definition
|
||||
*/
|
||||
|
||||
const serverOptions = {
|
||||
historyHook(req, res, newHistory) {
|
||||
// Use history hook to get a reference to the history object
|
||||
req.history = newHistory
|
||||
return req.history;
|
||||
},
|
||||
wrapperHook(req, res, app, loginToken) {
|
||||
// console.log('wrapper hook');
|
||||
|
||||
// configure apollo
|
||||
req.apolloClient = new ApolloClient(meteorClientConfig({ cookieLoginToken: loginToken }));
|
||||
const reducers = { ...getReducers(), apollo: req.apolloClient.reducer() };
|
||||
const middleware = [...getMiddleware(), req.apolloClient.middleware()];
|
||||
|
||||
// configure the redux store
|
||||
req.store = configureStore(reducers, {}, middleware);
|
||||
|
||||
return <ApolloProvider store={req.store} client={req.apolloClient}>{app}</ApolloProvider>
|
||||
},
|
||||
preRender(req, res, app) {
|
||||
Cookie.plugToRequest(req, res);
|
||||
//console.log('preRender hook', app);
|
||||
// console.log(req.cookies);
|
||||
return Promise.await(getDataFromTree(app));
|
||||
},
|
||||
dehydrateHook(req, res) {
|
||||
// console.log(client.store.getState());
|
||||
return req.apolloClient.store.getState();
|
||||
},
|
||||
postRender(req, res) {
|
||||
// console.log('postrender hook');
|
||||
},
|
||||
htmlHook(html) {
|
||||
const head = Helmet.rewind();
|
||||
return html.replace('<head>', '<head>'+ head.title + head.meta + head.link);
|
||||
},
|
||||
};
|
||||
|
||||
ReactRouterSSR.Run(AppRoutes, {}, serverOptions);
|
||||
});
|
|
@ -15,6 +15,7 @@ Package.onUse(function (api) {
|
|||
'nova:apollo@1.0.0',
|
||||
]);
|
||||
|
||||
api.mainModule('lib/routing.jsx', ['client', 'server']);
|
||||
api.mainModule('lib/routing-server.jsx', 'server');
|
||||
api.mainModule('lib/routing-client.jsx', 'client');
|
||||
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue