diff --git a/package.json b/package.json index 01b46a0c2..0a17796b6 100644 --- a/package.json +++ b/package.json @@ -13,17 +13,25 @@ "prettier:diff": "yarn nit:source", "lint-heading-ids": "node scripts/headingIdLinter.js", "fix-headings": "node scripts/headingIdLinter.js --fix", +<<<<<<< HEAD "ci-check": "npm-run-all prettier:diff --parallel lint tsc lint-heading-ids rss lint-editorconfig", +======= + "ci-check": "npm-run-all prettier:diff --parallel lint tsc lint-heading-ids rss deadlinks", +>>>>>>> e9a7cb1b6ca1659b42d81555ecef0cd554b7a983 "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 "cache-reset": "rm -rf node_modules/.cache && rm -rf .next && yarn cache clean", "lint-editorconfig": "yarn editorconfig-checker", "textlint-test": "yarn mocha ./textlint/tests/utils && yarn mocha ./textlint/tests/rules", "textlint-docs": "node ./textlint/generators/genTranslateGlossaryDocs.js && git add wiki/translate-glossary.md", "textlint-lint": "yarn textlint ./src/content --rulesdir ./textlint/rules -f pretty-error && npx --yes eslint@9 -c eslint.config.mjs" +======= + "deadlinks": "node scripts/deadLinkChecker.js" +>>>>>>> e9a7cb1b6ca1659b42d81555ecef0cd554b7a983 }, "dependencies": { "@codesandbox/sandpack-react": "2.13.5", @@ -64,7 +72,11 @@ "autoprefixer": "^10.4.2", "babel-eslint": "10.x", "babel-plugin-react-compiler": "19.0.0-beta-e552027-20250112", +<<<<<<< HEAD "editorconfig-checker": "^6.0.1", +======= + "chalk": "4.1.2", +>>>>>>> e9a7cb1b6ca1659b42d81555ecef0cd554b7a983 "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..90593b878 --- /dev/null +++ b/scripts/deadLinkChecker.js @@ -0,0 +1,385 @@ +#!/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 +const redirectMap = 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 for redirects + if (redirectMap.has(urlWithoutAnchor)) { + const redirectDestination = redirectMap.get(urlWithoutAnchor); + if ( + redirectDestination.startsWith('http://') || + redirectDestination.startsWith('https://') + ) { + return {valid: true}; + } + const redirectedLink = { + ...link, + url: redirectDestination + (anchorMatch ? anchorMatch[0] : ''), + }; + return validateLink(redirectedLink); + } + + // 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`)); + } catch (error) { + throw new Error(`Failed to fetch error codes: ${error.message}`); + } +} + +async function buildRedirectsMap() { + try { + const vercelConfigPath = path.join(__dirname, '../vercel.json'); + const vercelConfig = JSON.parse( + await fs.promises.readFile(vercelConfigPath, 'utf8') + ); + + if (vercelConfig.redirects) { + for (const redirect of vercelConfig.redirects) { + redirectMap.set(redirect.source, redirect.destination); + } + console.log( + chalk.gray(`Loaded ${redirectMap.size} redirects from vercel.json`) + ); + } + } catch (error) { + console.log( + chalk.yellow( + `Warning: Could not load redirects from vercel.json: ${error.message}\n` + ) + ); + } +} + +async function main() { + const files = getMarkdownFiles(); + console.log(chalk.gray(`Checking ${files.length} markdown files...`)); + + await fetchErrorCodes(); + await buildRedirectsMap(); + 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) { + console.log('\n'); + 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/components/Layout/HomeContent.js b/src/components/Layout/HomeContent.js index 8c1219c2a..3043aff07 100644 --- a/src/components/Layout/HomeContent.js +++ b/src/components/Layout/HomeContent.js @@ -782,9 +782,7 @@ function CommunityGallery() { }, []); return ( -
+
- {alt} +
+ {alt} +
))} 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 2fb2cd398..5e322489a 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 +>>>>>>> e9a7cb1b6ca1659b42d81555ecef0cd554b7a983 - [Dan Abramov](/community/team#dan-abramov)은 [RemixConf](https://www.youtube.com/watch?v=zMf_xeGPn6s)에서 "다른 차원의 React"를 주제로 강연했습니다. 이곳에서 React 서버 컴포넌트와 액션을 어떻게 만들었는지에 관한 대안적인 역사를 탐구했습니다. diff --git a/src/content/blog/2024/10/21/react-compiler-beta-release.md b/src/content/blog/2024/10/21/react-compiler-beta-release.md index 7e25ac7a6..db665e797 100644 --- a/src/content/blog/2024/10/21/react-compiler-beta-release.md +++ b/src/content/blog/2024/10/21/react-compiler-beta-release.md @@ -72,11 +72,11 @@ Or, if you're using Yarn: yarn add -D eslint-plugin-react-compiler@beta -After installation you can enable the linter by [adding it to your ESLint config](/learn/react-compiler#installing-eslint-plugin-react-compiler). Using the linter helps identify Rules of React breakages, making it easier to adopt the compiler when it's fully released. +After installation you can enable the linter by [adding it to your ESLint config](/learn/react-compiler/installation#eslint-integration). Using the linter helps identify Rules of React breakages, making it easier to adopt the compiler when it's fully released. ## Backwards Compatibility {/*backwards-compatibility*/} -React Compiler produces code that depends on runtime APIs added in React 19, but we've since added support for the compiler to also work with React 17 and 18. If you are not on React 19 yet, in the Beta release you can now try out React Compiler by specifying a minimum `target` in your compiler config, and adding `react-compiler-runtime` as a dependency. [You can find docs on this here](/learn/react-compiler#using-react-compiler-with-react-17-or-18). +React Compiler produces code that depends on runtime APIs added in React 19, but we've since added support for the compiler to also work with React 17 and 18. If you are not on React 19 yet, in the Beta release you can now try out React Compiler by specifying a minimum `target` in your compiler config, and adding `react-compiler-runtime` as a dependency. [You can find docs on this here](/reference/react-compiler/configuration#react-17-18). ## Using React Compiler in libraries {/*using-react-compiler-in-libraries*/} @@ -86,7 +86,7 @@ React Compiler can also be used to compile libraries. Because React Compiler nee Because your code is pre-compiled, users of your library will not need to have the compiler enabled in order to benefit from the automatic memoization applied to your library. If your library targets apps not yet on React 19, specify a minimum `target` and add `react-compiler-runtime` as a direct dependency. The runtime package will use the correct implementation of APIs depending on the application's version, and polyfill the missing APIs if necessary. -[You can find more docs on this here.](/learn/react-compiler#using-the-compiler-on-libraries) +[You can find more docs on this here.](/reference/react-compiler/compiling-libraries) ## Opening up React Compiler Working Group to everyone {/*opening-up-react-compiler-working-group-to-everyone*/} 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 c725e13f2..780ded2fb 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/community/meetups.md b/src/content/community/meetups.md index b4da5cdff..479ae50d4 100644 --- a/src/content/community/meetups.md +++ b/src/content/community/meetups.md @@ -137,9 +137,6 @@ Do you have a local React.js meetup? Add it here! (Please keep the list alphabet ## Portugal {/*portugal*/} * [Lisbon](https://www.meetup.com/JavaScript-Lisbon/) -## Scotland (UK) {/*scotland-uk*/} -* [Edinburgh](https://www.meetup.com/React-Scotland/) - ## Spain {/*spain*/} * [Barcelona](https://www.meetup.com/ReactJS-Barcelona/) 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 f6f212b83..24ed29417 100644 --- a/src/content/learn/add-react-to-an-existing-project.md +++ b/src/content/learn/add-react-to-an-existing-project.md @@ -24,7 +24,11 @@ title: 기존 프로젝트에 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 앱에서 처리되도록 하세요. +<<<<<<< HEAD 이는 앱의 React 부분이 이러한 프레임워크에 내장된 [최고의 사례들Best Practices로부터 이점을 얻을 수 있습니다.](/learn/start-a-new-react-project#can-i-use-react-without-a-framework) +======= +This ensures the React part of your app can [benefit from the best practices](/learn/build-a-react-app-from-scratch#consider-using-a-framework) baked into those frameworks. +>>>>>>> e9a7cb1b6ca1659b42d81555ecef0cd554b7a983 많은 React 기반의 프레임워크는 풀스택이며 React 앱이 서버를 활용할 수 있도록 합니다. 그러나 서버에서 자바스크립트를 실행할 수 없거나 실행하고 싶지 않은 경우에도 동일한 접근방식을 사용할 수 있습니다. 이러한 경우에는 HTML/CSS/JS 내보내기(Next.js의 경우 [`next export` output](https://nextjs.org/docs/advanced-features/static-html-export), Gatsby의 경우 기본값)를 `/some-app/`에서 대신 제공하세요. diff --git a/src/content/learn/react-compiler/debugging.md b/src/content/learn/react-compiler/debugging.md new file mode 100644 index 000000000..1883125a6 --- /dev/null +++ b/src/content/learn/react-compiler/debugging.md @@ -0,0 +1,93 @@ +--- +title: Debugging and Troubleshooting +--- + + +This guide helps you identify and fix issues when using React Compiler. Learn how to debug compilation problems and resolve common issues. + + + + +* The difference between compiler errors and runtime issues +* Common patterns that break compilation +* Step-by-step debugging workflow + + + +## Understanding Compiler Behavior {/*understanding-compiler-behavior*/} + +React Compiler is designed to handle code that follows the [Rules of React](/reference/rules). When it encounters code that might break these rules, it safely skips optimization rather than risk changing your app's behavior. + +### Compiler Errors vs Runtime Issues {/*compiler-errors-vs-runtime-issues*/} + +**Compiler errors** occur at build time and prevent your code from compiling. These are rare because the compiler is designed to skip problematic code rather than fail. + +**Runtime issues** occur when compiled code behaves differently than expected. Most of the time, if you encounter an issue with React Compiler, it's a runtime issue. This typically happens when your code violates the Rules of React in subtle ways that the compiler couldn't detect, and the compiler mistakenly compiled a component it should have skipped. + +When debugging runtime issues, focus your efforts on finding Rules of React violations in the affected components that were not detected by the ESLint rule. The compiler relies on your code following these rules, and when they're broken in ways it can't detect, that's when runtime problems occur. + + +## Common Breaking Patterns {/*common-breaking-patterns*/} + +One of the main ways React Compiler can break your app is if your code was written to rely on memoization for correctness. This means your app depends on specific values being memoized to work properly. Since the compiler may memoize differently than your manual approach, this can lead to unexpected behavior like effects over-firing, infinite loops, or missing updates. + +Common scenarios where this occurs: + +- **Effects that rely on referential equality** - When effects depend on objects or arrays maintaining the same reference across renders +- **Dependency arrays that need stable references** - When unstable dependencies cause effects to fire too often or create infinite loops +- **Conditional logic based on reference checks** - When code uses referential equality checks for caching or optimization + +## Debugging Workflow {/*debugging-workflow*/} + +Follow these steps when you encounter issues: + +### Compiler Build Errors {/*compiler-build-errors*/} + +If you encounter a compiler error that unexpectedly breaks your build, this is likely a bug in the compiler. Report it to the [facebook/react](https://github.com/facebook/react/issues) repository with: +- The error message +- The code that caused the error +- Your React and compiler versions + +### Runtime Issues {/*runtime-issues*/} + +For runtime behavior issues: + +### 1. Temporarily Disable Compilation {/*temporarily-disable-compilation*/} + +Use `"use no memo"` to isolate whether an issue is compiler-related: + +```js +function ProblematicComponent() { + "use no memo"; // Skip compilation for this component + // ... rest of component +} +``` + +If the issue disappears, it's likely related to a Rules of React violation. + +You can also try removing manual memoization (useMemo, useCallback, memo) from the problematic component to verify that your app works correctly without any memoization. If the bug still occurs when all memoization is removed, you have a Rules of React violation that needs to be fixed. + +### 2. Fix Issues Step by Step {/*fix-issues-step-by-step*/} + +1. Identify the root cause (often memoization-for-correctness) +2. Test after each fix +3. Remove `"use no memo"` once fixed +4. Verify the component shows the ✨ badge in React DevTools + +## Reporting Compiler Bugs {/*reporting-compiler-bugs*/} + +If you believe you've found a compiler bug: + +1. **Verify it's not a Rules of React violation** - Check with ESLint +2. **Create a minimal reproduction** - Isolate the issue in a small example +3. **Test without the compiler** - Confirm the issue only occurs with compilation +4. **File an [issue](https://github.com/facebook/react/issues/new?template=compiler_bug_report.yml)**: + - React and compiler versions + - Minimal reproduction code + - Expected vs actual behavior + - Any error messages + +## Next Steps {/*next-steps*/} + +- Review the [Rules of React](/reference/rules) to prevent issues +- Check the [incremental adoption guide](/learn/react-compiler/incremental-adoption) for gradual rollout strategies \ No newline at end of file diff --git a/src/content/learn/react-compiler/incremental-adoption.md b/src/content/learn/react-compiler/incremental-adoption.md new file mode 100644 index 000000000..56d932034 --- /dev/null +++ b/src/content/learn/react-compiler/incremental-adoption.md @@ -0,0 +1,225 @@ +--- +title: Incremental Adoption +--- + + +React Compiler can be adopted incrementally, allowing you to try it on specific parts of your codebase first. This guide shows you how to gradually roll out the compiler in existing projects. + + + + +* Why incremental adoption is recommended +* Using Babel overrides for directory-based adoption +* Using the "use memo" directive for opt-in compilation +* Using the "use no memo" directive to exclude components +* Runtime feature flags with gating +* Monitoring your adoption progress + + + +## Why Incremental Adoption? {/*why-incremental-adoption*/} + +React Compiler is designed to optimize your entire codebase automatically, but you don't have to adopt it all at once. Incremental adoption gives you control over the rollout process, letting you test the compiler on small parts of your app before expanding to the rest. + +Starting small helps you build confidence in the compiler's optimizations. You can verify that your app behaves correctly with compiled code, measure performance improvements, and identify any edge cases specific to your codebase. This approach is especially valuable for production applications where stability is critical. + +Incremental adoption also makes it easier to address any Rules of React violations the compiler might find. Instead of fixing violations across your entire codebase at once, you can tackle them systematically as you expand compiler coverage. This keeps the migration manageable and reduces the risk of introducing bugs. + +By controlling which parts of your code get compiled, you can also run A/B tests to measure the real-world impact of the compiler's optimizations. This data helps you make informed decisions about full adoption and demonstrates the value to your team. + +## Approaches to Incremental Adoption {/*approaches-to-incremental-adoption*/} + +There are three main approaches to adopt React Compiler incrementally: + +1. **Babel overrides** - Apply the compiler to specific directories +2. **Opt-in with "use memo"** - Only compile components that explicitly opt in +3. **Runtime gating** - Control compilation with feature flags + +All approaches allow you to test the compiler on specific parts of your application before full rollout. + +## Directory-Based Adoption with Babel Overrides {/*directory-based-adoption*/} + +Babel's `overrides` option lets you apply different plugins to different parts of your codebase. This is ideal for gradually adopting React Compiler directory by directory. + +### Basic Configuration {/*basic-configuration*/} + +Start by applying the compiler to a specific directory: + +```js +// babel.config.js +module.exports = { + plugins: [ + // Global plugins that apply to all files + ], + overrides: [ + { + test: './src/modern/**/*.{js,jsx,ts,tsx}', + plugins: [ + 'babel-plugin-react-compiler' + ] + } + ] +}; +``` + +### Expanding Coverage {/*expanding-coverage*/} + +As you gain confidence, add more directories: + +```js +// babel.config.js +module.exports = { + plugins: [ + // Global plugins + ], + overrides: [ + { + test: ['./src/modern/**/*.{js,jsx,ts,tsx}', './src/features/**/*.{js,jsx,ts,tsx}'], + plugins: [ + 'babel-plugin-react-compiler' + ] + }, + { + test: './src/legacy/**/*.{js,jsx,ts,tsx}', + plugins: [ + // Different plugins for legacy code + ] + } + ] +}; +``` + +### With Compiler Options {/*with-compiler-options*/} + +You can also configure compiler options per override: + +```js +// babel.config.js +module.exports = { + plugins: [], + overrides: [ + { + test: './src/experimental/**/*.{js,jsx,ts,tsx}', + plugins: [ + ['babel-plugin-react-compiler', { + // options ... + }] + ] + }, + { + test: './src/production/**/*.{js,jsx,ts,tsx}', + plugins: [ + ['babel-plugin-react-compiler', { + // options ... + }] + ] + } + ] +}; +``` + + +## Opt-in Mode with "use memo" {/*opt-in-mode-with-use-memo*/} + +For maximum control, you can use `compilationMode: 'annotation'` to only compile components and hooks that explicitly opt in with the `"use memo"` directive. + + +This approach gives you fine-grained control over individual components and hooks. It's useful when you want to test the compiler on specific components without affecting entire directories. + + +### Annotation Mode Configuration {/*annotation-mode-configuration*/} + +```js +// babel.config.js +module.exports = { + plugins: [ + ['babel-plugin-react-compiler', { + compilationMode: 'annotation', + }], + ], +}; +``` + +### Using the Directive {/*using-the-directive*/} + +Add `"use memo"` at the beginning of functions you want to compile: + +```js +function TodoList({ todos }) { + "use memo"; // Opt this component into compilation + + const sortedTodos = todos.slice().sort(); + + return ( +
    + {sortedTodos.map(todo => ( + + ))} +
+ ); +} + +function useSortedData(data) { + "use memo"; // Opt this hook into compilation + + return data.slice().sort(); +} +``` + +With `compilationMode: 'annotation'`, you must: +- Add `"use memo"` to every component you want optimized +- Add `"use memo"` to every custom hook +- Remember to add it to new components + +This gives you precise control over which components are compiled while you evaluate the compiler's impact. + +## Runtime Feature Flags with Gating {/*runtime-feature-flags-with-gating*/} + +The `gating` option enables you to control compilation at runtime using feature flags. This is useful for running A/B tests or gradually rolling out the compiler based on user segments. + +### How Gating Works {/*how-gating-works*/} + +The compiler wraps optimized code in a runtime check. If the gate returns `true`, the optimized version runs. Otherwise, the original code runs. + +### Gating Configuration {/*gating-configuration*/} + +```js +// babel.config.js +module.exports = { + plugins: [ + ['babel-plugin-react-compiler', { + gating: { + source: 'ReactCompilerFeatureFlags', + importSpecifierName: 'isCompilerEnabled', + }, + }], + ], +}; +``` + +### Implementing the Feature Flag {/*implementing-the-feature-flag*/} + +Create a module that exports your gating function: + +```js +// ReactCompilerFeatureFlags.js +export function isCompilerEnabled() { + // Use your feature flag system + return getFeatureFlag('react-compiler-enabled'); +} +``` + +## Troubleshooting Adoption {/*troubleshooting-adoption*/} + +If you encounter issues during adoption: + +1. Use `"use no memo"` to temporarily exclude problematic components +2. Check the [debugging guide](/learn/react-compiler/debugging) for common issues +3. Fix Rules of React violations identified by the ESLint plugin +4. Consider using `compilationMode: 'annotation'` for more gradual adoption + +## Next Steps {/*next-steps*/} + +- Read the [configuration guide](/reference/react-compiler/configuration) for more options +- Learn about [debugging techniques](/learn/react-compiler/debugging) +- Check the [API reference](/reference/react-compiler/configuration) for all compiler options \ No newline at end of file diff --git a/src/content/learn/react-compiler/index.md b/src/content/learn/react-compiler/index.md new file mode 100644 index 000000000..480187ed5 --- /dev/null +++ b/src/content/learn/react-compiler/index.md @@ -0,0 +1,33 @@ +--- +title: React Compiler +--- + +## Introduction {/*introduction*/} + +Learn [what React Compiler does](/learn/react-compiler/introduction) and how it automatically optimizes your React application by handling memoization for you, eliminating the need for manual `useMemo`, `useCallback`, and `React.memo`. + +## Installation {/*installation*/} + +Get started with [installing React Compiler](/learn/react-compiler/installation) and learn how to configure it with your build tools. + + +## Incremental Adoption {/*incremental-adoption*/} + +Learn [strategies for gradually adopting React Compiler](/learn/react-compiler/incremental-adoption) in your existing codebase if you're not ready to enable it everywhere yet. + +## Debugging and Troubleshooting {/*debugging-and-troubleshooting*/} + +When things don't work as expected, use our [debugging guide](/learn/react-compiler/debugging) to understand the difference between compiler errors and runtime issues, identify common breaking patterns, and follow a systematic debugging workflow. + +## Configuration and Reference {/*configuration-and-reference*/} + +For detailed configuration options and API reference: + +- [Configuration Options](/reference/react-compiler/configuration) - All compiler configuration options including React version compatibility +- [Directives](/reference/react-compiler/directives) - Function-level compilation control +- [Compiling Libraries](/reference/react-compiler/compiling-libraries) - Shipping pre-compiled libraries + +## Additional resources {/*additional-resources*/} + +In addition to these docs, we recommend checking the [React Compiler Working Group](https://github.com/reactwg/react-compiler) for additional information and discussion about the compiler. + diff --git a/src/content/learn/react-compiler/installation.md b/src/content/learn/react-compiler/installation.md new file mode 100644 index 000000000..3606c9c6d --- /dev/null +++ b/src/content/learn/react-compiler/installation.md @@ -0,0 +1,258 @@ +--- +title: Installation +--- + + +This guide will help you install and configure React Compiler in your React application. + + + + +* How to install React Compiler +* Basic configuration for different build tools +* How to verify your setup is working + + + +## Prerequisites {/*prerequisites*/} + +React Compiler is designed to work best with React 19, but it also supports React 17 and 18. Learn more about [React version compatibility](/reference/react-compiler/target). + + +React Compiler is currently in RC. Install it using the `@rc` tag to get the latest release candidate version. + + +## Installation {/*installation*/} + +Install React Compiler as a `devDependency`: + + +npm install -D babel-plugin-react-compiler@rc + + +Or with Yarn: + + +yarn add -D babel-plugin-react-compiler@rc + + +Or with pnpm: + + +pnpm install -D babel-plugin-react-compiler@rc + + +## Basic Setup {/*basic-setup*/} + +React Compiler is designed to work by default without any configuration. However, if you need to configure it in special circumstances (for example, to target React versions below 19), refer to the [compiler options reference](/reference/react-compiler/configuration). + +The setup process depends on your build tool. React Compiler includes a Babel plugin that integrates with your build pipeline. + + +React Compiler must run **first** in your Babel plugin pipeline. The compiler needs the original source information for proper analysis, so it must process your code before other transformations. + + +### Babel {/*babel*/} + +Create or update your `babel.config.js`: + +```js {3} +module.exports = { + plugins: [ + 'babel-plugin-react-compiler', // must run first! + // ... other plugins + ], + // ... other config +}; +``` + +### Vite {/*vite*/} + +If you use Vite, you can add the plugin to vite-plugin-react: + +```js {3,9} +// vite.config.js +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [ + react({ + babel: { + plugins: ['babel-plugin-react-compiler'], + }, + }), + ], +}); +``` + +Alternatively, if you prefer a separate Babel plugin for Vite: + + +npm install -D vite-plugin-babel + + +```js {2,11} +// vite.config.js +import babel from 'vite-plugin-babel'; +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [ + react(), + babel({ + babelConfig: { + plugins: ['babel-plugin-react-compiler'], + }, + }), + ], +}); +``` + +### Next.js {/*usage-with-nextjs*/} + +Please refer to the [Next.js docs](https://nextjs.org/docs/app/api-reference/next-config-js/reactCompiler) for more information. + +### React Router {/*usage-with-react-router*/} +Install `vite-plugin-babel`, and add the compiler's Babel plugin to it: + + +{`npm install vite-plugin-babel`} + + +```js {3-4,16} +// vite.config.js +import { defineConfig } from "vite"; +import babel from "vite-plugin-babel"; +import { reactRouter } from "@react-router/dev/vite"; + +const ReactCompilerConfig = { /* ... */ }; + +export default defineConfig({ + plugins: [ + reactRouter(), + babel({ + filter: /\.[jt]sx?$/, + babelConfig: { + presets: ["@babel/preset-typescript"], // if you use TypeScript + plugins: [ + ["babel-plugin-react-compiler", ReactCompilerConfig], + ], + }, + }), + ], +}); +``` + +### Webpack {/*usage-with-webpack*/} + +A community webpack loader is [now available here](https://github.com/SukkaW/react-compiler-webpack). + +### Expo {/*usage-with-expo*/} + +Please refer to [Expo's docs](https://docs.expo.dev/guides/react-compiler/) to enable and use the React Compiler in Expo apps. + +### Metro (React Native) {/*usage-with-react-native-metro*/} + +React Native uses Babel via Metro, so refer to the [Usage with Babel](#babel) section for installation instructions. + +### Rspack {/*usage-with-rspack*/} + +Please refer to [Rspack's docs](https://rspack.dev/guide/tech/react#react-compiler) to enable and use the React Compiler in Rspack apps. + +### Rsbuild {/*usage-with-rsbuild*/} + +Please refer to [Rsbuild's docs](https://rsbuild.dev/guide/framework/react#react-compiler) to enable and use the React Compiler in Rsbuild apps. + + +## ESLint Integration {/*eslint-integration*/} + +React Compiler includes an ESLint rule that helps identify code that can't be optimized. When the ESLint rule reports an error, it means the compiler will skip optimizing that specific component or hook. This is safe: the compiler will continue optimizing other parts of your codebase. You don't need to fix all violations immediately. Address them at your own pace to gradually increase the number of optimized components. + +Install the ESLint plugin: + + +npm install -D eslint-plugin-react-hooks@rc + + +Then enable the compiler rule in your ESLint configuration: + +```js {3} +// .eslintrc.js +module.exports = { + rules: { + 'react-hooks/react-compiler': 'error', + }, +}; +``` + +The ESLint rule will: +- Identify violations of the [Rules of React](/reference/rules) +- Show which components can't be optimized +- Provide helpful error messages for fixing issues + +## Verify Your Setup {/*verify-your-setup*/} + +After installation, verify that React Compiler is working correctly. + +### Check React DevTools {/*check-react-devtools*/} + +Components optimized by React Compiler will show a "Memo ✨" badge in React DevTools: + +1. Install the [React Developer Tools](/learn/react-developer-tools) browser extension +2. Open your app in development mode +3. Open React DevTools +4. Look for the ✨ emoji next to component names + +If the compiler is working: +- Components will show a "Memo ✨" badge in React DevTools +- Expensive calculations will be automatically memoized +- No manual `useMemo` is required + +### Check Build Output {/*check-build-output*/} + +You can also verify the compiler is running by checking your build output. The compiled code will include automatic memoization logic that the compiler adds automatically. + +```js +import { c as _c } from "react/compiler-runtime"; +export default function MyApp() { + const $ = _c(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 =
Hello World
; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} + +``` + +## Troubleshooting {/*troubleshooting*/} + +### Opting out specific components {/*opting-out-specific-components*/} + +If a component is causing issues after compilation, you can temporarily opt it out using the `"use no memo"` directive: + +```js +function ProblematicComponent() { + "use no memo"; + // Component code here +} +``` + +This tells the compiler to skip optimization for this specific component. You should fix the underlying issue and remove the directive once resolved. + +For more troubleshooting help, see the [debugging guide](/learn/react-compiler/debugging). + +## Next Steps {/*next-steps*/} + +Now that you have React Compiler installed, learn more about: + +- [React version compatibility](/reference/react-compiler/target) for React 17 and 18 +- [Configuration options](/reference/react-compiler/configuration) to customize the compiler +- [Incremental adoption strategies](/learn/react-compiler/incremental-adoption) for existing codebases +- [Debugging techniques](/learn/react-compiler/debugging) for troubleshooting issues +- [Compiling Libraries guide](/reference/react-compiler/compiling-libraries) for compiling your React library \ No newline at end of file diff --git a/src/content/learn/react-compiler/introduction.md b/src/content/learn/react-compiler/introduction.md new file mode 100644 index 000000000..440c66ab6 --- /dev/null +++ b/src/content/learn/react-compiler/introduction.md @@ -0,0 +1,176 @@ +--- +title: Introduction +--- + + +React Compiler is a new build-time tool that automatically optimizes your React app. It works with plain JavaScript, and understands the [Rules of React](/reference/rules), so you don't need to rewrite any code to use it. + + + + +* What React Compiler does +* Getting started with the compiler +* Incremental adoption strategies +* Debugging and troubleshooting when things go wrong +* Using the compiler on your React library + + + + +React Compiler is currently in Release Candidate (RC). We now recommend everyone to try the compiler and provide feedback. The latest RC release can be found with the `@rc` tag. + + +## What does React Compiler do? {/*what-does-react-compiler-do*/} + +React Compiler automatically optimizes your React application at build time. React is often fast enough without optimization, but sometimes you need to manually memoize components and values to keep your app responsive. This manual memoization is tedious, easy to get wrong, and adds extra code to maintain. React Compiler does this optimization automatically for you, freeing you from this mental burden so you can focus on building features. + +### Before React Compiler {/*before-react-compiler*/} + +Without the compiler, you need to manually memoize components and values to optimize re-renders: + +```js +import { useMemo, useCallback, memo } from 'react'; + +const ExpensiveComponent = memo(function ExpensiveComponent({ data, onClick }) { + const processedData = useMemo(() => { + return expensiveProcessing(data); + }, [data]); + + const handleClick = useCallback((item) => { + onClick(item.id); + }, [onClick]); + + return ( +
+ {processedData.map(item => ( + handleClick(item)} /> + ))} +
+ ); +}); +``` + +### After React Compiler {/*after-react-compiler*/} + +With React Compiler, you write the same code without manual memoization: + +```js +function ExpensiveComponent({ data, onClick }) { + const processedData = expensiveProcessing(data); + + const handleClick = (item) => { + onClick(item.id); + }; + + return ( +
+ {processedData.map(item => ( + handleClick(item)} /> + ))} +
+ ); +} +``` + +_[See this example in the React Compiler Playground](https://playground.react.dev/#N4Igzg9grgTgxgUxALhAMygOzgFwJYSYAEAogB4AOCmYeAbggMIQC2Fh1OAFMEQCYBDHAIA0RQowA2eOAGsiAXwCURYAB1iROITA4iFGBERgwCPgBEhAogF4iCStVoMACoeO1MAcy6DhSgG4NDSItHT0ACwFMPkkmaTlbIi48HAQWFRsAPlUQ0PFMKRlZFLSWADo8PkC8hSDMPJgEHFhiLjzQgB4+eiyO-OADIwQTM0thcpYBClL02xz2zXz8zoBJMqJZBABPG2BU9Mq+BQKiuT2uTJyomLizkoOMk4B6PqX8pSUFfs7nnro3qEapgFCAFEA)_ + +React Compiler automatically applies the equivalent optimizations, ensuring your app only re-renders when necessary. + + +#### What kind of memoization does React Compiler add? {/*what-kind-of-memoization-does-react-compiler-add*/} + +React Compiler's automatic memoization is primarily focused on **improving update performance** (re-rendering existing components), so it focuses on these two use cases: + +1. **Skipping cascading re-rendering of components** + * Re-rendering `` causes many components in its component tree to re-render, even though only `` has changed +1. **Skipping expensive calculations from outside of React** + * For example, calling `expensivelyProcessAReallyLargeArrayOfObjects()` inside of your component or hook that needs that data + +#### Optimizing Re-renders {/*optimizing-re-renders*/} + +React lets you express your UI as a function of their current state (more concretely: their props, state, and context). In its current implementation, when a component's state changes, React will re-render that component _and all of its children_ — unless you have applied some form of manual memoization with `useMemo()`, `useCallback()`, or `React.memo()`. For example, in the following example, `` will re-render whenever ``'s state changes: + +```javascript +function FriendList({ friends }) { + const onlineCount = useFriendOnlineCount(); + if (friends.length === 0) { + return ; + } + return ( +
+ {onlineCount} online + {friends.map((friend) => ( + + ))} + +
+ ); +} +``` +[_See this example in the React Compiler Playground_](https://playground.react.dev/#N4Igzg9grgTgxgUxALhAMygOzgFwJYSYAEAYjHgpgCYAyeYOAFMEWuZVWEQL4CURwADrEicQgyKEANnkwIAwtEw4iAXiJQwCMhWoB5TDLmKsTXgG5hRInjRFGbXZwB0UygHMcACzWr1ABn4hEWsYBBxYYgAeADkIHQ4uAHoAPksRbisiMIiYYkYs6yiqPAA3FMLrIiiwAAcAQ0wU4GlZBSUcbklDNqikusaKkKrgR0TnAFt62sYHdmp+VRT7SqrqhOo6Bnl6mCoiAGsEAE9VUfmqZzwqLrHqM7ubolTVol5eTOGigFkEMDB6u4EAAhKA4HCEZ5DNZ9ErlLIWYTcEDcIA) + +React Compiler automatically applies the equivalent of manual memoization, ensuring that only the relevant parts of an app re-render as state changes, which is sometimes referred to as "fine-grained reactivity". In the above example, React Compiler determines that the return value of `` can be reused even as `friends` changes, and can avoid recreating this JSX _and_ avoid re-rendering `` as the count changes. + +#### Expensive calculations also get memoized {/*expensive-calculations-also-get-memoized*/} + +React Compiler can also automatically memoize expensive calculations used during rendering: + +```js +// **Not** memoized by React Compiler, since this is not a component or hook +function expensivelyProcessAReallyLargeArrayOfObjects() { /* ... */ } + +// Memoized by React Compiler since this is a component +function TableContainer({ items }) { + // This function call would be memoized: + const data = expensivelyProcessAReallyLargeArrayOfObjects(items); + // ... +} +``` +[_See this example in the React Compiler Playground_](https://playground.react.dev/#N4Igzg9grgTgxgUxALhAejQAgFTYHIQAuumAtgqRAJYBeCAJpgEYCemASggIZyGYDCEUgAcqAGwQwANJjBUAdokyEAFlTCZ1meUUxdMcIcIjyE8vhBiYVECAGsAOvIBmURYSonMCAB7CzcgBuCGIsAAowEIhgYACCnFxioQAyXDAA5gixMDBcLADyzvlMAFYIvGAAFACUmMCYaNiYAHStOFgAvk5OGJgAshTUdIysHNy8AkbikrIKSqpaWvqGIiZmhE6u7p7ymAAqXEwSguZcCpKV9VSEFBodtcBOmAYmYHz0XIT6ALzefgFUYKhCJRBAxeLcJIsVIZLI5PKFYplCqVa63aoAbm6u0wMAQhFguwAPPRAQA+YAfL4dIloUmBMlODogDpAA) + +However, if `expensivelyProcessAReallyLargeArrayOfObjects` is truly an expensive function, you may want to consider implementing its own memoization outside of React, because: + +- React Compiler only memoizes React components and hooks, not every function +- React Compiler's memoization is not shared across multiple components or hooks + +So if `expensivelyProcessAReallyLargeArrayOfObjects` was used in many different components, even if the same exact items were passed down, that expensive calculation would be run repeatedly. We recommend [profiling](reference/react/useMemo#how-to-tell-if-a-calculation-is-expensive) first to see if it really is that expensive before making code more complicated. +
+ +## Should I try out the compiler? {/*should-i-try-out-the-compiler*/} + +We encourage everyone to start using React Compiler. While the compiler is still an optional addition to React today, in the future some features may require the compiler in order to fully work. + +### Is it safe to use? {/*is-it-safe-to-use*/} + +React Compiler is now in RC and has been tested extensively in production. While it has been used in production at companies like Meta, rolling out the compiler to production for your app will depend on the health of your codebase and how well you've followed the [Rules of React](/reference/rules). + +## What build tools are supported? {/*what-build-tools-are-supported*/} + +React Compiler can be installed across [several build tools](/learn/react-compiler/installation) such as Babel, Vite, Metro, and Rsbuild. + +React Compiler is primarily a light Babel plugin wrapper around the core compiler, which was designed to be decoupled from Babel itself. While the initial stable version of the compiler will remain primarily a Babel plugin, we are working with the swc and [oxc](https://github.com/oxc-project/oxc/issues/10048) teams to build first class support for React Compiler so you won't have to add Babel back to your build pipelines in the future. + +Next.js users can enable the swc-invoked React Compiler by using [v15.3.1](https://github.com/vercel/next.js/releases/tag/v15.3.1) and up. + +## What should I do about useMemo, useCallback, and React.memo? {/*what-should-i-do-about-usememo-usecallback-and-reactmemo*/} + +If you are using React Compiler, [`useMemo`](/reference/react/useMemo), [`useCallback`](/reference/react/useCallback), and [`React.memo`](/reference/react/memo) can be removed. React Compiler adds automatic memoization more precisely and granularly than is possible with these hooks. If you choose to keep manual memoization, React Compiler will analyze them and determine if your manual memoization matches its automatically inferred memoization. If there isn't a match, the compiler will choose to bail out of optimizing that component. + +This is done out of caution as a common anti-pattern with manual memoization is using it for correctness. This means your app depends on specific values being memoized to work properly. For example, in order to prevent an infinite loop, you may have memoized some values to stop a `useEffect` call from firing. This breaks the Rules of React, but since it can potentially be dangerous for the compiler to automatically remove manual memoization, the compiler will just bail out instead. You should manually remove your handwritten memoization and verify that your app still works as expected. + +## Try React Compiler {/*try-react-compiler*/} + +This section will help you get started with React Compiler and understand how to use it effectively in your projects. + +* **[Installation](/learn/react-compiler/installation)** - Install React Compiler and configure it for your build tools +* **[React Version Compatibility](/reference/react-compiler/target)** - Support for React 17, 18, and 19 +* **[Configuration](/reference/react-compiler/configuration)** - Customize the compiler for your specific needs +* **[Incremental Adoption](/learn/react-compiler/incremental-adoption)** - Strategies for gradually rolling out the compiler in existing codebases +* **[Debugging and Troubleshooting](/learn/react-compiler/debugging)** - Identify and fix issues when using the compiler +* **[Compiling Libraries](/reference/react-compiler/compiling-libraries)** - Best practices for shipping compiled code +* **[API Reference](/reference/react-compiler/configuration)** - Detailed documentation of all configuration options + +## Additional resources {/*additional-resources*/} + +In addition to these docs, we recommend checking the [React Compiler Working Group](https://github.com/reactwg/react-compiler) for additional information and discussion about the compiler. + diff --git a/src/content/learn/synchronizing-with-effects.md b/src/content/learn/synchronizing-with-effects.md index a4cb2fde7..ae6891461 100644 --- a/src/content/learn/synchronizing-with-effects.md +++ b/src/content/learn/synchronizing-with-effects.md @@ -732,8 +732,13 @@ Effect 안에서 `fetch` 호출을 작성하는 것은 [데이터를 가져오 이 단점 목록은 React에만 해당되는 것은 아닙니다. 어떤 라이브러리에서든 마운트 시에 데이터를 가져온다면 비슷한 단점이 존재합니다. 마운트 시에 데이터를 페칭하는 것도 라우팅과 마찬가지로 잘 수행하기 어려운 작업이므로 다음 접근 방식을 권장합니다. +<<<<<<< HEAD - **[프레임워크](/learn/start-a-new-react-project#production-grade-react-frameworks)를 사용하는 경우 해당 프레임워크의 내장 데이터 페칭 메커니즘을 사용하세요.** 현대적인 React 프레임워크에는 위의 단점을 겪지 않는 효율적이고 통합적인 데이터 페칭 메커니즘이 포함되어 있습니다. - **그렇지 않은 경우 클라이언트 측 캐시를 사용하거나 구축하는 것을 고려하세요.** 인기 있는 오픈 소스 솔루션으로는 [React Query](https://tanstack.com/query/latest), [useSWR](https://swr.vercel.app/) 및 [React Router 6.4+](https://beta.reactrouter.com/en/main/start/overview)이 있습니다. 직접 솔루션을 구축할 수도 있으며 이 경우 Effect를 내부적으로 사용하면서 요청 중복을 제거하고 응답을 캐시하고 네트워크 폭포를 피하는 로직을 추가할 것입니다. (데이터를 사전에 로드하거나 데이터 요구 사항을 라우트) +======= +- **If you use a [framework](/learn/start-a-new-react-project#full-stack-frameworks), use its built-in data fetching mechanism.** Modern React frameworks have integrated data fetching mechanisms that are efficient and don't suffer from the above pitfalls. +- **Otherwise, consider using or building a client-side cache.** Popular open source solutions include [React Query](https://tanstack.com/query/latest), [useSWR](https://swr.vercel.app/), and [React Router 6.4+.](https://beta.reactrouter.com/en/main/start/overview) You can build your own solution too, in which case you would use Effects under the hood, but add logic for deduplicating requests, caching responses, and avoiding network waterfalls (by preloading data or hoisting data requirements to routes). +>>>>>>> e9a7cb1b6ca1659b42d81555ecef0cd554b7a983 이러한 접근 방식 중 어느 것도 적합하지 않은 경우, Effect 내에서 데이터를 직접 가져오는 것을 계속하셔도 됩니다. diff --git a/src/content/learn/typescript.md b/src/content/learn/typescript.md index 7822f3623..1ccf1a984 100644 --- a/src/content/learn/typescript.md +++ b/src/content/learn/typescript.md @@ -11,16 +11,27 @@ TypeScript는 JavaScript 코드 베이스에 타입 정의를 추가하는 데 +<<<<<<< HEAD * [React 컴포넌트가 있는 TypeScript](/learn/typescript#typescript-with-react-components) * [Hooks 타이핑 예시](/learn/typescript#example-hooks) * [`@types/react`의 일반적인 타입](/learn/typescript/#useful-types) * [추가 학습 위치](/learn/typescript/#further-learning) +======= +* [TypeScript with React Components](/learn/typescript#typescript-with-react-components) +* [Examples of typing with Hooks](/learn/typescript#example-hooks) +* [Common types from `@types/react`](/learn/typescript#useful-types) +* [Further learning locations](/learn/typescript#further-learning) +>>>>>>> e9a7cb1b6ca1659b42d81555ecef0cd554b7a983 ## 설치 {/*installation*/} +<<<<<<< HEAD 모든 [프로덕션 수준의 React 프레임워크](/learn/start-a-new-react-project#production-grade-react-frameworks)는 TypeScript 사용을 지원합니다. 프레임워크별 설치 가이드를 따르세요. +======= +All [production-grade React frameworks](/learn/start-a-new-react-project#full-stack-frameworks) offer support for using TypeScript. Follow the framework specific guide for installation: +>>>>>>> e9a7cb1b6ca1659b42d81555ecef0cd554b7a983 - [Next.js](https://nextjs.org/docs/app/building-your-application/configuring/typescript) - [Remix](https://remix.run/docs/en/1.19.2/guides/typescript) @@ -124,7 +135,11 @@ export default App = AppTSX; ## Hooks 예시 {/*example-hooks*/} +<<<<<<< HEAD `@types/react`의 타입 정의에는 내장 Hooks에 대한 타입이 포함되어 있으므로 추가 설정 없이 컴포넌트에 사용할 수 있습니다. 컴포넌트에 작성한 코드를 고려하도록 만들어졌기 때문에 대부분의 경우 [추론된 타입](https://www.typescriptlang.org/ko/docs/handbook/type-inference.html)을 얻을 수 있으며, 이상적으로는 타입을 제공하는 사소한 작업을 처리할 필요가 없습니다. +======= +The type definitions from `@types/react` include types for the built-in Hooks, so you can use them in your components without any additional setup. They are built to take into account the code you write in your component, so you will get [inferred types](https://www.typescriptlang.org/docs/handbook/type-inference.html) a lot of the time and ideally do not need to handle the minutiae of providing the types. +>>>>>>> e9a7cb1b6ca1659b42d81555ecef0cd554b7a983 하지만, hooks에 타입을 제공하는 방법의 몇 가지 예시를 볼 수 있습니다. @@ -140,7 +155,11 @@ const [enabled, setEnabled] = useState(false); `boolean` 타입이 `enabled`에 할당되고, `setEnabled` 는 `boolean` 인수나 `boolean`을 반환하는 함수를 받는 함수가 됩니다. state에 대한 타입을 명시적으로 제공하려면 `useState` 호출에 타입 인수를 제공하면 됩니다. ```ts +<<<<<<< HEAD // 명시적으로 타입을 "boolean"으로 설정합니다 +======= +// Explicitly set the type to "boolean" +>>>>>>> e9a7cb1b6ca1659b42d81555ecef0cd554b7a983 const [enabled, setEnabled] = useState(false); ``` @@ -284,7 +303,11 @@ export default App = AppTSX; +<<<<<<< HEAD 이 기술은 합리적인 기본값이 있을 때 효과적이지만 그렇지 않은 경우도 간혹 있으며, 그러한 경우 `null`이 기본값으로 합리적이라고 느낄 수 있습니다. 그러나, 타입 시스템이 코드를 이해할 수 있도록 하려면 `createContext`에서 `ContextShape | null`을 명시적으로 설정해야 합니다. +======= +This technique works when you have a default value which makes sense - but there are occasionally cases when you do not, and in those cases `null` can feel reasonable as a default value. However, to allow the type-system to understand your code, you need to explicitly set `ContextShape | null` on the `createContext`. +>>>>>>> e9a7cb1b6ca1659b42d81555ecef0cd554b7a983 이에 따라 context 소비자에 대한 타입에서 `| null`을 제거해야 하는 문제가 발생합니다. 권장 사항은 Hook이 런타임에 존재 여부를 검사하고 존재하지 않을 경우 에러를 throw 하는 것입니다. @@ -329,7 +352,17 @@ function MyComponent() { ### `useMemo` {/*typing-usememo*/} +<<<<<<< HEAD [`useMemo`](/reference/react/useMemo) Hooks는 함수 호출로부터 memorized 된 값을 생성/재접근하여, 두 번째 매개변수로 전달된 종속성이 변경될 때만 함수를 다시 실행합니다. Hook을 호출한 결과는 첫 번째 매개변수에 있는 함수의 반환 값에서 추론됩니다. Hook에 타입 인수를 제공하여 더욱더 명확하게 할 수 있습니다. +======= + + +[React Compiler](/learn/react-compiler) automatically memoizes values and functions, reducing the need for manual `useMemo` calls. You can use the compiler to handle memoization automatically. + + + +The [`useMemo`](/reference/react/useMemo) Hooks will create/re-access a memorized value from a function call, re-running the function only when dependencies passed as the 2nd parameter are changed. The result of calling the Hook is inferred from the return value from the function in the first parameter. You can be more explicit by providing a type argument to the Hook. +>>>>>>> e9a7cb1b6ca1659b42d81555ecef0cd554b7a983 ```ts // visibleTodos의 타입은 filterTodos의 반환 값에서 추론됩니다. @@ -339,7 +372,17 @@ const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]); ### `useCallback` {/*typing-usecallback*/} +<<<<<<< HEAD [`useCallback`](/reference/react/useCallback)는 두 번째 매개변수로 전달되는 종속성이 같다면 함수에 대한 안정적인 참조를 제공합니다. `useMemo`와 마찬가지로, 함수의 타입은 첫 번째 매개변수에 있는 함수의 반환 값에서 추론되며, Hook에 타입 인수를 제공하여 더욱더 명확하게 할 수 있습니다. +======= + + +[React Compiler](/learn/react-compiler) automatically memoizes values and functions, reducing the need for manual `useCallback` calls. You can use the compiler to handle memoization automatically. + + + +The [`useCallback`](/reference/react/useCallback) provide a stable reference to a function as long as the dependencies passed into the second parameter are the same. Like `useMemo`, the function's type is inferred from the return value of the function in the first parameter, and you can be more explicit by providing a type argument to the Hook. +>>>>>>> e9a7cb1b6ca1659b42d81555ecef0cd554b7a983 ```ts @@ -350,7 +393,11 @@ const handleClick = useCallback(() => { TypeScript strict mode에서 작업할 때 `useCallback`을 사용하려면 콜백에 매개변수를 위한 타입을 추가해야 합니다. 콜백의 타입은 함수의 반환 값에서 추론되고, 매개변수 없이는 타입을 완전히 이해할 수 없기 때문입니다. +<<<<<<< HEAD 코드 스타일 선호도에 따라, 콜백을 정의하는 동시에 이벤트 핸들러의 타입을 제공하기 위해 React 타입의 `*EventHandler` 함수를 사용할 수 있습니다. +======= +Depending on your code-style preferences, you could use the `*EventHandler` functions from the React types to provide the type for the event handler at the same time as defining the callback: +>>>>>>> e9a7cb1b6ca1659b42d81555ecef0cd554b7a983 ```ts import { useState, useCallback } from 'react'; @@ -433,7 +480,11 @@ interface ModalRendererProps { } ``` +<<<<<<< HEAD 자식이 특정 JSX 엘리먼트 타입이라고 설명하기 위해 TypeScript를 사용할 수 없으므로, `
  • ` 자식만 허용하는 컴포넌트를 설명하기 위해 타입 시스템을 사용할 수 없다는 점에 주의하세요. +======= +Note, that you cannot use TypeScript to describe that the children are a certain type of JSX elements, so you cannot use the type-system to describe a component which only accepts `
  • ` children. +>>>>>>> e9a7cb1b6ca1659b42d81555ecef0cd554b7a983 [TypeScript 플레이그라운드](https://www.typescriptlang.org/ko/play?#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgIilQ3wChSB6CxYmAOmXRgDkIATJOdNJMGAZzgwAFpxAR+8YADswAVwGkZMJFEzpOjDKw4AFHGEEBvUnDhphwADZsi0gFw0mDWjqQBuUgF9yaCNMlENzgAXjgACjADfkctFnYkfQhDAEpQgD44AB42YAA3dKMo5P46C2tbJGkvLIpcgt9-QLi3AEEwMFCItJDMrPTTbIQ3dKywdIB5aU4kKyQQKpha8drhhIGzLLWODbNs3b3s8YAxKBQAcwXpAThMaGWDvbH0gFloGbmrgQfBzYpd1YjQZbEYARkB6zMwO2SHSAAlZlYIBCdtCRkZpHIrFYahQYQD8UYYFA5EhcfjyGYqHAXnJAsIUHlOOUbHYhMIIHJzsI0Qk4P9SLUBuRqXEXEwAKKfRZcNA8PiCfxWACecAAUgBlAAacFm80W-CU11U6h4TgwUv11yShjgJjMLMqDnN9Dilq+nh8pD8AXgCHdMrCkWisVoAet0R6fXqhWKhjKllZVVxMcavpd4Zg7U6Qaj+2hmdG4zeRF10uu-Aeq0LBfLMEe-V+T2L7zLVu+FBWLdLeq+lc7DYFf39deFVOotMCACNOCh1dq219a+30uC8YWoZsRyuEdjkevR8uvoVMdjyTWt4WiSSydXD4NqZP4AymeZE072ZzuUeZQKheQgA)에서 타입 체커를 사용하여 `React.ReactNode`와 `React.ReactElement`의 모든 예시를 확인할 수 있습니다. diff --git a/src/content/learn/you-might-not-need-an-effect.md b/src/content/learn/you-might-not-need-an-effect.md index 7c1e57132..5a9d0178a 100644 --- a/src/content/learn/you-might-not-need-an-effect.md +++ b/src/content/learn/you-might-not-need-an-effect.md @@ -26,7 +26,11 @@ Effect가 필요하지 않은 두 가지 일반적인 경우가 있습니다. * **렌더링을 위해 데이터를 변환하는 데 Effect가 필요하지 않습니다.** 예를 들어 리스트를 표시하기 전에 필터링하고 싶다고 가정해 보겠습니다. 리스트가 변경될 때 state 변수를 업데이트하는 Effect를 작성하고 싶을 수 있습니다. 하지만 이는 비효율적입니다. state를 업데이트할 때 React는 먼저 컴포넌트 함수를 호출해 화면에 표시될 내용을 계산합니다. 그런 다음 React는 이러한 변경 사항을 DOM에 ["commit"](/learn/render-and-commit)하여 화면을 업데이트합니다. 그리고 나서 React가 Effect를 실행합니다. 만약 Effect도 *즉시* state를 업데이트한다면 전체 프로세스가 처음부터 다시 시작됩니다! 불필요한 렌더링 패스를 피하려면, 컴포넌트의 최상위 레벨에서 모든 데이터를 변환하세요. 그러면 props나 state가 변경될 때마다 해당 코드가 자동으로 다시 실행됩니다. * **사용자 이벤트를 처리하는 데 Effect가 필요하지 않습니다.** 예를 들어 사용자가 제품을 구매할 때 `/api/buy` POST 요청을 전송하고 알림을 표시하고 싶다고 가정해 보겠습니다. 구매 버튼 클릭 이벤트 핸들러에서는 정확히 어떤 일이 일어났는지 알 수 있습니다. Effect가 실행될 때까지 사용자가 무엇을 했는지 (예: 어떤 버튼을 클릭 했는지) 알 수 없습니다. 그렇기 때문에 일반적으로 해당되는 이벤트 핸들러에서 사용자 이벤트를 처리합니다. +<<<<<<< HEAD 외부 시스템과 [동기화](/learn/synchronizing-with-effects#what-are-effects-and-how-are-they-different-from-events)하려면 Effect가 *반드시* 필요합니다. 예를 들어 jQuery 위젯이 React State와 동기화되도록 유지하는 Effect를 작성할 수 있습니다. Effect로 데이터를 가져올 수도 있습니다: 예를 들어 검색 결과를 현재 검색 쿼리와 동기화할 수 있습니다. 모던 [프레임워크](/learn/start-a-new-react-project#production-grade-react-frameworks)는 컴포넌트에 직접 Effect를 작성하는 것보다 더 효율적인 내장 데이터 가져오기 메커니즘을 제공한다는 점을 명심하세요. +======= +You *do* need Effects to [synchronize](/learn/synchronizing-with-effects#what-are-effects-and-how-are-they-different-from-events) with external systems. For example, you can write an Effect that keeps a jQuery widget synchronized with the React state. You can also fetch data with Effects: for example, you can synchronize the search results with the current search query. Keep in mind that modern [frameworks](/learn/start-a-new-react-project#full-stack-frameworks) provide more efficient built-in data fetching mechanisms than writing Effects directly in your components. +>>>>>>> e9a7cb1b6ca1659b42d81555ecef0cd554b7a983 올바른 직관을 얻기 위해, 몇 가지 일반적이고 구체적인 예를 살펴봅시다! @@ -95,6 +99,12 @@ function TodoList({ todos, filter }) { [`useMemo`](/reference/react/useMemo) Hook으로 래핑해서 값비싼 계산을 캐시(또는 ["메모이제이션"](https://ko.wikipedia.org/wiki/메모이제이션)) 할 수 있습니다. + + +[React Compiler](/learn/react-compiler) can automatically memoize expensive calculations for you, eliminating the need for manual `useMemo` in many cases. + + + ```js {5-8} import { useMemo, useState } from 'react'; @@ -752,7 +762,11 @@ function SearchResults({ query }) { 데이터 가져오기를 구현할 때 경쟁 조건을 처리하는 것만이 어려운 것은 아닙니다. 응답 캐싱(사용자가 뒤로가기 버튼을 클릭하여 이전 화면을 즉시 볼 수 있도록), 서버에서 데이터를 가져오는 방법(초기 서버 렌더링 HTML에 스피너 대신 가져온 콘텐츠가 포함되도록), 네트워크 워터폴을 피하는 방법(자식이 모든 부모를 기다리지 않고 데이터를 가져올 수 있도록)도 고려해야 합니다. +<<<<<<< HEAD **이러한 문제는 React뿐만 아니라 모든 UI 라이브러리에 적용됩니다. 이러한 문제를 해결하는 것은 간단하지 않기 때문에 모던 [프레임워크](/learn/start-a-new-react-project#production-grade-react-frameworks)는 Effect에서 데이터를 가져오는 것보다 더 효율적인 내장 데이터 가져오기 메커니즘을 제공합니다.** +======= +**These issues apply to any UI library, not just React. Solving them is not trivial, which is why modern [frameworks](/learn/start-a-new-react-project#full-stack-frameworks) provide more efficient built-in data fetching mechanisms than fetching data in Effects.** +>>>>>>> e9a7cb1b6ca1659b42d81555ecef0cd554b7a983 프레임워크를 사용하지 않고(그리고 직접 빌드하고 싶지 않고) Effect에서 데이터를 보다 인체공학적으로 가져오고 싶다면 이 예시처럼 가져오기 로직을 사용자 정의 Hook으로 추출하는 것을 고려하세요. diff --git a/src/content/reference/react-compiler/compilationMode.md b/src/content/reference/react-compiler/compilationMode.md new file mode 100644 index 000000000..5513d1c6a --- /dev/null +++ b/src/content/reference/react-compiler/compilationMode.md @@ -0,0 +1,201 @@ +--- +title: compilationMode +--- + + + +The `compilationMode` option controls how the React Compiler selects which functions to compile. + + + +```js +{ + compilationMode: 'infer' // or 'annotation', 'syntax', 'all' +} +``` + + + +--- + +## Reference {/*reference*/} + +### `compilationMode` {/*compilationmode*/} + +Controls the strategy for determining which functions the React Compiler will optimize. + +#### Type {/*type*/} + +``` +'infer' | 'syntax' | 'annotation' | 'all' +``` + +#### Default value {/*default-value*/} + +`'infer'` + +#### Options {/*options*/} + +- **`'infer'`** (default): The compiler uses intelligent heuristics to identify React components and hooks: + - Functions explicitly annotated with `"use memo"` directive + - Functions that are named like components (PascalCase) or hooks (`use` prefix) AND create JSX and/or call other hooks + +- **`'annotation'`**: Only compile functions explicitly marked with the `"use memo"` directive. Ideal for incremental adoption. + +- **`'syntax'`**: Only compile components and hooks that use Flow's [component](https://flow.org/en/docs/react/component-syntax/) and [hook](https://flow.org/en/docs/react/hook-syntax/) syntax. + +- **`'all'`**: Compile all top-level functions. Not recommended as it may compile non-React functions. + +#### Caveats {/*caveats*/} + +- The `'infer'` mode requires functions to follow React naming conventions to be detected +- Using `'all'` mode may negatively impact performance by compiling utility functions +- The `'syntax'` mode requires Flow and won't work with TypeScript +- Regardless of mode, functions with `"use no memo"` directive are always skipped + +--- + +## Usage {/*usage*/} + +### Default inference mode {/*default-inference-mode*/} + +The default `'infer'` mode works well for most codebases that follow React conventions: + +```js +{ + compilationMode: 'infer' +} +``` + +With this mode, these functions will be compiled: + +```js +// ✅ Compiled: Named like a component + returns JSX +function Button(props) { + return ; +} + +// ✅ Compiled: Named like a hook + calls hooks +function useCounter() { + const [count, setCount] = useState(0); + return [count, setCount]; +} + +// ✅ Compiled: Explicit directive +function expensiveCalculation(data) { + "use memo"; + return data.reduce(/* ... */); +} + +// ❌ Not compiled: Not a component/hook pattern +function calculateTotal(items) { + return items.reduce((a, b) => a + b, 0); +} +``` + +### Incremental adoption with annotation mode {/*incremental-adoption*/} + +For gradual migration, use `'annotation'` mode to only compile marked functions: + +```js +{ + compilationMode: 'annotation' +} +``` + +Then explicitly mark functions to compile: + +```js +// Only this function will be compiled +function ExpensiveList(props) { + "use memo"; + return ( +
      + {props.items.map(item => ( +
    • {item.name}
    • + ))} +
    + ); +} + +// This won't be compiled without the directive +function NormalComponent(props) { + return
    {props.content}
    ; +} +``` + +### Using Flow syntax mode {/*flow-syntax-mode*/} + +If your codebase uses Flow instead of TypeScript: + +```js +{ + compilationMode: 'syntax' +} +``` + +Then use Flow's component syntax: + +```js +// Compiled: Flow component syntax +component Button(label: string) { + return ; +} + +// Compiled: Flow hook syntax +hook useCounter(initial: number) { + const [count, setCount] = useState(initial); + return [count, setCount]; +} + +// Not compiled: Regular function syntax +function helper(data) { + return process(data); +} +``` + +### Opting out specific functions {/*opting-out*/} + +Regardless of compilation mode, use `"use no memo"` to skip compilation: + +```js +function ComponentWithSideEffects() { + "use no memo"; // Prevent compilation + + // This component has side effects that shouldn't be memoized + logToAnalytics('component_rendered'); + + return
    Content
    ; +} +``` + +--- + +## Troubleshooting {/*troubleshooting*/} + +### Component not being compiled in infer mode {/*component-not-compiled-infer*/} + +In `'infer'` mode, ensure your component follows React conventions: + +```js +// ❌ Won't be compiled: lowercase name +function button(props) { + return ; +} + +// ✅ Will be compiled: PascalCase name +function Button(props) { + return ; +} + +// ❌ Won't be compiled: doesn't create JSX or call hooks +function useData() { + return window.localStorage.getItem('data'); +} + +// ✅ Will be compiled: calls a hook +function useData() { + const [data] = useState(() => window.localStorage.getItem('data')); + return data; +} +``` diff --git a/src/content/reference/react-compiler/compiling-libraries.md b/src/content/reference/react-compiler/compiling-libraries.md new file mode 100644 index 000000000..f09ffcb72 --- /dev/null +++ b/src/content/reference/react-compiler/compiling-libraries.md @@ -0,0 +1,106 @@ +--- +title: Compiling Libraries +--- + + +This guide helps library authors understand how to use React Compiler to ship optimized library code to their users. + + + + +## Why Ship Compiled Code? {/*why-ship-compiled-code*/} + +As a library author, you can compile your library code before publishing to npm. This provides several benefits: + +- **Performance improvements for all users** - Your library users get optimized code even if they aren't using React Compiler yet +- **No configuration required by users** - The optimizations work out of the box +- **Consistent behavior** - All users get the same optimized version regardless of their build setup + +## Setting Up Compilation {/*setting-up-compilation*/} + +Add React Compiler to your library's build process: + + +npm install -D babel-plugin-react-compiler@rc + + +Configure your build tool to compile your library. For example, with Babel: + +```js +// babel.config.js +module.exports = { + plugins: [ + 'babel-plugin-react-compiler', + ], + // ... other config +}; +``` + +## Backwards Compatibility {/*backwards-compatibility*/} + +If your library supports React versions below 19, you'll need additional configuration: + +### 1. Install the runtime package {/*install-runtime-package*/} + +We recommend installing react-compiler-runtime as a direct dependency: + + +npm install react-compiler-runtime@rc + + +```json +{ + "dependencies": { + "react-compiler-runtime": "^19.1.0-rc.2" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + } +} +``` + +### 2. Configure the target version {/*configure-target-version*/} + +Set the minimum React version your library supports: + +```js +{ + target: '17', // Minimum supported React version +} +``` + +## Testing Strategy {/*testing-strategy*/} + +Test your library both with and without compilation to ensure compatibility. Run your existing test suite against the compiled code, and also create a separate test configuration that bypasses the compiler. This helps catch any issues that might arise from the compilation process and ensures your library works correctly in all scenarios. + +## Troubleshooting {/*troubleshooting*/} + +### Library doesn't work with older React versions {/*library-doesnt-work-with-older-react-versions*/} + +If your compiled library throws errors in React 17 or 18: + +1. Verify you've installed `react-compiler-runtime` as a dependency +2. Check that your `target` configuration matches your minimum supported React version +3. Ensure the runtime package is included in your published bundle + +### Compilation conflicts with other Babel plugins {/*compilation-conflicts-with-other-babel-plugins*/} + +Some Babel plugins may conflict with React Compiler: + +1. Place `babel-plugin-react-compiler` early in your plugin list +2. Disable conflicting optimizations in other plugins +3. Test your build output thoroughly + +### Runtime module not found {/*runtime-module-not-found*/} + +If users see "Cannot find module 'react-compiler-runtime'": + +1. Ensure the runtime is listed in `dependencies`, not `devDependencies` +2. Check that your bundler includes the runtime in the output +3. Verify the package is published to npm with your library + +## Next Steps {/*next-steps*/} + +- Learn about [debugging techniques](/learn/react-compiler/debugging) for compiled code +- Check the [configuration options](/reference/react-compiler/configuration) for all compiler options +- Explore [compilation modes](/reference/react-compiler/compilationMode) for selective optimization \ No newline at end of file diff --git a/src/content/reference/react-compiler/configuration.md b/src/content/reference/react-compiler/configuration.md new file mode 100644 index 000000000..f38f1afc0 --- /dev/null +++ b/src/content/reference/react-compiler/configuration.md @@ -0,0 +1,151 @@ +--- +title: Configuration +--- + + + +This page lists all configuration options available in React Compiler. + + + + + +For most apps, the default options should work out of the box. If you have a special need, you can use these advanced options. + + + +```js +// babel.config.js +module.exports = { + plugins: [ + [ + 'babel-plugin-react-compiler', { + // compiler options + } + ] + ] +}; +``` + +--- + +## Compilation Control {/*compilation-control*/} + +These options control *what* the compiler optimizes and *how* it selects components and hooks to compile. + +* [`compilationMode`](/reference/react-compiler/compilationMode) controls the strategy for selecting functions to compile (e.g., all functions, only annotated ones, or intelligent detection). + +```js +{ + compilationMode: 'annotation' // Only compile "use memo" functions +} +``` + +--- + +## Version Compatibility {/*version-compatibility*/} + +React version configuration ensures the compiler generates code compatible with your React version. + +[`target`](/reference/react-compiler/target) specifies which React version you're using (17, 18, or 19). + +```js +// For React 18 projects +{ + target: '18' // Also requires react-compiler-runtime package +} +``` + +--- + +## Error Handling {/*error-handling*/} + +These options control how the compiler responds to code that doesn't follow the [Rules of React](/reference/rules). + +[`panicThreshold`](/reference/react-compiler/panicThreshold) determines whether to fail the build or skip problematic components. + +```js +// Recommended for production +{ + panicThreshold: 'none' // Skip components with errors instead of failing the build +} +``` + +--- + +## Debugging {/*debugging*/} + +Logging and analysis options help you understand what the compiler is doing. + +[`logger`](/reference/react-compiler/logger) provides custom logging for compilation events. + +```js +{ + logger: { + logEvent(filename, event) { + if (event.kind === 'CompileSuccess') { + console.log('Compiled:', filename); + } + } + } +} +``` + +--- + +## Feature Flags {/*feature-flags*/} + +Conditional compilation lets you control when optimized code is used. + +[`gating`](/reference/react-compiler/gating) enables runtime feature flags for A/B testing or gradual rollouts. + +```js +{ + gating: { + source: 'my-feature-flags', + importSpecifierName: 'isCompilerEnabled' + } +} +``` + +--- + +## Common Configuration Patterns {/*common-patterns*/} + +### Default configuration {/*default-configuration*/} + +For most React 19 applications, the compiler works without configuration: + +```js +// babel.config.js +module.exports = { + plugins: [ + 'babel-plugin-react-compiler' + ] +}; +``` + +### React 17/18 projects {/*react-17-18*/} + +Older React versions need the runtime package and target configuration: + +```bash +npm install react-compiler-runtime@rc +``` + +```js +{ + target: '18' // or '17' +} +``` + +### Incremental adoption {/*incremental-adoption*/} + +Start with specific directories and expand gradually: + +```js +{ + compilationMode: 'annotation' // Only compile "use memo" functions +} +``` + diff --git a/src/content/reference/react-compiler/directives.md b/src/content/reference/react-compiler/directives.md new file mode 100644 index 000000000..705d0f620 --- /dev/null +++ b/src/content/reference/react-compiler/directives.md @@ -0,0 +1,198 @@ +--- +title: Directives +--- + + +React Compiler directives are special string literals that control whether specific functions are compiled. + + +```js +function MyComponent() { + "use memo"; // Opt this component into compilation + return
    {/* ... */}
    ; +} +``` + + + +--- + +## Overview {/*overview*/} + +React Compiler directives provide fine-grained control over which functions are optimized by the compiler. They are string literals placed at the beginning of a function body or at the top of a module. + +### Available directives {/*available-directives*/} + +* **[`"use memo"`](/reference/react-compiler/directives/use-memo)** - Opts a function into compilation +* **[`"use no memo"`](/reference/react-compiler/directives/use-no-memo)** - Opts a function out of compilation + +### Quick comparison {/*quick-comparison*/} + +| Directive | Purpose | When to use | +|-----------|---------|-------------| +| [`"use memo"`](/reference/react-compiler/directives/use-memo) | Force compilation | When using `annotation` mode or to override `infer` mode heuristics | +| [`"use no memo"`](/reference/react-compiler/directives/use-no-memo) | Prevent compilation | Debugging issues or working with incompatible code | + +--- + +## Usage {/*usage*/} + +### Function-level directives {/*function-level*/} + +Place directives at the beginning of a function to control its compilation: + +```js +// Opt into compilation +function OptimizedComponent() { + "use memo"; + return
    This will be optimized
    ; +} + +// Opt out of compilation +function UnoptimizedComponent() { + "use no memo"; + return
    This won't be optimized
    ; +} +``` + +### Module-level directives {/*module-level*/} + +Place directives at the top of a file to affect all functions in that module: + +```js +// At the very top of the file +"use memo"; + +// All functions in this file will be compiled +function Component1() { + return
    Compiled
    ; +} + +function Component2() { + return
    Also compiled
    ; +} + +// Can be overridden at function level +function Component3() { + "use no memo"; // This overrides the module directive + return
    Not compiled
    ; +} +``` + +### Compilation modes interaction {/*compilation-modes*/} + +Directives behave differently depending on your [`compilationMode`](/reference/react-compiler/compilationMode): + +* **`annotation` mode**: Only functions with `"use memo"` are compiled +* **`infer` mode**: Compiler decides what to compile, directives override decisions +* **`all` mode**: Everything is compiled, `"use no memo"` can exclude specific functions + +--- + +## Best practices {/*best-practices*/} + +### Use directives sparingly {/*use-sparingly*/} + +Directives are escape hatches. Prefer configuring the compiler at the project level: + +```js +// ✅ Good - project-wide configuration +{ + plugins: [ + ['babel-plugin-react-compiler', { + compilationMode: 'infer' + }] + ] +} + +// ⚠️ Use directives only when needed +function SpecialCase() { + "use no memo"; // Document why this is needed + // ... +} +``` + +### Document directive usage {/*document-usage*/} + +Always explain why a directive is used: + +```js +// ✅ Good - clear explanation +function DataGrid() { + "use no memo"; // TODO: Remove after fixing issue with dynamic row heights (JIRA-123) + // Complex grid implementation +} + +// ❌ Bad - no explanation +function Mystery() { + "use no memo"; + // ... +} +``` + +### Plan for removal {/*plan-removal*/} + +Opt-out directives should be temporary: + +1. Add the directive with a TODO comment +2. Create a tracking issue +3. Fix the underlying problem +4. Remove the directive + +```js +function TemporaryWorkaround() { + "use no memo"; // TODO: Remove after upgrading ThirdPartyLib to v2.0 + return ; +} +``` + +--- + +## Common patterns {/*common-patterns*/} + +### Gradual adoption {/*gradual-adoption*/} + +When adopting the React Compiler in a large codebase: + +```js +// Start with annotation mode +{ + compilationMode: 'annotation' +} + +// Opt in stable components +function StableComponent() { + "use memo"; + // Well-tested component +} + +// Later, switch to infer mode and opt out problematic ones +function ProblematicComponent() { + "use no memo"; // Fix issues before removing + // ... +} +``` + + +--- + +## Troubleshooting {/*troubleshooting*/} + +For specific issues with directives, see the troubleshooting sections in: + +* [`"use memo"` troubleshooting](/reference/react-compiler/directives/use-memo#troubleshooting) +* [`"use no memo"` troubleshooting](/reference/react-compiler/directives/use-no-memo#troubleshooting) + +### Common issues {/*common-issues*/} + +1. **Directive ignored**: Check placement (must be first) and spelling +2. **Compilation still happens**: Check `ignoreUseNoForget` setting +3. **Module directive not working**: Ensure it's before all imports + +--- + +## See also {/*see-also*/} + +* [`compilationMode`](/reference/react-compiler/compilationMode) - Configure how the compiler chooses what to optimize +* [`Configuration`](/reference/react-compiler/configuration) - Full compiler configuration options +* [React Compiler documentation](https://react.dev/learn/react-compiler) - Getting started guide \ No newline at end of file diff --git a/src/content/reference/react-compiler/directives/use-memo.md b/src/content/reference/react-compiler/directives/use-memo.md new file mode 100644 index 000000000..431862682 --- /dev/null +++ b/src/content/reference/react-compiler/directives/use-memo.md @@ -0,0 +1,157 @@ +--- +title: "use memo" +titleForTitleTag: "'use memo' directive" +--- + + + +`"use memo"` marks a function for React Compiler optimization. + + + + + +In most cases, you don't need `"use memo"`. It's primarily needed in `annotation` mode where you must explicitly mark functions for optimization. In `infer` mode, the compiler automatically detects components and hooks by their naming patterns (PascalCase for components, `use` prefix for hooks). If a component or hook isn't being compiled in `infer` mode, you should fix its naming convention rather than forcing compilation with `"use memo"`. + + + + + +--- + +## Reference {/*reference*/} + +### `"use memo"` {/*use-memo*/} + +Add `"use memo"` at the beginning of a function to mark it for React Compiler optimization. + +```js {1} +function MyComponent() { + "use memo"; + // ... +} +``` + +When a function contains `"use memo"`, the React Compiler will analyze and optimize it during build time. The compiler will automatically memoize values and components to prevent unnecessary re-computations and re-renders. + +#### Caveats {/*caveats*/} + +* `"use memo"` must be at the very beginning of a function body, before any imports or other code (comments are OK). +* The directive must be written with double or single quotes, not backticks. +* The directive must exactly match `"use memo"`. +* Only the first directive in a function is processed; additional directives are ignored. +* The effect of the directive depends on your [`compilationMode`](/reference/react-compiler/compilationMode) setting. + +### How `"use memo"` marks functions for optimization {/*how-use-memo-marks*/} + +In a React app that uses the React Compiler, functions are analyzed at build time to determine if they can be optimized. By default, the compiler automatically infers which components to memoize, but this can depend on your [`compilationMode`](/reference/react-compiler/compilationMode) setting if you've set it. + +`"use memo"` explicitly marks a function for optimization, overriding the default behavior: + +* In `annotation` mode: Only functions with `"use memo"` are optimized +* In `infer` mode: The compiler uses heuristics, but `"use memo"` forces optimization +* In `all` mode: Everything is optimized by default, making `"use memo"` redundant + +The directive creates a clear boundary in your codebase between optimized and non-optimized code, giving you fine-grained control over the compilation process. + +### When to use `"use memo"` {/*when-to-use*/} + +You should consider using `"use memo"` when: + +#### You're using annotation mode {/*annotation-mode-use*/} +In `compilationMode: 'annotation'`, the directive is required for any function you want optimized: + +```js +// ✅ This component will be optimized +function OptimizedList() { + "use memo"; + // ... +} + +// ❌ This component won't be optimized +function SimpleWrapper() { + // ... +} +``` + +#### You're gradually adopting React Compiler {/*gradual-adoption*/} +Start with `annotation` mode and selectively optimize stable components: + +```js +// Start by optimizing leaf components +function Button({ onClick, children }) { + "use memo"; + // ... +} + +// Gradually move up the tree as you verify behavior +function ButtonGroup({ buttons }) { + "use memo"; + // ... +} +``` + +--- + +## Usage {/*usage*/} + +### Working with different compilation modes {/*compilation-modes*/} + +The behavior of `"use memo"` changes based on your compiler configuration: + +```js +// babel.config.js +module.exports = { + plugins: [ + ['babel-plugin-react-compiler', { + compilationMode: 'annotation' // or 'infer' or 'all' + }] + ] +}; +``` + +#### Annotation mode {/*annotation-mode-example*/} +```js +// ✅ Optimized with "use memo" +function ProductCard({ product }) { + "use memo"; + // ... +} + +// ❌ Not optimized (no directive) +function ProductList({ products }) { + // ... +} +``` + +#### Infer mode (default) {/*infer-mode-example*/} +```js +// Automatically memoized because this is named like a Component +function ComplexDashboard({ data }) { + // ... +} + +// Skipped: Is not named like a Component +function simpleDisplay({ text }) { + // ... +} +``` + +In `infer` mode, the compiler automatically detects components and hooks by their naming patterns (PascalCase for components, `use` prefix for hooks). If a component or hook isn't being compiled in `infer` mode, you should fix its naming convention rather than forcing compilation with `"use memo"`. + +--- + +## Troubleshooting {/*troubleshooting*/} + +### Verifying optimization {/*verifying-optimization*/} + +To confirm your component is being optimized: + +1. Check the compiled output in your build +2. Use React DevTools to check for Memo ✨ badge + +### See also {/*see-also*/} + +* [`"use no memo"`](/reference/react-compiler/directives/use-no-memo) - Opt out of compilation +* [`compilationMode`](/reference/react-compiler/compilationMode) - Configure compilation behavior +* [React Compiler](/learn/react-compiler) - Getting started guide \ No newline at end of file diff --git a/src/content/reference/react-compiler/directives/use-no-memo.md b/src/content/reference/react-compiler/directives/use-no-memo.md new file mode 100644 index 000000000..e6c419bc6 --- /dev/null +++ b/src/content/reference/react-compiler/directives/use-no-memo.md @@ -0,0 +1,147 @@ +--- +title: "use no memo" +titleForTitleTag: "'use no memo' directive" +--- + + + +`"use no memo"` prevents a function from being optimized by React Compiler. + + + + + +--- + +## Reference {/*reference*/} + +### `"use no memo"` {/*use-no-memo*/} + +Add `"use no memo"` at the beginning of a function to prevent React Compiler optimization. + +```js {1} +function MyComponent() { + "use no memo"; + // ... +} +``` + +When a function contains `"use no memo"`, the React Compiler will skip it entirely during optimization. This is useful as a temporary escape hatch when debugging or when dealing with code that doesn't work correctly with the compiler. + +#### Caveats {/*caveats*/} + +* `"use no memo"` must be at the very beginning of a function body, before any imports or other code (comments are OK). +* The directive must be written with double or single quotes, not backticks. +* The directive must exactly match `"use no memo"` or its alias `"use no forget"`. +* This directive takes precedence over all compilation modes and other directives. +* It's intended as a temporary debugging tool, not a permanent solution. + +### How `"use no memo"` opts-out of optimization {/*how-use-no-memo-opts-out*/} + +React Compiler analyzes your code at build time to apply optimizations. `"use no memo"` creates an explicit boundary that tells the compiler to skip a function entirely. + +This directive takes precedence over all other settings: +* In `all` mode: The function is skipped despite the global setting +* In `infer` mode: The function is skipped even if heuristics would optimize it + +The compiler treats these functions as if the React Compiler wasn't enabled, leaving them exactly as written. + +### When to use `"use no memo"` {/*when-to-use*/} + +`"use no memo"` should be used sparingly and temporarily. Common scenarios include: + +#### Debugging compiler issues {/*debugging-compiler*/} +When you suspect the compiler is causing issues, temporarily disable optimization to isolate the problem: + +```js +function ProblematicComponent({ data }) { + "use no memo"; // TODO: Remove after fixing issue #123 + + // Rules of React violations that weren't statically detected + // ... +} +``` + +#### Third-party library integration {/*third-party*/} +When integrating with libraries that might not be compatible with the compiler: + +```js +function ThirdPartyWrapper() { + "use no memo"; + + useThirdPartyHook(); // Has side effects that compiler might optimize incorrectly + // ... +} +``` + +--- + +## Usage {/*usage*/} + +The `"use no memo"` directive is placed at the beginning of a function body to prevent React Compiler from optimizing that function: + +```js +function MyComponent() { + "use no memo"; + // Function body +} +``` + +The directive can also be placed at the top of a file to affect all functions in that module: + +```js +"use no memo"; + +// All functions in this file will be skipped by the compiler +``` + +`"use no memo"` at the function level overrides the module level directive. + +--- + +## Troubleshooting {/*troubleshooting*/} + +### Directive not preventing compilation {/*not-preventing*/} + +If `"use no memo"` isn't working: + +```js +// ❌ Wrong - directive after code +function Component() { + const data = getData(); + "use no memo"; // Too late! +} + +// ✅ Correct - directive first +function Component() { + "use no memo"; + const data = getData(); +} +``` + +Also check: +* Spelling - must be exactly `"use no memo"` +* Quotes - must use single or double quotes, not backticks + +### Best practices {/*best-practices*/} + +**Always document why** you're disabling optimization: + +```js +// ✅ Good - clear explanation and tracking +function DataProcessor() { + "use no memo"; // TODO: Remove after fixing rule of react violation + // ... +} + +// ❌ Bad - no explanation +function Mystery() { + "use no memo"; + // ... +} +``` + +### See also {/*see-also*/} + +* [`"use memo"`](/reference/react-compiler/directives/use-memo) - Opt into compilation +* [React Compiler](/learn/react-compiler) - Getting started guide \ No newline at end of file diff --git a/src/content/reference/react-compiler/gating.md b/src/content/reference/react-compiler/gating.md new file mode 100644 index 000000000..479506af3 --- /dev/null +++ b/src/content/reference/react-compiler/gating.md @@ -0,0 +1,141 @@ +--- +title: gating +--- + + + +The `gating` option enables conditional compilation, allowing you to control when optimized code is used at runtime. + + + +```js +{ + gating: { + source: 'my-feature-flags', + importSpecifierName: 'shouldUseCompiler' + } +} +``` + + + +--- + +## Reference {/*reference*/} + +### `gating` {/*gating*/} + +Configures runtime feature flag gating for compiled functions. + +#### Type {/*type*/} + +``` +{ + source: string; + importSpecifierName: string; +} | null +``` + +#### Default value {/*default-value*/} + +`null` + +#### Properties {/*properties*/} + +- **`source`**: Module path to import the feature flag from +- **`importSpecifierName`**: Name of the exported function to import + +#### Caveats {/*caveats*/} + +- The gating function must return a boolean +- Both compiled and original versions increase bundle size +- The import is added to every file with compiled functions + +--- + +## Usage {/*usage*/} + +### Basic feature flag setup {/*basic-setup*/} + +1. Create a feature flag module: + +```js +// src/utils/feature-flags.js +export function shouldUseCompiler() { + // your logic here + return getFeatureFlag('react-compiler-enabled'); +} +``` + +2. Configure the compiler: + +```js +{ + gating: { + source: './src/utils/feature-flags', + importSpecifierName: 'shouldUseCompiler' + } +} +``` + +3. The compiler generates gated code: + +```js +// Input +function Button(props) { + return ; +} + +// Output (simplified) +import { shouldUseCompiler } from './src/utils/feature-flags'; + +const Button = shouldUseCompiler() + ? function Button_optimized(props) { /* compiled version */ } + : function Button_original(props) { /* original version */ }; +``` + +Note that the gating function is evaluated once at module time, so once the JS bundle has been parsed and evaluated the choice of component stays static for the rest of the browser session. + +--- + +## Troubleshooting {/*troubleshooting*/} + +### Feature flag not working {/*flag-not-working*/} + +Verify your flag module exports the correct function: + +```js +// ❌ Wrong: Default export +export default function shouldUseCompiler() { + return true; +} + +// ✅ Correct: Named export matching importSpecifierName +export function shouldUseCompiler() { + return true; +} +``` + +### Import errors {/*import-errors*/} + +Ensure the source path is correct: + +```js +// ❌ Wrong: Relative to babel.config.js +{ + source: './src/flags', + importSpecifierName: 'flag' +} + +// ✅ Correct: Module resolution path +{ + source: '@myapp/feature-flags', + importSpecifierName: 'flag' +} + +// ✅ Also correct: Absolute path from project root +{ + source: './src/utils/flags', + importSpecifierName: 'flag' +} +``` diff --git a/src/content/reference/react-compiler/logger.md b/src/content/reference/react-compiler/logger.md new file mode 100644 index 000000000..41e2a1da0 --- /dev/null +++ b/src/content/reference/react-compiler/logger.md @@ -0,0 +1,118 @@ +--- +title: logger +--- + + + +The `logger` option provides custom logging for React Compiler events during compilation. + + + +```js +{ + logger: { + logEvent(filename, event) { + console.log(`[Compiler] ${event.kind}: ${filename}`); + } + } +} +``` + + + +--- + +## Reference {/*reference*/} + +### `logger` {/*logger*/} + +Configures custom logging to track compiler behavior and debug issues. + +#### Type {/*type*/} + +``` +{ + logEvent: (filename: string | null, event: LoggerEvent) => void; +} | null +``` + +#### Default value {/*default-value*/} + +`null` + +#### Methods {/*methods*/} + +- **`logEvent`**: Called for each compiler event with the filename and event details + +#### Event types {/*event-types*/} + +- **`CompileSuccess`**: Function successfully compiled +- **`CompileError`**: Function skipped due to errors +- **`CompileDiagnostic`**: Non-fatal diagnostic information +- **`CompileSkip`**: Function skipped for other reasons +- **`PipelineError`**: Unexpected compilation error +- **`Timing`**: Performance timing information + +#### Caveats {/*caveats*/} + +- Event structure may change between versions +- Large codebases generate many log entries + +--- + +## Usage {/*usage*/} + +### Basic logging {/*basic-logging*/} + +Track compilation success and failures: + +```js +{ + logger: { + logEvent(filename, event) { + switch (event.kind) { + case 'CompileSuccess': { + console.log(`✅ Compiled: ${filename}`); + break; + } + case 'CompileError': { + console.log(`❌ Skipped: ${filename}`); + break; + } + default: {} + } + } + } +} +``` + +### Detailed error logging {/*detailed-error-logging*/} + +Get specific information about compilation failures: + +```js +{ + logger: { + logEvent(filename, event) { + if (event.kind === 'CompileError') { + console.error(`\nCompilation failed: ${filename}`); + console.error(`Reason: ${event.detail.reason}`); + + if (event.detail.description) { + console.error(`Details: ${event.detail.description}`); + } + + if (event.detail.loc) { + const { line, column } = event.detail.loc.start; + console.error(`Location: Line ${line}, Column ${column}`); + } + + if (event.detail.suggestions) { + console.error('Suggestions:', event.detail.suggestions); + } + } + } + } +} +``` + diff --git a/src/content/reference/react-compiler/panicThreshold.md b/src/content/reference/react-compiler/panicThreshold.md new file mode 100644 index 000000000..e20f5c0c5 --- /dev/null +++ b/src/content/reference/react-compiler/panicThreshold.md @@ -0,0 +1,87 @@ +--- +title: panicThreshold +--- + + + +The `panicThreshold` option controls how the React Compiler handles errors during compilation. + + + +```js +{ + panicThreshold: 'none' // Recommended +} +``` + + + +--- + +## Reference {/*reference*/} + +### `panicThreshold` {/*panicthreshold*/} + +Determines whether compilation errors should fail the build or skip optimization. + +#### Type {/*type*/} + +``` +'none' | 'critical_errors' | 'all_errors' +``` + +#### Default value {/*default-value*/} + +`'none'` + +#### Options {/*options*/} + +- **`'none'`** (default, recommended): Skip components that can't be compiled and continue building +- **`'critical_errors'`**: Fail the build only on critical compiler errors +- **`'all_errors'`**: Fail the build on any compiler diagnostic + +#### Caveats {/*caveats*/} + +- Production builds should always use `'none'` +- Build failures prevent your application from building +- The compiler automatically detects and skips problematic code with `'none'` +- Higher thresholds are only useful during development for debugging + +--- + +## Usage {/*usage*/} + +### Production configuration (recommended) {/*production-configuration*/} + +For production builds, always use `'none'`. This is the default value: + +```js +{ + panicThreshold: 'none' +} +``` + +This ensures: +- Your build never fails due to compiler issues +- Components that can't be optimized run normally +- Maximum components get optimized +- Stable production deployments + +### Development debugging {/*development-debugging*/} + +Temporarily use stricter thresholds to find issues: + +```js +const isDevelopment = process.env.NODE_ENV === 'development'; + +{ + panicThreshold: isDevelopment ? 'critical_errors' : 'none', + logger: { + logEvent(filename, event) { + if (isDevelopment && event.kind === 'CompileError') { + // ... + } + } + } +} +``` \ No newline at end of file diff --git a/src/content/reference/react-compiler/target.md b/src/content/reference/react-compiler/target.md new file mode 100644 index 000000000..381748513 --- /dev/null +++ b/src/content/reference/react-compiler/target.md @@ -0,0 +1,148 @@ +--- +title: target +--- + + + +The `target` option specifies which React version the compiler should generate code for. + + + +```js +{ + target: '19' // or '18', '17' +} +``` + + + +--- + +## Reference {/*reference*/} + +### `target` {/*target*/} + +Configures the React version compatibility for the compiled output. + +#### Type {/*type*/} + +``` +'17' | '18' | '19' +``` + +#### Default value {/*default-value*/} + +`'19'` + +#### Valid values {/*valid-values*/} + +- **`'19'`**: Target React 19 (default). No additional runtime required. +- **`'18'`**: Target React 18. Requires `react-compiler-runtime` package. +- **`'17'`**: Target React 17. Requires `react-compiler-runtime` package. + +#### Caveats {/*caveats*/} + +- Always use string values, not numbers (e.g., `'17'` not `17`) +- Don't include patch versions (e.g., use `'18'` not `'18.2.0'`) +- React 19 includes built-in compiler runtime APIs +- React 17 and 18 require installing `react-compiler-runtime@rc` + +--- + +## Usage {/*usage*/} + +### Targeting React 19 (default) {/*targeting-react-19*/} + +For React 19, no special configuration is needed: + +```js +{ + // defaults to target: '19' +} +``` + +The compiler will use React 19's built-in runtime APIs: + +```js +// Compiled output uses React 19's native APIs +import { c as _c } from 'react/compiler-runtime'; +``` + +### Targeting React 17 or 18 {/*targeting-react-17-or-18*/} + +For React 17 and React 18 projects, you need two steps: + +1. Install the runtime package: + +```bash +npm install react-compiler-runtime@rc +``` + +2. Configure the target: + +```js +// For React 18 +{ + target: '18' +} + +// For React 17 +{ + target: '17' +} +``` + +The compiler will use the polyfill runtime for both versions: + +```js +// Compiled output uses the polyfill +import { c as _c } from 'react-compiler-runtime'; +``` + +--- + +## Troubleshooting {/*troubleshooting*/} + +### Runtime errors about missing compiler runtime {/*missing-runtime*/} + +If you see errors like "Cannot find module 'react/compiler-runtime'": + +1. Check your React version: + ```bash + npm why react + ``` + +2. If using React 17 or 18, install the runtime: + ```bash + npm install react-compiler-runtime@rc + ``` + +3. Ensure your target matches your React version: + ```js + { + target: '18' // Must match your React major version + } + ``` + +### Runtime package not working {/*runtime-not-working*/} + +Ensure the runtime package is: + +1. Installed in your project (not globally) +2. Listed in your `package.json` dependencies +3. The correct version (`@rc` tag) +4. Not in `devDependencies` (it's needed at runtime) + +### Checking compiled output {/*checking-output*/} + +To verify the correct runtime is being used, note the different import (`react/compiler-runtime` for builtin, `react-compiler-runtime` standalone package for 17/18): + +```js +// For React 19 (built-in runtime) +import { c } from 'react/compiler-runtime' +// ^ + +// For React 17/18 (polyfill runtime) +import { c } from 'react-compiler-runtime' +// ^ +``` \ No newline at end of file diff --git a/src/content/reference/react-dom/client/createRoot.md b/src/content/reference/react-dom/client/createRoot.md index e57dcbb31..1c1ad21a4 100644 --- a/src/content/reference/react-dom/client/createRoot.md +++ b/src/content/reference/react-dom/client/createRoot.md @@ -207,7 +207,11 @@ HTML이 비어있으면, 앱의 자바스크립트 코드가 로드되고 실행
    ``` +<<<<<<< HEAD 이것은 매우 느리게 느껴질 수 있습니다! 이 문제를 해결하기 위해 [서버에서 또는 빌드 중에](/reference/react-dom/server) 컴포넌트로부터 초기 HTML을 생성할 수 있습니다. 그러면 방문자는 자바스크립트 코드가 로드되기 전에 텍스트를 읽고, 이미지를 보고, 링크를 클릭할 수 있습니다. 이 최적화를 기본적으로 수행하는 [프레임워크를 사용](/learn/start-a-new-react-project#production-grade-react-frameworks)하는 것이 좋습니다. 실행 시점에 따라 이를 *서버 측 렌더링SSR* 또는 *정적 사이트 생성SSG* 이라고 합니다. +======= +This can feel very slow! To solve this, you can generate the initial HTML from your components [on the server or during the build.](/reference/react-dom/server) Then your visitors can read text, see images, and click links before any of the JavaScript code loads. We recommend [using a framework](/learn/start-a-new-react-project#full-stack-frameworks) that does this optimization out of the box. Depending on when it runs, this is called *server-side rendering (SSR)* or *static site generation (SSG).* +>>>>>>> e9a7cb1b6ca1659b42d81555ecef0cd554b7a983 diff --git a/src/content/reference/react-dom/client/index.md b/src/content/reference/react-dom/client/index.md index 16bbf609a..5c3ce207c 100644 --- a/src/content/reference/react-dom/client/index.md +++ b/src/content/reference/react-dom/client/index.md @@ -4,7 +4,12 @@ title: Client React DOM APIs +<<<<<<< HEAD `react-dom/client` API를 사용하면 클라이언트(브라우저)에서 React 컴포넌트를 렌더링 할 수 있습니다. 이러한 API는 일반적으로 앱의 최상위 수준에서 React 트리를 초기화하기 위해 사용합니다. [프레임워크](/learn/start-a-new-react-project#production-grade-react-frameworks)가 대신 호출할 수도 있습니다. 대부분의 컴포넌트는 이를 가져오거나 사용할 필요가 없습니다. +======= +The `react-dom/client` APIs let you render React components on the client (in the browser). These APIs are typically used at the top level of your app to initialize your React tree. A [framework](/learn/start-a-new-react-project#full-stack-frameworks) may call them for you. Most of your components don't need to import or use them. + +>>>>>>> e9a7cb1b6ca1659b42d81555ecef0cd554b7a983 --- diff --git a/src/content/reference/react-dom/components/common.md b/src/content/reference/react-dom/components/common.md index 3f0c95544..7e004c235 100644 --- a/src/content/reference/react-dom/components/common.md +++ b/src/content/reference/react-dom/components/common.md @@ -917,7 +917,11 @@ export default function Form() { +<<<<<<< HEAD [ref로 DOM 조작하기](/learn/manipulating-the-dom-with-refs) 및 [더 많은 예시](/reference/react/useRef#examples-dom)에 대해 더 자세히 읽어보세요. +======= +Read more about [manipulating DOM with refs](/learn/manipulating-the-dom-with-refs) and [check out more examples.](/reference/react/useRef#usage) +>>>>>>> e9a7cb1b6ca1659b42d81555ecef0cd554b7a983 고급 사용 사례의 경우 `ref` 어트리뷰트는 [콜백 함수](#ref-callback)도 허용합니다. diff --git a/src/content/reference/react-dom/components/form.md b/src/content/reference/react-dom/components/form.md index ab61de9f8..986b962eb 100644 --- a/src/content/reference/react-dom/components/form.md +++ b/src/content/reference/react-dom/components/form.md @@ -36,7 +36,11 @@ title: "
    " #### Props {/*props*/} +<<<<<<< HEAD ``은 모든 [공통 엘리먼트 Props](/reference/react-dom/components/common#props)를 지원합니다. +======= +`` supports all [common element props.](/reference/react-dom/components/common#common-props) +>>>>>>> e9a7cb1b6ca1659b42d81555ecef0cd554b7a983 [`action`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#action): URL 혹은 함수. URL을 `action`을 통해 전달하면, 폼은 HTML 폼 컴포넌트처럼 동작합니다. 함수를 `action`을 통해 전달하면, 해당 함수는 폼 제출을 처리합니다. `action`을 통한 함수는 비동기로 동작할 수 있으며, 폼을 통해 제출된 [`formData`](https://developer.mozilla.org/ko/docs/Web/API/FormData)를 포함한 단일 인수로 호출됩니다. `action`의 프로퍼티는 `formAction`의 속성인 `