Skip to content

Commit

Permalink
#bugFix for nested routes. Seo and match correction throughouth the app
Browse files Browse the repository at this point in the history
  • Loading branch information
tirthbodawala committed Nov 23, 2021
1 parent 5ad02c6 commit 3a35f14
Show file tree
Hide file tree
Showing 16 changed files with 204 additions and 84 deletions.
1 change: 0 additions & 1 deletion demo/src/components/contribute.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import GuestLayout from './guest-layout';

export default () => (
Expand Down
4 changes: 2 additions & 2 deletions demo/src/components/footer.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';

export default () => (
export default React.memo(() => (
<footer className="footer">
<div className="content has-text-centered">
<p>
Expand All @@ -26,4 +26,4 @@ export default () => (
</div>
</div>
</footer>
);
));
7 changes: 5 additions & 2 deletions demo/src/components/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,11 @@ export default class Header extends PureComponent<any, any> {
<Link className="navbar-item" to="/skeleton-loading" onClick={this.closeMenuBar}>
Skeleton Loading
</Link>
<Link className="navbar-item" to="/image-optimization" onClick={this.closeMenuBar}>
Image Optimization
<Link className="navbar-item" to="/nested" onClick={this.closeMenuBar}>
Nested
</Link>
<Link className="navbar-item" to="/invalid-path" onClick={this.closeMenuBar}>
Invalid Path
</Link>
<Link className="navbar-item" to="/login" onClick={this.closeMenuBar}>
Auth
Expand Down
13 changes: 13 additions & 0 deletions demo/src/components/nested/dynamic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useParams } from 'react-router';
import { toName } from '../../utils/text';

const NestedRoute2 = () => {
const { name } = useParams();
return (
<h2>
Hi, <span className="is-capitalized">{toName(name || '')}!</span>
</h2>
);
};

export default NestedRoute2;
30 changes: 30 additions & 0 deletions demo/src/components/nested/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Outlet } from 'react-router';
import GuestLayout from '../guest-layout';
import { Link } from 'react-router-dom';

const NestedRoute = () => {
return (
<GuestLayout>
<div className="container p-2" style={{ minHeight: '20rem' }}>
<h1 className="title mt-5">I am a nested Route</h1>
<hr />
<ul>
<li>
<Link to="/nested/simple">A simple route</Link>
</li>
<li>
<Link to="/nested/john-doe">John Doe as param</Link>
</li>
<li>
<Link to="/nested/cathrine">Cathrine as param</Link>
</li>
</ul>
<hr />
<Outlet />
</div>

</GuestLayout>
);
};

export default NestedRoute;
7 changes: 7 additions & 0 deletions demo/src/components/nested/simple.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const NestedRoute1 = () => {
return (
<h2>I am a sub nested route 1</h2>
);
};

export default NestedRoute1;
36 changes: 36 additions & 0 deletions demo/src/pages/guest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { IRoute } from '@pawjs/pawjs';
import FeaturesImage from '../resources/images/seo/features.png';
import CSSGlobalLocalImage from '../resources/images/seo/css-global-local.png';
import SkeletonImage from '../resources/images/seo/skeleton-loading.png';
import { toName } from '../utils/text';

const routes: IRoute[] = [
{
Expand Down Expand Up @@ -59,6 +60,41 @@ const routes: IRoute[] = [
description: 'Be a part of larger family. Get involved with us and support our project ReactPWA',
},
},
{
path: '/nested',
component: () => import('../components/nested/index'),
seo: {
title: 'Nested routes',
description: 'Create nested routes. Very handy when managing large applications.',
},
cache: false,
routes: [
{
path: 'simple',
component: () => import('../components/nested/simple'),
cache: {
// 1 year
maxAge: 31536000000,
},
seo: {
title: 'Simple | Nested routes',
description: 'Create nested routes. Very handy when managing large applications.',
},
},
{
path: ':name',
component: () => import('../components/nested/dynamic'),
loadData: ({ updateSeo, match }) => {
const { params: { name } } = match;
updateSeo({
title: `${toName(name || '')} | Nested routes`,
description: `${toName(name || '')} in nested routes. You can add your own dynamic name in the url!`,
});
return {};
},
},
],
},
];

export default routes;
6 changes: 6 additions & 0 deletions demo/src/utils/text.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

export const toName = (str: string) => {
return str.replace(/-/g, ' ').split(' ').map((s) => {
return s.charAt(0).toUpperCase() + s.slice(1);
}).join(' ');
};
3 changes: 3 additions & 0 deletions main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,7 @@ export interface IRoute {
};
routes?: IRoute[];
index?: boolean;
cache?: false | {
maxAge: number,
},
}
6 changes: 6 additions & 0 deletions src/@types/route.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ export interface IRoute extends RouteProps {
timeout?: number;
webpack?: string [];
selfManageNewProps?: boolean;
cache?: false | {
maxAge: number,
},
}

