diff --git a/package.json b/package.json index e48f4cac5..ce0abd7e9 100644 --- a/package.json +++ b/package.json @@ -15,14 +15,18 @@ "prettier:diff": "yarn nit:source", "lint-heading-ids": "node scripts/headingIdLinter.js", "fix-headings": "node scripts/headingIdLinter.js --fix", - "ci-check": "npm-run-all prettier:diff --parallel lint tsc lint-heading-ids rss", + "ci-check": "npm-run-all prettier:diff --parallel lint tsc lint-heading-ids rss deadlinks", "tsc": "tsc --noEmit", "start": "next start", "postinstall": "is-ci || husky install .husky", "check-all": "npm-run-all prettier lint:fix tsc rss", "rss": "node scripts/generateRss.js", +<<<<<<< HEAD "textlint": "cd textlint && yarn --frozen-lockfile && yarn textlint", "textlint-staged": "cd textlint && yarn --frozen-lockfile && yarn textlint-staged --" +======= + "deadlinks": "node scripts/deadLinkChecker.js" +>>>>>>> d52b3ec734077fd56f012fc2b30a67928d14cc73 }, "dependencies": { "@codesandbox/sandpack-react": "2.13.5", @@ -32,7 +36,6 @@ "@radix-ui/react-context-menu": "^2.1.5", "body-scroll-lock": "^3.1.3", "classnames": "^2.2.6", - "date-fns": "^2.16.1", "debounce": "^1.2.1", "github-slugger": "^1.3.0", "next": "15.1.0", @@ -64,6 +67,7 @@ "autoprefixer": "^10.4.2", "babel-eslint": "10.x", "babel-plugin-react-compiler": "19.0.0-beta-e552027-20250112", + "chalk": "4.1.2", "eslint": "7.x", "eslint-config-next": "12.0.3", "eslint-config-react-app": "^5.2.1", diff --git a/scripts/deadLinkChecker.js b/scripts/deadLinkChecker.js new file mode 100644 index 000000000..ab8761e26 --- /dev/null +++ b/scripts/deadLinkChecker.js @@ -0,0 +1,342 @@ +#!/usr/bin/env node + +const fs = require('fs'); +const path = require('path'); +const globby = require('globby'); +const chalk = require('chalk'); + +const CONTENT_DIR = path.join(__dirname, '../src/content'); +const PUBLIC_DIR = path.join(__dirname, '../public'); +const fileCache = new Map(); +const anchorMap = new Map(); // Map> +const contributorMap = new Map(); // Map +let errorCodes = new Set(); + +async function readFileWithCache(filePath) { + if (!fileCache.has(filePath)) { + try { + const content = await fs.promises.readFile(filePath, 'utf8'); + fileCache.set(filePath, content); + } catch (error) { + throw new Error(`Failed to read file ${filePath}: ${error.message}`); + } + } + return fileCache.get(filePath); +} + +async function fileExists(filePath) { + try { + await fs.promises.access(filePath, fs.constants.R_OK); + return true; + } catch { + return false; + } +} + +function getMarkdownFiles() { + // Convert Windows paths to POSIX for globby compatibility + const baseDir = CONTENT_DIR.replace(/\\/g, '/'); + const patterns = [ + path.posix.join(baseDir, '**/*.md'), + path.posix.join(baseDir, '**/*.mdx'), + ]; + return globby.sync(patterns); +} + +function extractAnchorsFromContent(content) { + const anchors = new Set(); + + // MDX-style heading IDs: {/*anchor-id*/} + const mdxPattern = /\{\/\*([a-zA-Z0-9-_]+)\*\/\}/g; + let match; + while ((match = mdxPattern.exec(content)) !== null) { + anchors.add(match[1].toLowerCase()); + } + + // HTML id attributes + const htmlIdPattern = /\sid=["']([a-zA-Z0-9-_]+)["']/g; + while ((match = htmlIdPattern.exec(content)) !== null) { + anchors.add(match[1].toLowerCase()); + } + + // Markdown heading with explicit ID: ## Heading {#anchor-id} + const markdownHeadingPattern = /^#+\s+.*\{#([a-zA-Z0-9-_]+)\}/gm; + while ((match = markdownHeadingPattern.exec(content)) !== null) { + anchors.add(match[1].toLowerCase()); + } + + return anchors; +} + +async function buildAnchorMap(files) { + for (const filePath of files) { + const content = await readFileWithCache(filePath); + const anchors = extractAnchorsFromContent(content); + if (anchors.size > 0) { + anchorMap.set(filePath, anchors); + } + } +} + +function extractLinksFromContent(content) { + const linkPattern = /\[([^\]]*)\]\(([^)]+)\)/g; + const links = []; + let match; + + while ((match = linkPattern.exec(content)) !== null) { + const [, linkText, linkUrl] = match; + if (linkUrl.startsWith('/') && !linkUrl.startsWith('//')) { + const lines = content.substring(0, match.index).split('\n'); + const line = lines.length; + const lastLineStart = + lines.length > 1 ? content.lastIndexOf('\n', match.index - 1) + 1 : 0; + const column = match.index - lastLineStart + 1; + + links.push({ + text: linkText, + url: linkUrl, + line, + column, + }); + } + } + + return links; +} + +async function findTargetFile(urlPath) { + // Check if it's an image or static asset that might be in the public directory + const imageExtensions = [ + '.png', + '.jpg', + '.jpeg', + '.gif', + '.svg', + '.ico', + '.webp', + ]; + const hasImageExtension = imageExtensions.some((ext) => + urlPath.toLowerCase().endsWith(ext) + ); + + if (hasImageExtension || urlPath.includes('.')) { + // Check in public directory (with and without leading slash) + const publicPaths = [ + path.join(PUBLIC_DIR, urlPath), + path.join(PUBLIC_DIR, urlPath.substring(1)), + ]; + + for (const p of publicPaths) { + if (await fileExists(p)) { + return p; + } + } + } + + const possiblePaths = [ + path.join(CONTENT_DIR, urlPath + '.md'), + path.join(CONTENT_DIR, urlPath + '.mdx'), + path.join(CONTENT_DIR, urlPath, 'index.md'), + path.join(CONTENT_DIR, urlPath, 'index.mdx'), + // Without leading slash + path.join(CONTENT_DIR, urlPath.substring(1) + '.md'), + path.join(CONTENT_DIR, urlPath.substring(1) + '.mdx'), + path.join(CONTENT_DIR, urlPath.substring(1), 'index.md'), + path.join(CONTENT_DIR, urlPath.substring(1), 'index.mdx'), + ]; + + for (const p of possiblePaths) { + if (await fileExists(p)) { + return p; + } + } + return null; +} + +async function validateLink(link) { + const urlAnchorPattern = /#([a-zA-Z0-9-_]+)$/; + const anchorMatch = link.url.match(urlAnchorPattern); + const urlWithoutAnchor = link.url.replace(urlAnchorPattern, ''); + + if (urlWithoutAnchor === '/') { + return {valid: true}; + } + + // Check if it's an error code link + const errorCodeMatch = urlWithoutAnchor.match(/^\/errors\/(\d+)$/); + if (errorCodeMatch) { + const code = errorCodeMatch[1]; + if (!errorCodes.has(code)) { + return { + valid: false, + reason: `Error code ${code} not found in React error codes`, + }; + } + return {valid: true}; + } + + // Check if it's a contributor link on the team or acknowledgements page + if ( + anchorMatch && + (urlWithoutAnchor === '/community/team' || + urlWithoutAnchor === '/community/acknowledgements') + ) { + const anchorId = anchorMatch[1].toLowerCase(); + if (contributorMap.has(anchorId)) { + const correctUrl = contributorMap.get(anchorId); + if (correctUrl !== link.url) { + return { + valid: false, + reason: `Contributor link should be updated to: ${correctUrl}`, + }; + } + return {valid: true}; + } else { + return { + valid: false, + reason: `Contributor link not found`, + }; + } + } + + const targetFile = await findTargetFile(urlWithoutAnchor); + + if (!targetFile) { + return { + valid: false, + reason: `Target file not found for: ${urlWithoutAnchor}`, + }; + } + + // Only check anchors for content files, not static assets + if (anchorMatch && targetFile.startsWith(CONTENT_DIR)) { + const anchorId = anchorMatch[1].toLowerCase(); + + // TODO handle more special cases. These are usually from custom MDX components that include + // a Heading from src/components/MDX/Heading.tsx which automatically injects an anchor tag. + switch (anchorId) { + case 'challenges': + case 'recap': { + return {valid: true}; + } + } + + const fileAnchors = anchorMap.get(targetFile); + + if (!fileAnchors || !fileAnchors.has(anchorId)) { + return { + valid: false, + reason: `Anchor #${anchorMatch[1]} not found in ${path.relative( + CONTENT_DIR, + targetFile + )}`, + }; + } + } + + return {valid: true}; +} + +async function processFile(filePath) { + const content = await readFileWithCache(filePath); + const links = extractLinksFromContent(content); + const deadLinks = []; + + for (const link of links) { + const result = await validateLink(link); + if (!result.valid) { + deadLinks.push({ + file: path.relative(process.cwd(), filePath), + line: link.line, + column: link.column, + text: link.text, + url: link.url, + reason: result.reason, + }); + } + } + + return {deadLinks, totalLinks: links.length}; +} + +async function buildContributorMap() { + const teamFile = path.join(CONTENT_DIR, 'community/team.md'); + const teamContent = await readFileWithCache(teamFile); + + const teamMemberPattern = /]*permalink=["']([^"']+)["']/g; + let match; + + while ((match = teamMemberPattern.exec(teamContent)) !== null) { + const permalink = match[1]; + contributorMap.set(permalink, `/community/team#${permalink}`); + } + + const ackFile = path.join(CONTENT_DIR, 'community/acknowledgements.md'); + const ackContent = await readFileWithCache(ackFile); + const contributorPattern = /\*\s*\[([^\]]+)\]\(([^)]+)\)/g; + + while ((match = contributorPattern.exec(ackContent)) !== null) { + const name = match[1]; + const url = match[2]; + const hyphenatedName = name.toLowerCase().replace(/\s+/g, '-'); + if (!contributorMap.has(hyphenatedName)) { + contributorMap.set(hyphenatedName, url); + } + } +} + +async function fetchErrorCodes() { + try { + const response = await fetch( + 'https://raw.githubusercontent.com/facebook/react/main/scripts/error-codes/codes.json' + ); + if (!response.ok) { + throw new Error(`Failed to fetch error codes: ${response.status}`); + } + const codes = await response.json(); + errorCodes = new Set(Object.keys(codes)); + console.log(chalk.gray(`Fetched ${errorCodes.size} React error codes\n`)); + } catch (error) { + throw new Error(`Failed to fetch error codes: ${error.message}`); + } +} + +async function main() { + const files = getMarkdownFiles(); + console.log(chalk.gray(`Checking ${files.length} markdown files...`)); + + await fetchErrorCodes(); + await buildContributorMap(); + await buildAnchorMap(files); + + const filePromises = files.map((filePath) => processFile(filePath)); + const results = await Promise.all(filePromises); + const deadLinks = results.flatMap((r) => r.deadLinks); + const totalLinks = results.reduce((sum, r) => sum + r.totalLinks, 0); + + if (deadLinks.length > 0) { + for (const link of deadLinks) { + console.log(chalk.yellow(`${link.file}:${link.line}:${link.column}`)); + console.log(chalk.reset(` Link text: ${link.text}`)); + console.log(chalk.reset(` URL: ${link.url}`)); + console.log(` ${chalk.red('✗')} ${chalk.red(link.reason)}\n`); + } + + console.log( + chalk.red( + `\nFound ${deadLinks.length} dead link${ + deadLinks.length > 1 ? 's' : '' + } out of ${totalLinks} total links\n` + ) + ); + process.exit(1); + } + + console.log(chalk.green(`\n✓ All ${totalLinks} links are valid!\n`)); + process.exit(0); +} + +main().catch((error) => { + console.log(chalk.red(`Error: ${error.message}`)); + process.exit(1); +}); diff --git a/src/content/blog/2023/03/16/introducing-react-dev.md b/src/content/blog/2023/03/16/introducing-react-dev.md index a7969a0ed..9b6bb6628 100644 --- a/src/content/blog/2023/03/16/introducing-react-dev.md +++ b/src/content/blog/2023/03/16/introducing-react-dev.md @@ -41,8 +41,13 @@ March 16, 2023 by [Dan Abramov](https://bsky.app/profile/danabra.mov) and [Rache **新しいドキュメントでは、最初からフックを用いて React を学びます**。ドキュメントは主に 2 つのセクションに分かれています。 +<<<<<<< HEAD * **[React を学ぶ](/learn)** は、ゼロから React を学ぶ自己学習型のコースです。 * **[API リファレンス](/reference)** では、すべての React API の詳細と使い方のサンプルが提供されています。 +======= +* **[Learn React](/learn)** is a self-paced course that teaches React from scratch. +* **[API Reference](/reference/react)** provides the details and usage examples for every React API. +>>>>>>> d52b3ec734077fd56f012fc2b30a67928d14cc73 それぞれのセクションで見ることができる内容を詳しく見ていきましょう。 @@ -607,7 +612,11 @@ button { display: block; margin-top: 10px; } +<<<<<<< HEAD 一部の API ページには、(よくある問題に対する)[トラブルシューティング](/reference/react/useEffect#troubleshooting)や、(非推奨の API に対する)[代替手段](/reference/react-dom/findDOMNode#alternatives)のセクションも含まれています。 +======= +Some API pages also include [Troubleshooting](/reference/react/useEffect#troubleshooting) (for common problems) and [Alternatives](https://18.react.dev/reference/react-dom/findDOMNode#alternatives) (for deprecated APIs). +>>>>>>> d52b3ec734077fd56f012fc2b30a67928d14cc73 このアプローチにより、API リファレンスが引数を調べる手段としてだけでなく、その API を使用してどれだけ色々なことができるのか、他の API とどのように繋がっているのかを示す手段として役立つことを願っています。 diff --git a/src/content/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023.md b/src/content/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023.md index 9e831a382..931820fb2 100644 --- a/src/content/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023.md +++ b/src/content/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023.md @@ -31,7 +31,11 @@ RSC は、サーバセントリックなマルチページアプリケーショ データフェッチがかなり整理されましたので、クライアントからサーバという逆方向へのデータ送信を行って、データベース更新やフォームの実装ができる方法についても検討しています。サーバ/クライアントの境界を越えて Server Action 関数を渡せるようにすることで、クライアントがそれを呼び出し、シームレスな RPC を実現できるようする、という方法を考えています。Server Action により、JavaScript が読み込まれる前に段階的に動作するようになるフォームを提供することも可能です。 +<<<<<<< HEAD React Server Components は [Next.js App Router](/learn/start-a-new-react-project#nextjs-app-router) でリリースされました。これは RSC をプリミティブとして完全採用し深く統合を行ったルータのデモとなっていますが、これが RSC 互換ルータやフレームワークを構築するための唯一の方法というわけではありません。RSC 仕様が提供するものと、その実装が提供するものとの間には明確な区切りがあります。React Server Components は、互換のある複数の React フレームワーク間で動作する、コンポーネント仕様として作られています。 +======= +React Server Components has shipped in [Next.js App Router](/learn/creating-a-react-app#nextjs-app-router). This showcases a deep integration of a router that really buys into RSC as a primitive, but it's not the only way to build a RSC-compatible router and framework. There's a clear separation for features provided by the RSC spec and implementation. React Server Components is meant as a spec for components that work across compatible React frameworks. +>>>>>>> d52b3ec734077fd56f012fc2b30a67928d14cc73 一般的には既存のフレームワークを使用することをお勧めしますが、自分自身のカスタムフレームワークの構築が必要な場合は、それも可能です。独自の RSC 互換フレームワークの構築は、主にバンドラとの深い統合が必要であるという理由により、我々が望むほど簡単なものにはなっていません。現行世代のバンドラはクライアントでの使用には適していますが、サーバとクライアントをまたいだ単一のモジュールグラフの分割を行うことを優先的なサポート項目として設計されてはいません。これが、現在 RSC のプリミティブを組み込んでもらうために、バンドラ開発者と直接協力している理由です。 @@ -92,7 +96,11 @@ React コンポーネント内のプレーンな JavaScript をリアクティ ## トランジショントレーシング {/*transition-tracing*/} +<<<<<<< HEAD Transition Tracing API により、[React のトランジション](/reference/react/useTransition)が遅くなったことを検出し、なぜ遅くなるのかを調査することができます。前回のお知らせ以降、API の初期設計を完了し、[RFC](https://github.com/reactjs/rfcs/pull/238) を公開しました。基本的な機能も実装されています。現在、プロジェクトは保留中です。RFC に対するフィードバックを歓迎します。React のパフォーマンス測定ツールをより良くするために開発を再開できるようになることを楽しみにしています。これは、[Next.js App Router](/learn/start-a-new-react-project#nextjs-app-router) のように React トランジション上に構築されたルータでは、特に役立ちます。 +======= +The Transition Tracing API lets you detect when [React Transitions](/reference/react/useTransition) become slower and investigate why they may be slow. Following our last update, we have completed the initial design of the API and published an [RFC](https://github.com/reactjs/rfcs/pull/238). The basic capabilities have also been implemented. The project is currently on hold. We welcome feedback on the RFC and look forward to resuming its development to provide a better performance measurement tool for React. This will be particularly useful with routers built on top of React Transitions, like the [Next.js App Router](/learn/creating-a-react-app#nextjs-app-router). +>>>>>>> d52b3ec734077fd56f012fc2b30a67928d14cc73 * * * このページでのアップデートに加えて、私たちのチームは最近、コミュニティのポッドキャストやライブストリームにゲスト出演し、取り組みについてより多くのことをお話しし、質問に答える機会がありました。 diff --git a/src/content/blog/2024/02/15/react-labs-what-we-have-been-working-on-february-2024.md b/src/content/blog/2024/02/15/react-labs-what-we-have-been-working-on-february-2024.md index b95c82e4d..773ad74b8 100644 --- a/src/content/blog/2024/02/15/react-labs-what-we-have-been-working-on-february-2024.md +++ b/src/content/blog/2024/02/15/react-labs-what-we-have-been-working-on-february-2024.md @@ -107,7 +107,11 @@ Activity はまだ研究中であり、残された作業は、ライブラリ このアップデートに加えて、私たちのチームはカンファレンスでの発表やポッドキャストへの出演を通じ、我々の作業についてお伝えし、質問にお答えしています。 +<<<<<<< HEAD - [Sathya Gunasekaran](/community/team#sathya-gunasekaran) は [React India](https://www.youtube.com/watch?v=kjOacmVsLSE) カンファレンスで React コンパイラについて話しました。 +======= +- [Sathya Gunasekaran](https://github.com/gsathya) spoke about the React Compiler at the [React India](https://www.youtube.com/watch?v=kjOacmVsLSE) conference +>>>>>>> d52b3ec734077fd56f012fc2b30a67928d14cc73 - [Dan Abramov](/community/team#dan-abramov) は [RemixConf](https://www.youtube.com/watch?v=zMf_xeGPn6s) で "React from Another Dimension" というタイトルの講演を行い、React Server Components やアクションの成立に際してありえたかもしれない別の歴史について紹介しました。 diff --git a/src/content/blog/2024/04/25/react-19-upgrade-guide.md b/src/content/blog/2024/04/25/react-19-upgrade-guide.md index bf68baeab..f2c4b1d3b 100644 --- a/src/content/blog/2024/04/25/react-19-upgrade-guide.md +++ b/src/content/blog/2024/04/25/react-19-upgrade-guide.md @@ -129,7 +129,11 @@ npx codemod@latest react/19/migration-recipe これまでのバージョンの React では、レンダー中にスローされたエラーはキャッチされた後に再スローされていました。開発環境では、`console.error` にもログを出力していたため、エラーログの重複が発生していました。 +<<<<<<< HEAD React 19 では、重複を減らすためにエラーの扱いを[改善し](/blog/2024/04/25/react-19#error-handling)、再スローは行わないようになりました。 +======= +In React 19, we've [improved how errors are handled](/blog/2024/12/05/react-19#error-handling) to reduce duplication by not re-throwing: +>>>>>>> d52b3ec734077fd56f012fc2b30a67928d14cc73 - **キャッチされないエラー**:エラーバウンダリによってキャッチされないエラーは `window.reportError` に報告されます。 - **キャッチされたエラー**:エラーバウンダリによってキャッチされたエラーは `console.error` に報告されます。 @@ -499,7 +503,11 @@ function AutoselectingInput() { ### 非推奨化:`element.ref` {/*deprecated-element-ref*/} +<<<<<<< HEAD React 19 では [props としての `ref`](/blog/2024/04/25/react-19#ref-as-a-prop) がサポートされるため、`element.ref` を非推奨化します。代わりに `element.props.ref` を使用します。 +======= +React 19 supports [`ref` as a prop](/blog/2024/12/05/react-19#ref-as-a-prop), so we're deprecating the `element.ref` in place of `element.props.ref`. +>>>>>>> d52b3ec734077fd56f012fc2b30a67928d14cc73 `element.ref` にアクセスすると、以下の警告が表示されます。 diff --git a/src/content/blog/2024/12/05/react-19.md b/src/content/blog/2024/12/05/react-19.md index f9f21c1e0..032d737ec 100644 --- a/src/content/blog/2024/12/05/react-19.md +++ b/src/content/blog/2024/12/05/react-19.md @@ -355,7 +355,11 @@ async function handler(request) { サーバコンポーネントは、クライアントアプリケーションや SSR サーバとは別の環境で、バンドル前にコンポーネントを事前レンダーするための新しいオプションです。React Server Components の "server" とはこの別の環境を指しています。サーバコンポーネントは、CI サーバでビルド時に一度だけ実行することも、ウェブサーバを使用してリクエストごとに実行することもできます。 +<<<<<<< HEAD React 19 には、Canary チャンネルにあったすべての React Server Components の機能が含まれています。これにより、サーバコンポーネントを使用するライブラリは、React 19 を peer dependency としてターゲットにすることができ、`react-server` [エクスポート条件](https://github.com/reactjs/rfcs/blob/main/text/0227-server-module-conventions.md#react-server-conditional-exports) を用いて[フルスタック React アーキテクチャ](/learn/start-a-new-react-project#which-features-make-up-the-react-teams-full-stack-architecture-vision)をサポートするフレームワークで使用できます。 +======= +React 19 includes all of the React Server Components features included from the Canary channel. This means libraries that ship with Server Components can now target React 19 as a peer dependency with a `react-server` [export condition](https://github.com/reactjs/rfcs/blob/main/text/0227-server-module-conventions.md#react-server-conditional-exports) for use in frameworks that support the [Full-stack React Architecture](/learn/creating-a-react-app#which-features-make-up-the-react-teams-full-stack-architecture-vision). +>>>>>>> d52b3ec734077fd56f012fc2b30a67928d14cc73 @@ -389,7 +393,11 @@ React Server Components をバンドラやフレームワークでサポート サーバアクションをサーバコンポーネント内で作成し props としてクライアントコンポーネントに渡すことも、クライアントコンポーネントでインポートして使用することもできます。 +<<<<<<< HEAD 詳細については、[React サーバアクション](/reference/rsc/server-actions)のドキュメントをご覧ください。 +======= +For more, see the docs for [React Server Actions](/reference/rsc/server-functions). +>>>>>>> d52b3ec734077fd56f012fc2b30a67928d14cc73 ## React 19 の改善点 {/*improvements-in-react-19*/} diff --git a/src/content/blog/2025/04/23/react-labs-view-transitions-activity-and-more.md b/src/content/blog/2025/04/23/react-labs-view-transitions-activity-and-more.md index e4bb25a4a..0f54d02b4 100644 --- a/src/content/blog/2025/04/23/react-labs-view-transitions-activity-and-more.md +++ b/src/content/blog/2025/04/23/react-labs-view-transitions-activity-and-more.md @@ -2495,7 +2495,7 @@ For example, we can slow down the `default` cross fade animation: ``` -And define `slow-fade` in CSS using [view transition classes](/reference/react/ViewTransition#view-transition-classes): +And define `slow-fade` in CSS using [view transition classes](/reference/react/ViewTransition#view-transition-class): ```css ::view-transition-old(.slow-fade) { diff --git a/src/content/learn/add-react-to-an-existing-project.md b/src/content/learn/add-react-to-an-existing-project.md index 51af6134e..36cbd82c4 100644 --- a/src/content/learn/add-react-to-an-existing-project.md +++ b/src/content/learn/add-react-to-an-existing-project.md @@ -20,11 +20,19 @@ title: 既存プロジェクトに React を追加する 以下の手順に従って設定することをお勧めします。 +<<<<<<< HEAD 1. [React ベースのフレームワーク](/learn/start-a-new-react-project)のうちひとつを使い、アプリの **React 部分をビルド**します。 2. フレームワークの設定で `/some-app` を ***base path* に指定**します(方法:[Next.js](https://nextjs.org/docs/app/api-reference/config/next-config-js/basePath)、[Gatsby](https://www.gatsbyjs.com/docs/how-to/previews-deploys-hosting/path-prefix/))。 3. **サーバまたはプロキシを設定**して、`/some-app/` 以下のすべてのリクエストを React アプリで処理するようにします。 こうすることで、アプリの React 部分がこれらのフレームワークに組み込まれた[ベストプラクティスを最大限に取り入れる](/learn/start-a-new-react-project#can-i-use-react-without-a-framework)ことができます。 +======= +1. **Build the React part of your app** using one of the [React-based frameworks](/learn/creating-a-react-app). +2. **Specify `/some-app` as the *base path*** in your framework's configuration (here's how: [Next.js](https://nextjs.org/docs/app/api-reference/config/next-config-js/basePath), [Gatsby](https://www.gatsbyjs.com/docs/how-to/previews-deploys-hosting/path-prefix/)). +3. **Configure your server or a proxy** so that all requests under `/some-app/` are handled by your React app. + +This ensures the React part of your app can [benefit from the best practices](/learn/creating-a-react-app#full-stack-frameworks) baked into those frameworks. +>>>>>>> d52b3ec734077fd56f012fc2b30a67928d14cc73 多くの React ベースのフレームワークはフルスタックであり、React アプリがサーバ機能を活用できるようになっています。ただし、サーバで JavaScript を実行できない場合や実行したくない場合でも、同じアプローチが使用できます。この場合、エクスポートされた HTML/CSS/JS(Next.js の場合は [`next export` 出力](https://nextjs.org/docs/advanced-features/static-html-export)、Gatsby の場合はデフォルト)を `/some-app` としてサーブします。 @@ -149,7 +157,11 @@ root.render(); `index.html` にあるオリジナルの HTML コンテンツはそのままに、自分の `NavigationBar` という React コンポーネントが、HTML の `