使用 React Router 4 预取数据
Prefetch data with React Router 4
我正在尝试迁移到 React Router 的 v4,但我受困于预取数据。我们以前的最新版本有 match 功能。
其中一个返回参数是 renderProps,但现在我不能使用 match,所以我不知道如何预取数据。 documentation shows how to load data but doesn't works for me because I'm using the new version of react-router-redux 同步商店。
这是客户端的代码:
return (
<MuiThemeProvider muiTheme={letgoMuiTheme}>
<Provider store={store}>
<IntlProvider>
{ routes(history, store) }
</IntlProvider>
</Provider>
</MuiThemeProvider>
);
路线:
export default function routes(history) {
return (
<ConnectedRouter history={history}>
<Switch>
<Route exact path="/" component={App} />
<Route path="/:lang/chat" component={Chat} />
...
<Route path="*" component={NotFound} status={404} />
</Switch>
</ConnectedRouter>
);
}
服务器:
if (context.url) {
res.redirect(302, redirectLocation.pathname + redirectLocation.search);
} else {
const render = () => {
const body = renderToString(
<StaticRouter
location={req.url}
context={context}
>
<MuiThemeProvider muiTheme={muiTheme}>
<Provider store={store}>
<IntlProvider>
{ routes(history, store) }
</IntlProvider>
</Provider>
</MuiThemeProvider>,
</StaticRouter>
);
res.setHeader('content-language', locale);
res.render('index', {
head,
body,
locale,
state: stringState,
...
});
};
Promise.all(
prefetchData(renderProps, store),
)
.then(render)
.catch(prefetchError => next(prefetchError));
}
这是我的旧服务器文件:
match({ history, routes, location: req.url }, (error, redirectLocation, renderProps) => {
if (error) {
throw error;
} else if (redirectLocation) {
res.redirect(302, redirectLocation.pathname + redirectLocation.search);
} else if (renderProps) {
// Render react components once store is initializaed and return HTTP response
const render = () => {
const body = renderToString(
<MuiThemeProvider muiTheme={muiTheme}>
<Provider store={store}>
<IntlProvider>
<RouterContext {...renderProps} />
</IntlProvider>
</Provider>
</MuiThemeProvider>,
);
res.setHeader('content-language', locale);
res.render('index', {
head,
newrelicScript,
body,
locale,
state: stringState,
...
});
};
// Fetch components data and render HTML (no matter the fetch results
// we need to render something)
Promise.all(
prefetchData(renderProps, store),
)
.then(render)
.catch(prefetchError => next(prefetchError));
} else {
logger.warning('Ops !!! We should never arrive here :(');
next();
}
});
并预取数据:
export const prefetchData = (renderProps, store) => {
const { components, params } = renderProps;
const { dispatch } = store;
const state = store.getState();
const extractWrappedComponent = (component) => {
if (component.WrappedComponent) {
return extractWrappedComponent(component.WrappedComponent);
}
return component;
};
return components
.filter(component => component !== undefined)
// Get component, be aware if they are wrapped by redux connect or intl.
.map(component => extractWrappedComponent(component))
// Get the fetchData method of each component
.map(component => component.fetchData)
.filter(fetchData => fetchData !== undefined)
// Invoke each fetchData to initialize the redux state.
.map(fetchData => fetchData(state, dispatch, params));
};
在研究之后,我找到了适合我的解决方案。这是助手(之前是 prefetchData)。 matchPath
来自 react-router-dom
.
export const getRouteData = (routesArray, url, store) => {
const { dispatch } = store;
const state = store.getState();
const extractWrappedComponent = (component) => {
if (component.WrappedComponent) {
return extractWrappedComponent(component.WrappedComponent);
}
return component;
};
return routesArray.map((route) => {
const component = route.component;
const extractedComponent = extractWrappedComponent(component);
const match = matchPath(url, { path: route.path, exact: route.exact, strict: false });
if (match) {
if (extractedComponent.fetchData !== undefined) {
extractedComponent.fetchData(state, dispatch, match.params);
}
}
});
}
那我就叫它:
getRouteData(routes, req.url, store);
路线是 static routes:
export const routes = [
{
path: '/',
component: App
},
{
path: '/:lang/chat',
component: Chat
}
...
我的服务器文件现在没有匹配项。相反:
if (context.url) {
res.redirect(302, context.url);
} else {
const render = () => {
const body = renderApp(req, muiTheme, store, history, context);
res.render('index', {
head,
body,
locale,
...
});
};
Promise.all(
getRouteData(routes, req.url, store),
)
.then(render)
.catch(prefetchError => next(prefetchError));
RenderApp 文件(客户端必须与 StaticRouter
相同):
const renderApp = (req, muiTheme, store, history, context) => {
const app = (
<MuiThemeProvider muiTheme={muiTheme}>
<Provider store={store}>
<IntlProvider>
<StaticRouter
location={req.url}
context={context}
>
{ appRoutes(history) }
</StaticRouter>
</IntlProvider>
</Provider>
</MuiThemeProvider>
);
return renderToString(app);
};
appRoutes 文件:
export default function appRoutes(history) {
return (
<ConnectedRouter history={history}>
<div>
{routes.map((route, key) => (
<Route key={key} {...route} />
))}
</div>
</ConnectedRouter>
);
}
我正在尝试迁移到 React Router 的 v4,但我受困于预取数据。我们以前的最新版本有 match 功能。
其中一个返回参数是 renderProps,但现在我不能使用 match,所以我不知道如何预取数据。 documentation shows how to load data but doesn't works for me because I'm using the new version of react-router-redux 同步商店。
这是客户端的代码:
return (
<MuiThemeProvider muiTheme={letgoMuiTheme}>
<Provider store={store}>
<IntlProvider>
{ routes(history, store) }
</IntlProvider>
</Provider>
</MuiThemeProvider>
);
路线:
export default function routes(history) {
return (
<ConnectedRouter history={history}>
<Switch>
<Route exact path="/" component={App} />
<Route path="/:lang/chat" component={Chat} />
...
<Route path="*" component={NotFound} status={404} />
</Switch>
</ConnectedRouter>
);
}
服务器:
if (context.url) {
res.redirect(302, redirectLocation.pathname + redirectLocation.search);
} else {
const render = () => {
const body = renderToString(
<StaticRouter
location={req.url}
context={context}
>
<MuiThemeProvider muiTheme={muiTheme}>
<Provider store={store}>
<IntlProvider>
{ routes(history, store) }
</IntlProvider>
</Provider>
</MuiThemeProvider>,
</StaticRouter>
);
res.setHeader('content-language', locale);
res.render('index', {
head,
body,
locale,
state: stringState,
...
});
};
Promise.all(
prefetchData(renderProps, store),
)
.then(render)
.catch(prefetchError => next(prefetchError));
}
这是我的旧服务器文件:
match({ history, routes, location: req.url }, (error, redirectLocation, renderProps) => {
if (error) {
throw error;
} else if (redirectLocation) {
res.redirect(302, redirectLocation.pathname + redirectLocation.search);
} else if (renderProps) {
// Render react components once store is initializaed and return HTTP response
const render = () => {
const body = renderToString(
<MuiThemeProvider muiTheme={muiTheme}>
<Provider store={store}>
<IntlProvider>
<RouterContext {...renderProps} />
</IntlProvider>
</Provider>
</MuiThemeProvider>,
);
res.setHeader('content-language', locale);
res.render('index', {
head,
newrelicScript,
body,
locale,
state: stringState,
...
});
};
// Fetch components data and render HTML (no matter the fetch results
// we need to render something)
Promise.all(
prefetchData(renderProps, store),
)
.then(render)
.catch(prefetchError => next(prefetchError));
} else {
logger.warning('Ops !!! We should never arrive here :(');
next();
}
});
并预取数据:
export const prefetchData = (renderProps, store) => {
const { components, params } = renderProps;
const { dispatch } = store;
const state = store.getState();
const extractWrappedComponent = (component) => {
if (component.WrappedComponent) {
return extractWrappedComponent(component.WrappedComponent);
}
return component;
};
return components
.filter(component => component !== undefined)
// Get component, be aware if they are wrapped by redux connect or intl.
.map(component => extractWrappedComponent(component))
// Get the fetchData method of each component
.map(component => component.fetchData)
.filter(fetchData => fetchData !== undefined)
// Invoke each fetchData to initialize the redux state.
.map(fetchData => fetchData(state, dispatch, params));
};
在研究之后,我找到了适合我的解决方案。这是助手(之前是 prefetchData)。 matchPath
来自 react-router-dom
.
export const getRouteData = (routesArray, url, store) => {
const { dispatch } = store;
const state = store.getState();
const extractWrappedComponent = (component) => {
if (component.WrappedComponent) {
return extractWrappedComponent(component.WrappedComponent);
}
return component;
};
return routesArray.map((route) => {
const component = route.component;
const extractedComponent = extractWrappedComponent(component);
const match = matchPath(url, { path: route.path, exact: route.exact, strict: false });
if (match) {
if (extractedComponent.fetchData !== undefined) {
extractedComponent.fetchData(state, dispatch, match.params);
}
}
});
}
那我就叫它:
getRouteData(routes, req.url, store);
路线是 static routes:
export const routes = [
{
path: '/',
component: App
},
{
path: '/:lang/chat',
component: Chat
}
...
我的服务器文件现在没有匹配项。相反:
if (context.url) {
res.redirect(302, context.url);
} else {
const render = () => {
const body = renderApp(req, muiTheme, store, history, context);
res.render('index', {
head,
body,
locale,
...
});
};
Promise.all(
getRouteData(routes, req.url, store),
)
.then(render)
.catch(prefetchError => next(prefetchError));
RenderApp 文件(客户端必须与 StaticRouter
相同):
const renderApp = (req, muiTheme, store, history, context) => {
const app = (
<MuiThemeProvider muiTheme={muiTheme}>
<Provider store={store}>
<IntlProvider>
<StaticRouter
location={req.url}
context={context}
>
{ appRoutes(history) }
</StaticRouter>
</IntlProvider>
</Provider>
</MuiThemeProvider>
);
return renderToString(app);
};
appRoutes 文件:
export default function appRoutes(history) {
return (
<ConnectedRouter history={history}>
<div>
{routes.map((route, key) => (
<Route key={key} {...route} />
))}
</div>
</ConnectedRouter>
);
}