diff --git a/package.json b/package.json index 6d6b53f92..c1cd16741 100644 --- a/package.json +++ b/package.json @@ -15,12 +15,13 @@ "prettier:diff": "yarn nit:source", "lint-heading-ids": "node scripts/headingIdLinter.js", "fix-headings": "node scripts/headingIdLinter.js --fix", - "ci-check": "npm-run-all prettier:diff --parallel lint tsc lint-heading-ids rss", + "ci-check": "npm-run-all prettier:diff --parallel lint tsc lint-heading-ids rss deadlinks", "tsc": "tsc --noEmit", "start": "next start", "postinstall": "is-ci || husky install .husky", "check-all": "npm-run-all prettier lint:fix tsc rss", - "rss": "node scripts/generateRss.js" + "rss": "node scripts/generateRss.js", + "deadlinks": "node scripts/deadLinkChecker.js" }, "dependencies": { "@codesandbox/sandpack-react": "2.13.5", @@ -30,7 +31,6 @@ "@radix-ui/react-context-menu": "^2.1.5", "body-scroll-lock": "^3.1.3", "classnames": "^2.2.6", - "date-fns": "^2.16.1", "debounce": "^1.2.1", "github-slugger": "^1.3.0", "next": "15.1.0", @@ -62,6 +62,7 @@ "autoprefixer": "^10.4.2", "babel-eslint": "10.x", "babel-plugin-react-compiler": "19.0.0-beta-e552027-20250112", + "chalk": "4.1.2", "eslint": "7.x", "eslint-config-next": "12.0.3", "eslint-config-react-app": "^5.2.1", diff --git a/scripts/deadLinkChecker.js b/scripts/deadLinkChecker.js new file mode 100644 index 000000000..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/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 ffe761624..9ec330e6b 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,7 @@ Activity is still under research and our remaining work is to finalize the primi In addition to this update, our team has presented at conferences and made appearances on podcasts to speak more on our work and answer questions. -- [Sathya Gunasekaran](/community/team#sathya-gunasekaran) spoke about the React Compiler at the [React India](https://www.youtube.com/watch?v=kjOacmVsLSE) conference +- [Sathya Gunasekaran](https://github.com/gsathya) spoke about the React Compiler at the [React India](https://www.youtube.com/watch?v=kjOacmVsLSE) conference - [Dan Abramov](/community/team#dan-abramov) gave a talk at [RemixConf](https://www.youtube.com/watch?v=zMf_xeGPn6s) titled “React from Another Dimension” which explores an alternative history of how React Server Components and Actions could have been created 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 58e6b24aa..cd49b6eb2 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 e4bb25a4a..0f54d02b4 100644 --- a/src/content/blog/2025/04/23/react-labs-view-transitions-activity-and-more.md +++ b/src/content/blog/2025/04/23/react-labs-view-transitions-activity-and-more.md @@ -2495,7 +2495,7 @@ For example, we can slow down the `default` cross fade animation: ``` -And define `slow-fade` in CSS using [view transition classes](/reference/react/ViewTransition#view-transition-classes): +And define `slow-fade` in CSS using [view transition classes](/reference/react/ViewTransition#view-transition-class): ```css ::view-transition-old(.slow-fade) { diff --git a/src/content/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 2627f6b6b..5de2c1649 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 @@ Inilah cara kami menyarankan untuk menyiapkannya: 2. **Tentukan `/some-app` sebagai *base path*** dalam konfigurasi *framework* Anda (begini caranya: [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. **Konfigurasi server Anda atau Proxy** sehingga semua permintaan di rute `/some-app/` ditangani oleh aplikasi React Anda. +<<<<<<< HEAD Ini memastikan bagian React dari aplikasi Anda bisa mendapatkan [keuntungan dari praktik terbaik](/learn/start-a-new-react-project#can-i-use-react-without-a-framework) yang dimasukkan ke dalam *framework* tersebut. +======= +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. +>>>>>>> e07ac94bc2c1ffd817b13930977be93325e5bea9 Banyak *framework* berbasis React bersifat *full-stack* dan membiarkan aplikasi React Anda memanfaatkan server. Namun, Anda dapat menggunakan pendekatan yang sama meskipun Anda tidak dapat atau tidak ingin menjalankan JavaScript di server. Dalam hal ini, sajikan HTML/CSS/JS ([`next export` output](https://nextjs.org/docs/advanced-features/static-html-export) untuk Next.js, *default* untuk Gatsby) di `/some-app/` sebagai gantinya. 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/scaling-up-with-reducer-and-context.md b/src/content/learn/scaling-up-with-reducer-and-context.md index df04abf96..0558a037a 100644 --- a/src/content/learn/scaling-up-with-reducer-and-context.md +++ b/src/content/learn/scaling-up-with-reducer-and-context.md @@ -685,7 +685,11 @@ Sekarang Anda tidak perlu lagi meneruskan daftar tugas atau *event handler* ke b ``` +<<<<<<< HEAD Sebaliknya, komponen mana pun yang memerlukan daftar tugas dapat membacanya dari `TaskContext`: +======= +Instead, any component that needs the task list can read it from the `TasksContext`: +>>>>>>> e07ac94bc2c1ffd817b13930977be93325e5bea9 ```js {2} export default function TaskList() { diff --git a/src/content/learn/synchronizing-with-effects.md b/src/content/learn/synchronizing-with-effects.md index 7e0e6ebc4..3b0488a91 100644 --- a/src/content/learn/synchronizing-with-effects.md +++ b/src/content/learn/synchronizing-with-effects.md @@ -734,8 +734,13 @@ Menulis panggilan `fetch` di dalam *Effects* adalah [cara populer untuk mengambi Daftar kelemahan ini tidak spesifik untuk React. Ini berlaku untuk mengambil data saat pemasangan komponen dengan pustaka apa pun. Seperti halnya dengan *routing*, pengambilan data bukanlah hal yang sepele untuk dilakukan dengan baik, jadi kami merekomendasikan pendekatan berikut ini: +<<<<<<< HEAD - **Jika Anda menggunakan [kerangka kerja (*framework*)](/learn/start-a-new-react-project#production-grade-react-frameworks), gunakan mekanisme pengambilan data yang sudah ada di dalamnya.** Kerangka kerja React modern memiliki mekanisme pengambilan data terintegrasi yang efisien dan tidak mengalami kendala di atas. - **Jika tidak, pertimbangkan untuk menggunakan atau membangun *cache* sisi klien.** Solusi sumber terbuka (*open source*) yang populer termasuk [React Query](https://tanstack.com/query/latest), [useSWR](https://swr.vercel.app/), dan [React Router 6.4+.](https://beta.reactrouter.com/en/main/start/overview) Anda juga dapat membuat solusi sendiri, dalam hal ini Anda dapat menggunakan *Effects* di dalamnya, tetapi menambahkan logika untuk menduplikasi *request*, menyimpan respons dalam *cache*, dan menghindari *waterfall* jaringan (dengan melakukan pramuat data atau mengangkat kebutuhan data ke *route*). +======= +- **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). +>>>>>>> e07ac94bc2c1ffd817b13930977be93325e5bea9 Anda dapat terus mengambil data secara langsung di *Effects* jika tidak ada satu pun dari pendekatan ini yang cocok untuk Anda. diff --git a/src/content/learn/typescript.md b/src/content/learn/typescript.md index e846b6cc2..bd24b2a5f 100644 --- a/src/content/learn/typescript.md +++ b/src/content/learn/typescript.md @@ -11,16 +11,27 @@ TypeScript adalah salah satu cara populer untuk menambahkan definisi *type* ke d +<<<<<<< HEAD * [TypeScript dengan Komponen React](/learn/typescript#typescript-with-react-components) * [Contoh menambahkan *type* dalam Hooks](/learn/typescript#example-hooks) * [*Types* umum dari `@types/react`](/learn/typescript/#useful-types) * [Tempat pembelajaran lebih lanjut](/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) +>>>>>>> e07ac94bc2c1ffd817b13930977be93325e5bea9 ## Pemasangan {/*installation*/} +<<<<<<< HEAD Semua [kerangka kerja React tingkat produksi](/learn/start-a-new-react-project#production-grade-react-frameworks) menawarkan dukungan untuk menggunakan TypeScript. Ikuti panduan khusus kerangka kerja tersebut untuk pemasangan: +======= +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: +>>>>>>> e07ac94bc2c1ffd817b13930977be93325e5bea9 - [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; ## Contoh Hooks {/*example-hooks*/} +<<<<<<< HEAD Definisi *type* dari `@types/react` menyertakan *type* untuk Hooks bawaan, sehingga Anda dapat menggunakannya dalam komponen tanpa pengaturan tambahan apa pun. Definisi tersebut dibuat untuk memperhitungkan kode yang Anda tulis dalam komponen, sehingga Anda akan memperoleh [*type* yang disimpulkan](https://www.typescriptlang.org/docs/handbook/type-inference.html) sering kali dan idealnya tidak perlu menangani hal-hal sepele dalam menyediakan *type* sendiri. +======= +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. +>>>>>>> e07ac94bc2c1ffd817b13930977be93325e5bea9 Namun, kita dapat melihat beberapa contoh tentang cara menyediakan *type* untuk Hooks. @@ -139,8 +154,13 @@ const [enabled, setEnabled] = useState(false); Ini akan menetapkan *type* `boolean` ke `enabled`, dan `setEnabled` akan menjadi fungsi yang menerima argumen `boolean`, atau fungsi yang mengembalikan `boolean`. Jika Anda ingin secara eksplisit memberikan *type* untuk *state*, Anda dapat melakukannya dengan memberikan argumen *type* ke panggilan `useState`: +<<<<<<< HEAD ```ts // Menyetel type ke "boolean" secara eksplisit +======= +```ts +// Explicitly set the type to "boolean" +>>>>>>> e07ac94bc2c1ffd817b13930977be93325e5bea9 const [enabled, setEnabled] = useState(false); ``` @@ -174,7 +194,7 @@ const [requestState, setRequestState] = useState({ status: 'idle' import {useReducer} from 'react'; interface State { - count: number + count: number }; type CounterAction = @@ -284,7 +304,11 @@ export default App = AppTSX; +<<<<<<< HEAD Teknik ini berfungsi saat Anda memiliki nilai bawaan yang masuk akal - tetapi terkadang ada kasus saat Anda tidak memilikinya, dan dalam kasus tersebut `null` dapat terasa masuk akal sebagai nilai bawaan. Namun, untuk memungkinkan sistem *type* memahami kode Anda, Anda perlu secara eksplisit menetapkan `ContextShape | null` pada `createContext`. +======= +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`. +>>>>>>> e07ac94bc2c1ffd817b13930977be93325e5bea9 Hal ini menyebabkan masalah yang mengharuskan Anda menghilangkan `| ​​null` dalam *type* untuk *consumer* *Context*. Rekomendasi kami adalah agar Hook melakukan pemeriksaan *runtime* untuk keberadaannya dan memunculkan kesalahan saat tidak ada: @@ -329,7 +353,17 @@ function MyComponent() { ### `useMemo` {/*typing-usememo*/} +<<<<<<< HEAD Hook [`useMemo`](/reference/react/useMemo) akan membuat/mengakses ulang nilai yang tersimpan dari pemanggilan fungsi, dan menjalankan ulang fungsi hanya saat dependensi yang diteruskan sebagai parameter ke-2 diubah. Hasil pemanggilan Hook disimpulkan dari nilai yang dikembalikan dari fungsi di parameter pertama. Anda dapat lebih eksplisit dengan memberikan argumen *type* ke 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. +>>>>>>> e07ac94bc2c1ffd817b13930977be93325e5bea9 ```ts // Type dari visibleTodos disimpulkan dari nilai kembalian filterTodos @@ -339,7 +373,17 @@ const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]); ### `useCallback` {/*typing-usecallback*/} +<<<<<<< HEAD [`useCallback`](/reference/react/useCallback) menyediakan referensi stabil ke suatu fungsi selama dependensi yang dimasukkan ke parameter kedua sama. Seperti `useMemo`, *type* fungsi disimpulkan dari nilai kembalian fungsi di parameter pertama, dan Anda dapat lebih eksplisit dengan memberikan argumen *type* ke 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. +>>>>>>> e07ac94bc2c1ffd817b13930977be93325e5bea9 ```ts @@ -350,7 +394,11 @@ const handleClick = useCallback(() => { Saat bekerja dalam mode *strict* TypeScript, `useCallback` mengharuskan penambahan *type* untuk parameter dalam *callback* Anda. Hal ini karena *type* dari *callback* disimpulkan dari nilai pengembalian fungsi, dan tanpa parameter, *type* tersebut tidak dapat dipahami sepenuhnya. +<<<<<<< HEAD Bergantung pada preferensi gaya kode Anda, Anda dapat menggunakan fungsi `*EventHandler` dari *type* React untuk menyediakan *type* bagi *event handler* pada saat yang sama saat mendefinisikan *callback*: +======= +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: +>>>>>>> e07ac94bc2c1ffd817b13930977be93325e5bea9 ```ts import { useState, useCallback } from 'react'; @@ -361,7 +409,7 @@ export default function Form() { const handleChange = useCallback>((event) => { setValue(event.currentTarget.value); }, [setValue]) - + return ( <> @@ -433,7 +481,11 @@ interface ModalRendererProps { } ``` +<<<<<<< HEAD Perlu diingat, bahwa Anda tidak dapat menggunakan TypeScript untuk menjelaskan bahwa anak dari komponen adalah tipe elemen JSX tertentu, jadi Anda tidak dapat menggunakan sistem *type* untuk menjelaskan komponen yang hanya menerima anak `
  • `. +======= +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. +>>>>>>> e07ac94bc2c1ffd817b13930977be93325e5bea9 Anda dapat melihat contoh `React.ReactNode` dan `React.ReactElement` dengan pemeriksa *type* di [*playground* TypeScript ini](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgIilQ3wChSB6CxYmAOmXRgDkIATJOdNJMGAZzgwAFpxAR+8YADswAVwGkZMJFEzpOjDKw4AFHGEEBvUnDhphwADZsi0gFw0mDWjqQBuUgF9yaCNMlENzgAXjgACjADfkctFnYkfQhDAEpQgD44AB42YAA3dKMo5P46C2tbJGkvLIpcgt9-QLi3AEEwMFCItJDMrPTTbIQ3dKywdIB5aU4kKyQQKpha8drhhIGzLLWODbNs3b3s8YAxKBQAcwXpAThMaGWDvbH0gFloGbmrgQfBzYpd1YjQZbEYARkB6zMwO2SHSAAlZlYIBCdtCRkZpHIrFYahQYQD8UYYFA5EhcfjyGYqHAXnJAsIUHlOOUbHYhMIIHJzsI0Qk4P9SLUBuRqXEXEwAKKfRZcNA8PiCfxWACecAAUgBlAAacFm80W-CU11U6h4TgwUv11yShjgJjMLMqDnN9Dilq+nh8pD8AXgCHdMrCkWisVoAet0R6fXqhWKhjKllZVVxMcavpd4Zg7U6Qaj+2hmdG4zeRF10uu-Aeq0LBfLMEe-V+T2L7zLVu+FBWLdLeq+lc7DYFf39deFVOotMCACNOCh1dq219a+30uC8YWoZsRyuEdjkevR8uvoVMdjyTWt4WiSSydXD4NqZP4AymeZE072ZzuUeZQKheQgA). 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 5342a27ed..5a832c6a5 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 @@ Ada dua kasus umum di mana Anda tidak memerlukan *Effects*: * **Anda tidak memerlukan *Effects* untuk melakukan transformasi data untuk *rendering*.** Sebagai contoh, katakanlah Anda ingin melakukan *filter* terhadap sebuah daftar sebelum menampiklannya. Anda mungkin merasa tergoda untuk menulis *Effect* yang memperbarui variabel *state* ketika daftar berubah. Akan tetapi, hal ini tidak efisien. Ketika Anda memperbarui *state*, React akan memanggil fungsi komponen Anda terlebih dahulu untuk menghitung apa yang seharusnya ada di layar. Kemudian React akan ["*commit*"](/learn/render-and-commit) perubahan ini ke DOM, memperbarui layar. Kemudian React akan menjalankan *Effect* Anda. Jika *Effect* Anda *juga* segera memperbarui state, ini akan mengulang seluruh proses dari awal! Untuk menghindari *render pass* yang tidak perlu, ubah semua data pada tingkat teratas komponen Anda. Kode tersebut akan secara otomatis dijalankan ulang setiap kali *props* atau *state* anda berubah. * **Anda tidak memerlukan Efek untuk menangani *event* dari pengguna.** Sebagai contoh, katakanlah Anda ingin mengirim *request* POST `/api/buy` dan menampilkan notifikasi ketika pengguna membeli produk. Di *event handler* klik tombol Beli, Anda tahu persis apa yang terjadi. Pada saat *Effect* berjalan, Anda tidak tahu *apa* yang dilakukan pengguna (misalnya, tombol mana yang diklik). Inilah sebabnya mengapa Anda biasanya akan menangani *event* pengguna di *event handler* yang sesuai. +<<<<<<< HEAD Anda *memang* membutuhkan Effect untuk [melakukan sinkronisasi](/learn/synchronizing-with-effects#what-are-effects-and-how-are-they-different-from-events) dengan sistem eksternal. dengan sistem eksternal. Sebagai contoh, Anda dapat menulis sebuah *Effect* yang membuat *widget* jQuery tetap tersinkronisasi dengan *state* React. Anda juga dapat mengambil data dengan *Effect*: sebagai contoh, Anda dapat menyinkronkan hasil pencarian dengan kueri pencarian saat ini. Perlu diingat bahwa [kerangka kerja (*framework*)](/learn/start-a-new-react-project#production-grade-react-frameworks) modern menyediakan mekanisme pengambilan data bawaan yang lebih efisien daripada menulis *Effect* secara langsung di dalam komponen Anda. +======= +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. +>>>>>>> e07ac94bc2c1ffd817b13930977be93325e5bea9 Untuk membantu Anda mendapatkan intuisi yang tepat, mari kita lihat beberapa contoh konkret yang umum! @@ -95,6 +99,12 @@ Biasanya, kode ini baik-baik saja! Namun mungkin `getFilteredTodos()` lambat ata Anda dapat melakukan *cache* (atau ["memoisasi"](https://en.wikipedia.org/wiki/Memoization)) perhitungan yang mahal dengan membungkusnya dalam Hook [`useMemo`](/reference/react/useMemo): + + +[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'; @@ -750,7 +760,11 @@ function SearchResults({ query }) { Hal ini memastikan bahwa saat *Effect* Anda mengambil data, semua respons kecuali yang terakhir diminta akan diabaikan. +<<<<<<< HEAD Menangani *race condition* bukan satu-satunya kesulitan dalam mengimplementasikan pengambilan data. Anda mungkin juga ingin memikirkan tentang *cache* terhadap respons (sehingga pengguna dapat mengeklik Kembali dan langsung melihat layar sebelumnya), cara mengambil data di server (sehingga HTML awal yang di-*render* oleh server berisi konten yang diambil, bukan *spinner*), dan cara menghindari air terjun jaringan (*network waterfalls*) (sehingga komponen anak dapat mengambil data tanpa menunggu induknya). +======= +**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.** +>>>>>>> e07ac94bc2c1ffd817b13930977be93325e5bea9 **Masalah ini berlaku untuk semua perpustakaan UI, bukan hanya React. Menyelesaikannya bukanlah hal yang sepele, itulah sebabnya [kerangka kerja](/learn/start-a-new-react-project#production-grade-react-frameworks) modern menyediakan mekanisme pengambilan data bawaan yang lebih efisien daripada mengambil data di *Effect*.** 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 5a00e6c82..9495ccfae 100644 --- a/src/content/reference/react-dom/client/createRoot.md +++ b/src/content/reference/react-dom/client/createRoot.md @@ -86,7 +86,7 @@ React akan menampilkan `` dalam `root`, dan mengambil alih pengelolaan DO * Jika Anda memanggil `render` pada akar yang sama berulang kali, React akan memperbarui DOM sebisa mungkin hingga menyamai JSX terakhir yang Anda berikan. React akan memutuskan bagian mana dari DOM yang dapat digunakan kembali dan bagian mana yang perlu dibuat ulang, dengan melakukan ["pencocokan"](/learn/preserving-and-resetting-state) dengan pohon yang telah di-*render* sebelumnya. Pemanggilan kembali `render` di akar yang sama mirip dengan memanggil [fungsi `set`](/reference/react/useState#setstate) pada komponen akar: React menghindari pembaharuan DOM yang tidak diperlukan. -* Although rendering is synchronous once it starts, `root.render(...)` is not. This means code after `root.render()` may run before any effects (`useLayoutEffect`, `useEffect`) of that specific render are fired. This is usually fine and rarely needs adjustment. In rare cases where effect timing matters, you can wrap `root.render(...)` in [`flushSync`](https://react.dev/reference/react-dom/client/flushSync) to ensure the initial render runs fully synchronously. +* Although rendering is synchronous once it starts, `root.render(...)` is not. This means code after `root.render()` may run before any effects (`useLayoutEffect`, `useEffect`) of that specific render are fired. This is usually fine and rarely needs adjustment. In rare cases where effect timing matters, you can wrap `root.render(...)` in [`flushSync`](https://react.dev/reference/react-dom/flushSync) to ensure the initial render runs fully synchronously. ```js const root = createRoot(document.getElementById('root')); @@ -205,7 +205,11 @@ Saat HTML Anda kosong, pengguna akan melihat sebuah halaman kosong sampai kode J
    ``` +<<<<<<< HEAD Ini dapat terasa sangat lambat! Untuk mengatasi masalah ini, Anda dapat membuat HTML awal dari komponen Anda [pada server atau saat proses *build*](/reference/react-dom/server) Sehingga pengunjung dapat membaca teks, melihat gambar dan mengklik tautan sebelum kode JavaScript apapun selesai dimuat. Kami merekomendasikan untuk [menggunakan sebuah *framework*](/learn/start-a-new-react-project#production-grade-react-frameworks) yang telah melakukan optimisasi ini sejak awal. Bergantung dari kapan proses ini berjalan, ini dapat dipanggil sebagai *server-side rendering (SSR)* atau *static site generation (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).* +>>>>>>> e07ac94bc2c1ffd817b13930977be93325e5bea9 diff --git a/src/content/reference/react-dom/client/index.md b/src/content/reference/react-dom/client/index.md index 3a22c837b..6c8c1d1f4 100644 --- a/src/content/reference/react-dom/client/index.md +++ b/src/content/reference/react-dom/client/index.md @@ -4,7 +4,11 @@ title: API Klien React DOM +<<<<<<< HEAD API `react-dom/client` memungkinkan Anda me-*render* komponen React di sisi klien (di peramban). Biasanya API ini digunakan pada level teratas aplikasi React untuk menginisialisasi pohon React Anda. Sebuah [*framework*](/learn/start-a-new-react-project#production-grade-react-frameworks) mungkin memanggilnya untuk Anda. Biasanya komponen Anda tidak perlu mengimpor atau menggunakannya. +======= +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. +>>>>>>> e07ac94bc2c1ffd817b13930977be93325e5bea9 diff --git a/src/content/reference/react-dom/components/common.md b/src/content/reference/react-dom/components/common.md index f9fb19680..c49cee889 100644 --- a/src/content/reference/react-dom/components/common.md +++ b/src/content/reference/react-dom/components/common.md @@ -920,7 +920,11 @@ export default function Form() { +<<<<<<< HEAD Baca lebih lanjut mengenai [memanipulasi DOM dengan refs](/learn/manipulating-the-dom-with-refs) and [lihat lebih banyak contoh.](/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) +>>>>>>> e07ac94bc2c1ffd817b13930977be93325e5bea9 Untuk kasus yang lebih canggih, attribut `ref` juga menerima sebuah [fungsi *callback*.](#ref-callback) diff --git a/src/content/reference/react-dom/components/form.md b/src/content/reference/react-dom/components/form.md index d7d3f271c..1afd24250 100644 --- a/src/content/reference/react-dom/components/form.md +++ b/src/content/reference/react-dom/components/form.md @@ -36,7 +36,11 @@ Untuk membuat kontrol interaktif untuk mengirimkan informasi, render [komponen ` #### Props {/*props*/} +<<<<<<< HEAD `
    ` mendukung semua [props elemen umum.](/reference/react-dom/components/common#props) +======= +`` supports all [common element props.](/reference/react-dom/components/common#common-props) +>>>>>>> e07ac94bc2c1ffd817b13930977be93325e5bea9 [`action`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#action): URL atau fungsi. Ketika URL diberikan ke `action`, formulir akan berperilaku seperti komponen formulir HTML. Ketika fungsi diberikan ke `action`, fungsi tersebut akan menangani pengiriman formulir. Fungsi yang diberikan ke `action` dapat berupa *async* dan akan dipanggil dengan satu argumen yang berisi [data formulir](https://developer.mozilla.org/en-US/docs/Web/API/FormData) dari formulir yang dikirimkan. Prop `action` dapat ditimpa oleh atribut `formAction` pada komponen `