我如何使用getInitialProps使用React Router和React Loadable使用Next.js进行数据提取(Razzle)。我有data fetching without react-loadable,但没有进行代码拆分的应用程序捆绑包太大,为客户端加载页面花费的时间太长。
此代码有效,但是我只是不了解一年前所做的事情(与前面的示例有些不同)
const promises = routes
.map((route) => {
const match = matchPath(req.url, route)
if (match && route.component) {
let promise
if (typeof route.component.preload === "function") {
promise = route.component.preload()
} else {
promise = Promise.resolve(route.component)
}
return promise
.then((res) => {
return res && res.__esModule ? res.default : res
})
.then((component) => {
let promises = []
// STATIC INTI ACTION
promises.push(store.dispatch(getToken()))
if (component.initialAction) {
let results = component.initialAction({ match })
results = Array.isArray(results)
? results
: [results]
results.forEach((result) => {
promises.push(store.dispatch(result))
})
}
return promises
})
}
return null
})
.filter((el) => el !== null)
// page not found
if (promises.length === 0) {
renderTree(req, res, store)
} else {
Promise.all(promises.map((data) => data.then((moreData) => moreData)))
.then((data) => {
Promise.all(data[0]).then(() => renderTree(req, res, store))
})
Server.js
const promises = []
routes.some((route) => {
const match = matchPath(req.url, route);
if (match) {
// route.component is React Loadable Component so getInitialData is undefined
if (route.component.getInitialData) {
promises.push(route.component.getInitialData({ match, req, res }))
}
return true;
}
return false;
});
Promise.all(promises)
.then(renderReact)
.catch(handleError)
// and at the end i will call
Loadable.preloadAll()
.then(sendResponseToUser)
路线:
[
{
path: "/",
exact: true,
component: Loadable({
loader: () => import("@views/Home"),
loading: Loading,
}),
},
{
path: "/shop",
exact: true,
component: Loadable({
loader: () => import("@views/Shop"),
loading: Loading,
}),
},
]
我的组件是这样的:
class Home extends React.Component {
// This works similarly to Next.js's `getInitialProps`
static getInitialData({ match, req, res }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
text: `
This text is server rendered if and only if it's the initial render.
Go to another route.
`,
currentRoute: match.pathname,
});
}, 500);
});
}
render() {
return <p>this is just a test</p>
}
}
React Loadable Component具有可以加载Component的preload()方法,因此我尝试了:
route.component.preload()
,但不起作用。
我尝试了可加载组件,它也有同样的问题,但是我可以用可加载组件替换react-loadable(我的首选库是可加载组件,因为使用StrictMode可以。)
实际上,如果我可以提取代码拆分逻辑并在我的应用中使用它,或者将after.js和data-fetching一起使用,那么react-loadable就解决了这个问题(它使用Razzle)会很棒。
答案 0 :(得分:0)
这就是我这样做的方式。
可同时使用。可加载的组件和普通组件。
内部matchRoutes()。检查component.preload。
如果为true,则允许它加载component.preload()。
这将返回一个Promise,并在Promise解决后,我们将获得组件。 然后检查loadData / getInitialProps / fetchData,无论我们使用什么静态方法名称。
return res.default.loadData ? res.default.loadData(store) : null;
您可以调用服务器端提取请求。我将其称为loadData()
永远记住要返回承诺,因为它是一连串的承诺。
在redux动作中的Aslo,请务必记住要返回dispatch()。
app.get('*', (req, res) => {
// store instance for every incoming request
const store = createStore(req);
// loadData from api... before rendering
const promises = matchRoutes(Routes, req.path)
.map((comp) => {
console.log(comp, comp.route.component.loadData)
if (comp.route.component.preload){
console.log("LoadableComponent")
comp.route.component.preload().then(res => {
return res.default.loadData ? res.default.loadData(store) : null;
})
}
return comp.route.component.loadData ? comp.route.component.loadData(store) : null;
})
.map(promise => {
if (promise) {
return new Promise((resolve, reject) => {
promise.then(resolve).catch(resolve);
});
}
});
Promise.all(promises).then(() => {
console.log(store.getState())
const context = {};
const content = renderer(req, store, context);
if (context.url) {
return res.redirect(301, context.url);
}
if (context.notFound) {
res.status(404);
}
Loadable.preloadAll().then(() => {
res.send(content);
});
});
});