Skip to content

Commit

Permalink
feature: [9] loading 파일에 대한 처리 추가
Browse files Browse the repository at this point in the history
feature: [9] loading 파일에 대한 처리 추가
  • Loading branch information
joseph0926 authored Oct 15, 2024
2 parents 6e68eac + 85b834c commit 92c61c6
Show file tree
Hide file tree
Showing 10 changed files with 139 additions and 126 deletions.
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,14 +152,31 @@ export default function HomeError() {
console.log(error);

return (
<div style={styles.error}>
<div className={styles.error}>
<h1>Home Page Error</h1>
<p>test</p>
</div>
);
}
```

#### **7. Loading Support**

You can add a `loading.tsx` file inside the folder to handle **loading** to that path.<br/>
For more information, see [Suspense fallback in React](https://react.dev/reference/react/Suspense#suspense)

```tsx
// src/pages/loading.tsx
export default function HomeLoading() {
return (
<div className={styles.wrapper}>
<h1>Home Page Title</h1>
<div className={styles.spinner} />
</div>
);
}
```

---

### **📄 How to Contribute**
Expand Down
21 changes: 19 additions & 2 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ export default function DashboardLayout() {

---

#### **4. 에러 지원**
#### **6. 에러 지원**

폴더 내에 `error.tsx` 파일을 추가하여 해당 경로에 **에러**에 대한 처리를 수행할 수 있습니다.<br/>
자세한 내용은 [React Router의 errorElement](https://reactrouter.com/en/main/route/error-element)를 참고해주세요
Expand All @@ -152,14 +152,31 @@ export default function HomeError() {
console.log(error);

return (
<div style={styles.error}>
<div className={styles.error}>
<h1>Home Page Error</h1>
<p>test</p>
</div>
);
}
```

#### **7. 로딩 지원**

