@before.js/server
Version:
Enables data fetching with any React SSR app that uses React Router 5
101 lines (94 loc) • 2.72 kB
JavaScript
// @flow strict
import type { AsyncFixMeComponentType, Context, Route } from 'fetchInitialPropsFromRoutes';
import { matchPath, type Match } from 'react-router-dom';
import { has } from 'ramda';
import { findRouteByPathname, getQueryString, isNotNil } from './utils';
/**
* Check if the `getInitialProps` property exist in given object.
* @func
* @param {object} value to check if property exist
* @returns {boolean}
*/
const hasGetInitialProps = has('getInitialProps');
/**
* Check if the `load` property exist in given object.
* @func
* @param {object} value to check if property exist
* @returns {boolean}
*/
const hasLoad = has('load');
const defaultContext = {
location: {
hash: '',
search: '',
pathname: ''
},
req: {
query: {},
originalUrl: '',
url: '',
path: ''
}
};
/**
* Retrieve the initial props from given component.
* @param {React$PureComponent} component to fetch the initial props
* @param {Match|Route} match react-router match or route object
* @param {object} context
* @returns {Promise<object|null>}
*/
export const getInitialPropsFromComponent = async (
component: AsyncFixMeComponentType,
match: ?Match | ?Route,
context: Context = defaultContext
) => {
if (match && hasGetInitialProps(component)) {
const location = isNotNil(context.location) ? context.location : { search: '' };
const req = isNotNil(context.req) ? context.req : { query: {} };
try {
if (hasLoad(component)) {
await component.load();
}
return await component.getInitialProps({
// Note: context contains the old `match` too, so it's important to overwrite that with the new `match`
...context,
query: getQueryString(location, req),
match
});
} catch (error) {
console.error('There was an error while trying to retrieve the initial props', error);
throw error;
}
}
return null;
};
/**
* Iterates over all given routes to find the route that a user is trying to render and
* will try to fetch the intial props from it.
* @param {array} routes an array of react-router routes
* @param {string} pathname request pathname
* @param {obect} context
* @returns {object} {route, data}
*/
export async function fetchInitialPropsFromRoute(
routes: Array<Route>,
pathname: string = '',
context?: Context
) {
const route = findRouteByPathname(pathname)(routes);
if (route) {
const match = matchPath(pathname, route);
const { component } = route;
return {
route: {
...match,
...route
},
data: await getInitialPropsFromComponent(component, match, context)
};
}
return {
route: {},
data: {}
};
}