// routes.js module.exports = [ { path: '/', exact: true, // component: Home, componentName: 'home' }, { path: '/users', exact: true, // component: UsersList, componentName: 'components/usersList', }, { path: '/users/:id', exact: true, // component: User, componentName: 'components/user', }, ];
import React from 'react'; import { Switch, Route } from 'react-router'; import routes from './routes'; import Layout from './components/layout' export default (data) => ( <Layout> <Switch> { routes.map(props => { props.component = require('./' + props.componentName); if (props.component.default) { props.component = props.component.default; } return <Route key={ props.path } {...props}/> }) } </Switch> </Layout> );
<Layout/>
in Next.js was the starting point for writing this article. import React from 'react'; import { Router, Route, Switch} from 'react-router'; import routes from './routes'; import Loadable from 'react-loadable'; import Layout from './components/layout'; export default (data) => ( <Layout> <Switch> { routes.map(props => { props.component = Loadable({ loader: () => import('./' + props.componentName), loading: () => null, delay: () => 0, timeout: 10000, }); return <Route key={ props.path } {...props}/>; }) } </Switch> </Layout> );
() => import('./' + props.componentName)
. The import () function gives the webpack command to implement code splitting. If the page had the usual import or require () construction, then the webpack would include the component code in one resulting file. And so the code will be loaded when switching to a route from a separate code fragment. 'use strict' import React from 'react'; import { hydrate } from 'react-dom'; import { Provider } from 'react-redux'; import {BrowserRouter} from 'react-router-dom'; import Layout from './react/components/layout'; import AppRouter from './react/clientRouter'; import routes from './react/routes'; import createStore from './redux/store'; const preloadedState = window.__PRELOADED_STATE__; delete window.__PRELOADED_STATE__; const store = createStore(preloadedState); const component = hydrate( <Provider store={store}> <BrowserRouter> <AppRouter /> </BrowserRouter> </Provider>, document.getElementById('app') );
import { matchPath } from 'react-router-dom'; import routes from './react/routes'; import AppRouter from './react/serverRouter'; import stats from '../dist/stats.generated'; ... app.use('/', async function(req, res, next) { const store = createStore(); const promises = []; const componentNames = []; routes.forEach(route => { const match = matchPath(req.path, route); if (match) { let component = require('./react/' + route.componentName); if (component.default) { component = component.default; } componentNames.push(route.componentName); if (typeof component.getInitialProps == 'function') { promises.push(component.getInitialProps({req, res, next, match, store})); } } return match; }) Promise.all(promises).then(data => { const context = {data}; const html = ReactDOMServer.renderToString( <Provider store={store}> <StaticRouter location={req.url} context={context}> <AppRouter/> </StaticRouter> </Provider> ); if (context.url) { res.writeHead(301, { Location: context.url }) res.end() } else { res.write(` <!doctype html> <script> // WARNING: See the following for security issues around embedding JSON in HTML: // http://redux.js.org/docs/recipes/ServerRendering.html#security-considerations window.__PRELOADED_STATE__ = ${JSON.stringify(store.getState()).replace(/</g, '\\u003c')} </script> <div id="app">${html}</div> <script src='${assets(stats.common)}'></script> ${componentNames.map(componentName => `<script src='${assets(stats[componentName])}'></script>` )} `) res.end() } }) });
routes.forEach(route => { const match = matchPath(req.path, route); if (match) { let component = require('./react/' + route.componentName); if (component.default) { component = component.default; } componentNames.push(route.componentName); if (typeof component.getInitialProps == 'function') { promises.push(component.getInitialProps({req, res, next, match, store})); } } return match; })
component.getInitialProps({req, res, next, match, store})
. Static - because a component instance on the server has not yet been created. This method is named by analogy with Next.js. Here is how this method might look like in a component: class Home extends React.PureComponent { static async getInitialProps({ req, match, store, dispatch }) { const userAgent = req ? req.headers['user-agent'] : navigator.userAgent const action = userActions.login({name: 'John', userAgent}); if (req) { await store.dispatch(action); } else { dispatch(action); } return; }
Source: https://habr.com/ru/post/349064/
All Articles