export interface ICompiledRoute {
Expand All @@ -47,6 +50,9 @@ export interface ICompiledRoute {
routes?: CompiledRoute[];
modules?: string[];
webpack?: string[];
cache?: false | {
maxAge: number,
};
}

export type Route = IRoute ;
Expand Down
53 changes: 28 additions & 25 deletions src/client/handler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ import ErrorBoundary from '../components/ErrorBoundary';
import { generateMeta } from '../utils/seo';
import possibleStandardNames from '../utils/reactPossibleStandardNames';
import AbstractPlugin from '../abstract-plugin';
import { ICompiledRoute } from '../@types/route';
import { useLayoutEffect } from 'react';
import { useLayoutEffect, useRef } from 'react';
import { useLocation } from 'react-router';
import { PawProvider } from '../components/Paw';

Expand Down Expand Up @@ -130,19 +129,20 @@ export default class ClientHandler extends AbstractPlugin {
let seoData = {};
const pwaSchema = this.routeHandler.getPwaSchema();
const seoSchema = this.routeHandler.getDefaultSeoSchema();
currentRoutes.forEach((r: { route: ICompiledRoute, match: any }) => {
if (r?.route?.element?.preload) {
promises.push(r.route.element.preload(undefined, {
route: r.route,
match: r.match,
currentRoutes.forEach((match) => {
const { route, params } = match as any;
if (route?.element?.preload) {
promises.push(route.element.preload(undefined, {
match: { params },
})?.promise);
}
});
await Promise.all(promises);
currentRoutes.forEach((r: { route: ICompiledRoute, match: any }) => {
currentRoutes.forEach((match) => {
const { route } = match as any;
let routeSeo = {};
if (r.route.getRouteSeo) {
routeSeo = r.route.getRouteSeo();
if (route.getRouteSeo) {
routeSeo = route.getRouteSeo();
}
seoData = { ...seoData, ...routeSeo };
});
Expand Down Expand Up @@ -288,30 +288,29 @@ export default class ClientHandler extends AbstractPlugin {
const promises: Promise<any> [] = [];
if (window.PAW_PRELOADED_DATA) {
const preloadedData = window.PAW_PRELOADED_DATA;
currentPageRoutes.forEach((r: { route: ICompiledRoute, match: any }, i: number) => {
currentPageRoutes.forEach((match, i: number) => {
const { route, params } = match as any;
if (
(typeof preloadedData[i] !== 'undefined')
&& r.route && r.route.element && r.route.element.preload
&& route && route.element && route.element.preload
) {
const preloadInit = r.route.element.preload(preloadedData[i], {
route: r.route,
match: r.match,
const preloadInit = route.element.preload(preloadedData[i], {
match: { params },
});
promises.push(preloadInit.promise);
} else if (r.route && r.route.element && r.route.element.preload) {
const preloadInit = r.route.element.preload(undefined, {
route: r.route,
match: r.match,
} else if (route && route.element && route.element.preload) {
const preloadInit = route.element.preload(undefined, {
match: { params },
});
promises.push(preloadInit.promise);
}
});
} else {
currentPageRoutes.forEach((r: { route: ICompiledRoute, match: any }) => {
if (r.route && r.route.element && r.route.element.preload) {
const preloadInit = r.route.element.preload(undefined, {
route: r.route,
match: r.match,
currentPageRoutes.forEach((match) => {
const { route, params } = match as any;
if (route && route.element && route.element.preload) {
const preloadInit = route.element.preload(undefined, {
match: { params },
});
promises.push(preloadInit.promise);
}
Expand Down Expand Up @@ -348,9 +347,13 @@ export default class ClientHandler extends AbstractPlugin {
const NavigationListner: React.FC = ({ children }) => {
const navigationType = useNavigationType();
const location = useLocation();
const isFirstLoad = useRef(true);
useLayoutEffect(
() => {
this.manageHistoryChange(location, navigationType);
if (!isFirstLoad.current) {
this.manageHistoryChange(location, navigationType);
}
isFirstLoad.current = false;
},
[navigationType, location],
);
Expand Down
24 changes: 12 additions & 12 deletions src/components/Loadable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React, {
useRef,
} from 'react';
import { useLocation } from 'react-router';
import { useMatch } from 'react-router-dom';
import { useParams } from 'react-router-dom';
import loadMap, { load, LoadableState } from '../utils/loadable';

// eslint-disable-next-line no-underscore-dangle
Expand Down Expand Up @@ -77,13 +77,14 @@ const createLoadableComponent = (
}

const loadableComponent = () => {
const propsLocation = useLocation();
const propsMatch = useMatch(opts.path || propsLocation.pathname);
const location = useLocation();
const params = useParams();
const props = { match: { params } };
const previousRouterProps = useRef({
propsLocation,
propsMatch,
location,
params,
});
const props = { match: propsMatch };
// console.log(opts.path, props);
// Initialisation
// Set res reference
const resReference = useRef(init(undefined, props));
Expand Down Expand Up @@ -225,21 +226,20 @@ const createLoadableComponent = (

if (
!opts.selfManageNewProps
&& previousRouterProps.current.propsMatch?.pathname === propsMatch?.pathname
&& previousRouterProps.current.propsLocation?.key !== propsLocation.key
&& previousRouterProps.current.location?.key !== location.key
) {
// Component will receive props
// The route has been changed, and the component remains the same
// console.log('Props changed, route changed');
retry();
previousRouterProps.current = {
propsLocation,
propsMatch,
location,
params,
};
} else if (!previousRouterProps.current) {
previousRouterProps.current = {
propsLocation,
propsMatch,
location,
params,
};
}

Expand Down
4 changes: 3 additions & 1 deletion src/router/compiler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export default class RouteCompiler {
modules,
routes,
selfManageNewProps,
cache,
} = route;

// If we have skeleton for route then default delay is 0
Expand Down Expand Up @@ -174,9 +175,10 @@ export default class RouteCompiler {
path,
webpack,
modules,
cache,
getRouteSeo: () => ({ ...routeSeo }),
element: loadableComponent,
...(routes ? { routes: this.compileRoutes(routes, routerService) } : {}),
...(routes ? { children: this.compileRoutes(routes, routerService) } : {}),
};
}
}
27 changes: 3 additions & 24 deletions src/router/handler.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AsyncSeriesHook } from 'tapable';
import _uniq from 'lodash/uniq';
import _cloneDeep from 'lodash/cloneDeep';
import { matchPath } from 'react-router';
import { matchRoutes } from 'react-router';
// @ts-ignore
// eslint-disable-next-line
import pawSeoSchema from 'pawSeoConfig';
Expand Down Expand Up @@ -83,30 +83,9 @@ export default class RouteHandler extends AbstractPlugin implements IRouteHandle

static matchRoutes = (...args: any[] | [any, any]) => {
const [routes, pathname] = args;
const branch = args.length > 2 && args[2] !== undefined ? args[2] : [];
const matches = matchRoutes(routes, pathname);

routes.some((route: Route) => {
let match;

if (route.path) {
match = matchPath(route.path, pathname);
} else {
match = branch.length ? branch[branch.length - 1].match // use parent match
: RouteHandler.computeRootMatch(pathname);
}

if (match) {
branch.push({ route, match });

if (route.routes) {
RouteHandler.matchRoutes(route.routes, pathname, branch);
}
}

return match;
});

return branch;
return matches || [];
};

routes: CompiledRoute [] = [];
Expand Down
Loading

0 comments on commit 3a35f14

Please sign in to comment.