React-Router/Redux - 动态同构路由
React-Router/Redux - Dynamic Isomorphic Routes
I'm working off of github's
erikras/react-redux-universal-hot-example repository.
I am trying to make the routes dynamic to aptly reflect a real world scenario.
client/entry.js 页面调用共享模块函数 'getRoutes' 传递存储参数。
const component = (
<Router render={(props) =>
<ReduxAsyncConnect {...props} helpers={{client}}
filter={item => !item.deferred} />
} history={history}>
// where the routes are populated
{getRoutes(store)} // <-- function call
</Router>
);
服务器的同构-routes.config.server.js也调用getRoutes(store)与客户端进行路由匹配:
match({history, routes: getRoutes(store), location: req.originalUrl},
(error, redirectLocation, renderProps) => {
.....
});
共享的 dynamic-routes.shared.js 页面是一个 reg JavaScript 函数 'getRoutes':
export default (store) => {
......
let dynRoutes= [];
store.dispatch(loadNav()).then(result => {
dynRoutes = result;
})
return (
<Route path="/" component={App}>
{ /* Home (main) route */ }
<IndexRoute component={Home}/>
{ /* Get dynamic routes from database */ }
{ dynRoutes.length > 0 ?
<DynamicRoutes dynRoutes={dynRoutes} />
: ''}
{ /* Catch all route */ }
<Route path="*" component={NotFound} status={404} />
</Route>
)
};
而在第二个 'go around' 上,dynRoutes 的数组确实接收到路由集合,return 语句将不会重新处理并且
组件被确定为零长度,回调值将被忽略。
我知道这不是一个提供重新加载新 return 值的好处的 React 组件,但是,有没有人知道我如何获得这个 JavaScript 函数return 与回调值?
谢谢。
标准解决方案是将 getRoutes
设置为一个真正的 React 组件,它遵循 React 组件生命周期,在这种情况下,您可以在回调 returns 时使用 this.setState({ dynRoutes })
触发重新渲染。在其当前设置中,getRoutes
将永远无法强制 caller/parent 重新渲染。
您需要将 store.dispatch(loadNav())
调用与组件呈现分开,并构建代码,以便承诺处理程序在具有 setState
层次结构更上层的组件内触发重新呈现,或再次调用根 ReactDOM.render
。
The short answer is I needed to make not only the
dynamic-routes.shared.js asynchronous,
but also any other function
calls, from both the server & client.
I used
promises on the dynamic-routes.shared.js, and async/await on the
server/client calls to dynamic-routes.shared.js.
I didn't bother throwing the data into a JSON, you can pretty much figure that out yourself.
1.动态-routes.shared.js
function routesWithStore(store) {
return new Promise(function(resolve, reject) {
// you can use something like this to actually have these
// routes in a database
// let dynRoutes= [];
// store.dispatch(loadNav()).then(result => {
// dynRoutes = result;
// })
// resolve(dynRoutes.map(route => {
// ..... your code here .....
// }))
resolve(
{
path: '',
component: App,
childRoutes: [
{path: '/', component: Home},
{path: 'home', component: Home},
{path: 'about', component: About},
{path: '*', component: NotFound}
]
}
)
});
}
function getRoutes(store) {
return(
routesWithStore(store)
)
}
exports.getRoutes = getRoutes;
2.client/entry.js
// async call to dynamic-routes.shared.js ////////
async function main() {
try {
const result = await getRoutes(store);
processRoutes(result);
} catch(err) {
console.log(err.message)
}
}
function processRoutes(result) {
const component = (
<Router render={(props) =>
<ReduxAsyncConnect {...props} helpers={{client}}
filter={item => !item.deferred} />
} history={history}>
{result} <------------- client value from dynamic-routes.shared.js
</Router>
);
ReactDOM.render(
<Provider store={store} key="provider">
{component}
</Provider>,
document.querySelector('#root');
);
_
if (process.env.NODE_ENV !== 'production') {
window.React = React; // enable debugger
if (!dest || !dest.firstChild
|| !dest.firstChild.attributes
|| !dest.firstChild.attributes['data-react-checksum']) {
console.error
('Server-side React render was discarded. ' +
'Make sure that your initial render does not contain any client-side code.');
}
}
if (__DEVTOOLS__ && !window.devToolsExtension) {
const DevTools = require('shared/redux/dev-tools/dev-tools.redux.shared');
ReactDOM.render(
<Provider store={store} key="provider">
<div>
{component}
<DevTools />
</div>
</Provider>,
document.querySelector('#root');
);
}
}
main();
3。同构-routes.config.server
module.exports = (app) => {
app.use((req, res) => {
if (__DEVELOPMENT__) {
// Do not cache webpack stats: the script file would change since
// hot module replacement is enabled in the development env
webpackIsomorphicTools.refresh();
}
const client = new ApiClient(req);
const memoryHistory = createHistory(req.originalUrl);
const store = createStore(memoryHistory, client);
const history = syncHistoryWithStore(memoryHistory, store);
function hydrateOnClient() {
res.send('<!doctype html>\n' +
ReactDOM.renderToString(
<Html assets={webpackIsomorphicTools.assets()}
store={store}/>));
}
if (__DISABLE_SSR__) {
hydrateOnClient();
return;
}
_
// Async call to dynamic-routes.shared.js ////////
async function main() {
try {
const routesResult = await getRoutes(store);
// pass routesResult below
match({history, routes: routesResult, location: req.originalUrl},
(error, redirectLocation, renderProps) => {
if (redirectLocation) {
res.redirect(redirectLocation.pathname + redirectLocation.search);
} else if (error) {
console.error('ROUTER ERROR:', pretty.render(error));
res.status(500);
hydrateOnClient();
_
} else if (renderProps) {
loadOnServer({...renderProps, store, helpers: {client}}).then(() => {
const component = (
<Provider store={store} key="provider">
<ReduxAsyncConnect {...renderProps} />
</Provider>
);
res.status(200);
global.navigator = {userAgent: req.headers['user-agent']};
res.send('<!doctype html>\n' +
ReactDOM.renderToString(
<Html assets={webpackIsomorphicTools.assets()}
component={component} store={store}/>));
});
} else {
res.status(404).send('Iso Routes Not Found ' + routeResult);
}
});
} catch(error) {
console.error(error);
}
}
main();
});
};
我希望这对希望使同构路由动态化的任何人有所帮助。
让美国再次伟大!
I'm working off of github's erikras/react-redux-universal-hot-example repository.
I am trying to make the routes dynamic to aptly reflect a real world scenario.
client/entry.js 页面调用共享模块函数 'getRoutes' 传递存储参数。
const component = ( <Router render={(props) => <ReduxAsyncConnect {...props} helpers={{client}} filter={item => !item.deferred} /> } history={history}> // where the routes are populated {getRoutes(store)} // <-- function call </Router>
);
服务器的同构-routes.config.server.js也调用getRoutes(store)与客户端进行路由匹配:
match({history, routes: getRoutes(store), location: req.originalUrl}, (error, redirectLocation, renderProps) => { ..... });
共享的 dynamic-routes.shared.js 页面是一个 reg JavaScript 函数 'getRoutes':
export default (store) => { ...... let dynRoutes= []; store.dispatch(loadNav()).then(result => { dynRoutes = result; }) return ( <Route path="/" component={App}> { /* Home (main) route */ } <IndexRoute component={Home}/> { /* Get dynamic routes from database */ } { dynRoutes.length > 0 ? <DynamicRoutes dynRoutes={dynRoutes} /> : ''} { /* Catch all route */ } <Route path="*" component={NotFound} status={404} /> </Route> ) };
而在第二个 'go around' 上,dynRoutes 的数组确实接收到路由集合,return 语句将不会重新处理并且 组件被确定为零长度,回调值将被忽略。
我知道这不是一个提供重新加载新 return 值的好处的 React 组件,但是,有没有人知道我如何获得这个 JavaScript 函数return 与回调值?
谢谢。
标准解决方案是将 getRoutes
设置为一个真正的 React 组件,它遵循 React 组件生命周期,在这种情况下,您可以在回调 returns 时使用 this.setState({ dynRoutes })
触发重新渲染。在其当前设置中,getRoutes
将永远无法强制 caller/parent 重新渲染。
您需要将 store.dispatch(loadNav())
调用与组件呈现分开,并构建代码,以便承诺处理程序在具有 setState
层次结构更上层的组件内触发重新呈现,或再次调用根 ReactDOM.render
。
The short answer is I needed to make not only the dynamic-routes.shared.js asynchronous,
but also any other function calls, from both the server & client.
I used promises on the dynamic-routes.shared.js, and async/await on the server/client calls to dynamic-routes.shared.js.
I didn't bother throwing the data into a JSON, you can pretty much figure that out yourself.
1.动态-routes.shared.js
function routesWithStore(store) {
return new Promise(function(resolve, reject) {
// you can use something like this to actually have these
// routes in a database
// let dynRoutes= [];
// store.dispatch(loadNav()).then(result => {
// dynRoutes = result;
// })
// resolve(dynRoutes.map(route => {
// ..... your code here .....
// }))
resolve(
{
path: '',
component: App,
childRoutes: [
{path: '/', component: Home},
{path: 'home', component: Home},
{path: 'about', component: About},
{path: '*', component: NotFound}
]
}
)
});
}
function getRoutes(store) {
return(
routesWithStore(store)
)
}
exports.getRoutes = getRoutes;
2.client/entry.js
// async call to dynamic-routes.shared.js ////////
async function main() {
try {
const result = await getRoutes(store);
processRoutes(result);
} catch(err) {
console.log(err.message)
}
}
function processRoutes(result) {
const component = (
<Router render={(props) =>
<ReduxAsyncConnect {...props} helpers={{client}}
filter={item => !item.deferred} />
} history={history}>
{result} <------------- client value from dynamic-routes.shared.js
</Router>
);
ReactDOM.render(
<Provider store={store} key="provider">
{component}
</Provider>,
document.querySelector('#root');
);
_
if (process.env.NODE_ENV !== 'production') {
window.React = React; // enable debugger
if (!dest || !dest.firstChild
|| !dest.firstChild.attributes
|| !dest.firstChild.attributes['data-react-checksum']) {
console.error
('Server-side React render was discarded. ' +
'Make sure that your initial render does not contain any client-side code.');
}
}
if (__DEVTOOLS__ && !window.devToolsExtension) {
const DevTools = require('shared/redux/dev-tools/dev-tools.redux.shared');
ReactDOM.render(
<Provider store={store} key="provider">
<div>
{component}
<DevTools />
</div>
</Provider>,
document.querySelector('#root');
);
}
}
main();
3。同构-routes.config.server
module.exports = (app) => {
app.use((req, res) => {
if (__DEVELOPMENT__) {
// Do not cache webpack stats: the script file would change since
// hot module replacement is enabled in the development env
webpackIsomorphicTools.refresh();
}
const client = new ApiClient(req);
const memoryHistory = createHistory(req.originalUrl);
const store = createStore(memoryHistory, client);
const history = syncHistoryWithStore(memoryHistory, store);
function hydrateOnClient() {
res.send('<!doctype html>\n' +
ReactDOM.renderToString(
<Html assets={webpackIsomorphicTools.assets()}
store={store}/>));
}
if (__DISABLE_SSR__) {
hydrateOnClient();
return;
}
_
// Async call to dynamic-routes.shared.js ////////
async function main() {
try {
const routesResult = await getRoutes(store);
// pass routesResult below
match({history, routes: routesResult, location: req.originalUrl},
(error, redirectLocation, renderProps) => {
if (redirectLocation) {
res.redirect(redirectLocation.pathname + redirectLocation.search);
} else if (error) {
console.error('ROUTER ERROR:', pretty.render(error));
res.status(500);
hydrateOnClient();
_
} else if (renderProps) {
loadOnServer({...renderProps, store, helpers: {client}}).then(() => {
const component = (
<Provider store={store} key="provider">
<ReduxAsyncConnect {...renderProps} />
</Provider>
);
res.status(200);
global.navigator = {userAgent: req.headers['user-agent']};
res.send('<!doctype html>\n' +
ReactDOM.renderToString(
<Html assets={webpackIsomorphicTools.assets()}
component={component} store={store}/>));
});
} else {
res.status(404).send('Iso Routes Not Found ' + routeResult);
}
});
} catch(error) {
console.error(error);
}
}
main();
});
};
我希望这对希望使同构路由动态化的任何人有所帮助。
让美国再次伟大!