폴더 내에 `loading.tsx` 파일을 추가하여 해당 경로에 **로딩**에 대한 처리를 수행할 수 있습니다.<br/>
자세한 내용은 [React의 Suspense fallback](https://react.dev/reference/react/Suspense#suspense)을 참고해주세요

```tsx
// src/pages/loading.tsx
export default function HomeLoading() {
return (
<div className={styles.wrapper}>
<h1>Home Page Title</h1>
<div className={styles.spinner} />
</div>
);
}
```

### **📄 기여 방법**

이 프로젝트에 기여하고 싶으시다면, 다음 절차를 따라주세요:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-router-file-routing",
"version": "0.1.3",
"version": "0.1.4",
"description": "A library to support folder-based routing of next.js to react-router-dom",
"main": "dist/index.js",
"files": [
Expand Down
4 changes: 3 additions & 1 deletion src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ export type RouteNode = {
pageElement?: React.LazyExoticComponent<any>;
/** error component */
errorElement?: React.LazyExoticComponent<any>;
/** loading component */
loadingElement?: React.LazyExoticComponent<any>;
/** children 라우트 */
children?: Record<string, RouteNode>;
};

export type NodeData = {
type: 'page' | 'layout' | 'error';
type: 'page' | 'layout' | 'error' | 'loading';
element: React.LazyExoticComponent<any>;
};
7 changes: 7 additions & 0 deletions src/utils/add-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ export function addToRouteTree(
tree.errorElement = nodeData.element;
return;
}
/** 루트 로딩이면 트리의 최상위에 loadingElement를 설정 */
if (isRootLayout && nodeData.type === 'loading') {
tree.loadingElement = nodeData.element;
return;
}

for (let index = 0; index < segments.length; index++) {
let segment = segments[index];
Expand Down Expand Up @@ -76,6 +81,8 @@ export function addToRouteTree(
current.layoutElement = nodeData.element;
} else if (nodeData.type === 'error') {
current.errorElement = nodeData.element;
} else if (nodeData.type === 'loading') {
current.loadingElement = nodeData.element;
}
}
}
Expand Down
112 changes: 16 additions & 96 deletions src/utils/build-tree.ts
Original file line number Diff line number Diff line change
@@ -1,62 +1,39 @@
// src/utils/build-tree.ts
import { RouteObject } from 'react-router-dom';
import { RouteNode } from '../types';
import { createRouteObject } from './create-route-object';
import { wrapWithSuspense } from './suspens-wrapper';
import React from 'react';

/**
* 트리구조를 라우터 형태로 변경하는 함수
* @param node
* @param parentPath
* @returns
* 트리 구조를 route object로 변경하는 함수
* @param node - RouteNode
* @param parentPath - 상위 path
* @returns RouteObject[]
*/
export function buildRoutesFromTree(
node: RouteNode,
parentPath: string = '',
): RouteObject[] {
/** 루트 노드이고 레이아웃 요소가 있는 경우 */
/** 최상위 루트에 layout이 존재하는 경우 */
if (parentPath === '' && node.layoutElement) {
const rootRoute: RouteObject = {
path: '/',
element: React.createElement(
React.Suspense,
{ fallback: null },
React.createElement(node.layoutElement),
),
errorElement: node.errorElement
? React.createElement(
React.Suspense,
{ fallback: null },
React.createElement(node.errorElement),
)
: undefined,

children: [],
};
const rootRoute: RouteObject = createRouteObject(node, '/');

const childRoutes: RouteObject[] = [];

/** 루트 노드에 페이지 요소가 있는 경우 인덱스 라우트로 추가 */
if (node.pageElement) {
const pageElement = React.createElement(
React.Suspense,
{ fallback: null },
React.createElement(node.pageElement),
);
const loadingComponent = node.loadingElement
? React.createElement(node.loadingElement)
: undefined;

if (node.pageElement) {
childRoutes.push({
index: true,
element: pageElement,
element: wrapWithSuspense(node.pageElement, loadingComponent),
errorElement: node.errorElement
? React.createElement(
React.Suspense,
{ fallback: null },
React.createElement(node.errorElement),
)
? wrapWithSuspense(node.errorElement)
: undefined,
});
}

/** 자식 노드를 재귀적으로 처리 */
if (node.children) {
for (const childSegment in node.children) {
const childNode = node.children[childSegment];
Expand All @@ -68,65 +45,13 @@ export function buildRoutesFromTree(
}
}

/** 자식 라우트를 루트 라우트의 children에 추가 */
rootRoute.children = childRoutes;

/** 루트 라우트만 반환하여 모든 경로에 루트 레이아웃이 적용되도록 함 */
return [rootRoute];
}

const routes: RouteObject[] = [];
const route = createRouteObject(node, parentPath || undefined);

const route: RouteObject = {};

/** 현재 노드의 경로 설정 */
if (parentPath !== '') {
route.path = parentPath;
}

/** 레이아웃 요소 설정 */
if (node.layoutElement) {
route.element = React.createElement(
React.Suspense,
{ fallback: null },
React.createElement(node.layoutElement),
);
}

/** 페이지 요소 설정 (레이아웃과 함께 있을 때는 인덱스 라우트로 추가) */
if (node.pageElement) {
const pageElement = React.createElement(
React.Suspense,
{ fallback: null },
React.createElement(node.pageElement),
);

const errorElement = node.errorElement
? React.createElement(
React.Suspense,
{ fallback: null },
React.createElement(node.errorElement),
)
: undefined;

if (node.layoutElement) {
/** 레이아웃이 있는 경우, 자식으로 인덱스 라우트 추가 */
if (!route.children) {
route.children = [];
}
route.children.push({
index: true,
element: pageElement,
errorElement,
});
} else {
/** 레이아웃이 없는 경우, 현재 라우트에 페이지 요소 설정 */
route.element = pageElement;
route.errorElement = errorElement;
}
}

/** 자식 노드 처리 */
if (node.children) {
const childRoutes: RouteObject[] = [];

Expand All @@ -137,18 +62,13 @@ export function buildRoutesFromTree(
}

if (node.layoutElement) {
/** 레이아웃이 있는 경우, 자식 라우트를 route.children에 추가 */
if (!route.children) {
route.children = [];
}
route.children = route.children || [];
route.children.push(...childRoutes);
} else {
/** 레이아웃이 없는 경우, 현재 라우트의 자식으로 추가 */
routes.push(...childRoutes);
}
}

routes.push(route);

return routes;
}
51 changes: 51 additions & 0 deletions src/utils/create-route-object.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { RouteObject } from 'react-router-dom';
import { RouteNode } from '../types';
import { wrapWithSuspense } from './suspens-wrapper';
import React from 'react';

/**
* Creates a route object 함수
* @param node - RouteNode
* @param path - route의 path
* @returns RouteObject
*/
export function createRouteObject(node: RouteNode, path?: string): RouteObject {
const route: RouteObject = {};

const loadingComponent = node.loadingElement
? React.createElement(node.loadingElement)
: undefined;

if (path) {
route.path = path;
}

/** layout 처리 */
if (node.layoutElement) {
route.element = wrapWithSuspense(node.layoutElement, loadingComponent);
}

/** error 처리 */
if (node.errorElement) {
route.errorElement = wrapWithSuspense(node.errorElement, loadingComponent);
}

/** page 처리 */
if (node.pageElement) {
const pageElement = wrapWithSuspense(node.pageElement, loadingComponent);

if (node.layoutElement) {
route.children = [
{
index: true,
element: pageElement,
errorElement: route.errorElement,
},
];
} else {
route.element = pageElement;
}
}

return route;
}
6 changes: 4 additions & 2 deletions src/utils/get-path-segments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ export function getPathSegments(filePath: string): string[] {
if (
segments[0].startsWith('page.') ||
segments[0].startsWith('layout.') ||
segments[0].startsWith('error.')
segments[0].startsWith('error.') ||
segments[0].startsWith('loading.')
) {
segments[0] = '/';
}
Expand All @@ -33,7 +34,8 @@ export function getPathSegments(filePath: string): string[] {
if (
segments[segments.length - 1].startsWith('page.') ||
segments[segments.length - 1].startsWith('layout.') ||
segments[segments.length - 1].startsWith('error.')
segments[segments.length - 1].startsWith('error.') ||
segments[segments.length - 1].startsWith('loading.')
) {
segments.pop();
}
Expand Down
Loading

0 comments on commit 92c61c6

Please sign in to comment.