diff --git a/src/components/Layout/Sidebar/SidebarLink.tsx b/src/components/Layout/Sidebar/SidebarLink.tsx index 5c01fefc3..9650e95fa 100644 --- a/src/components/Layout/Sidebar/SidebarLink.tsx +++ b/src/components/Layout/Sidebar/SidebarLink.tsx @@ -24,7 +24,7 @@ interface SidebarLinkProps { selected?: boolean; title: string; level: number; - version?: 'canary' | 'major' | 'experimental'; + version?: 'canary' | 'major' | 'experimental' | 'rc'; icon?: React.ReactNode; isExpanded?: boolean; hideArrow?: boolean; @@ -102,6 +102,12 @@ export function SidebarLink({ className="ms-1 text-gray-30 dark:text-gray-60 inline-block w-3.5 h-3.5 align-[-3px]" /> )} + {version === 'rc' && ( + + )} {isExpanded != null && !hideArrow && ( diff --git a/src/components/MDX/ExpandableCallout.tsx b/src/components/MDX/ExpandableCallout.tsx index b2115bb4c..9fb49d13c 100644 --- a/src/components/MDX/ExpandableCallout.tsx +++ b/src/components/MDX/ExpandableCallout.tsx @@ -24,6 +24,7 @@ type CalloutVariants = | 'wip' | 'canary' | 'experimental' + | 'rc' | 'major' | 'rsc'; @@ -50,6 +51,15 @@ const variantMap = { overlayGradient: 'linear-gradient(rgba(245, 249, 248, 0), rgba(245, 249, 248, 1)', }, + rc: { + title: 'RC', + Icon: IconCanary, + containerClasses: + 'bg-gray-5 dark:bg-gray-60 dark:bg-opacity-20 text-primary dark:text-primary-dark text-lg', + textColor: 'text-gray-60 dark:text-gray-30', + overlayGradient: + 'linear-gradient(rgba(245, 249, 248, 0), rgba(245, 249, 248, 1)', + }, canary: { title: 'Test Ortamı (Canary)', Icon: IconCanary, diff --git a/src/components/MDX/MDXComponents.tsx b/src/components/MDX/MDXComponents.tsx index fb0948e90..41de40b11 100644 --- a/src/components/MDX/MDXComponents.tsx +++ b/src/components/MDX/MDXComponents.tsx @@ -106,6 +106,10 @@ const Canary = ({children}: {children: React.ReactNode}) => ( {children} ); +const RC = ({children}: {children: React.ReactNode}) => ( + {children} +); + const Experimental = ({children}: {children: React.ReactNode}) => ( {children} ); @@ -533,6 +537,7 @@ export const MDXComponents = { Math, MathI, Note, + RC, Canary, Experimental, ExperimentalBadge, diff --git a/src/components/PageHeading.tsx b/src/components/PageHeading.tsx index 760542750..ee92f5e55 100644 --- a/src/components/PageHeading.tsx +++ b/src/components/PageHeading.tsx @@ -19,7 +19,7 @@ import {IconExperimental} from './Icon/IconExperimental'; interface PageHeadingProps { title: string; - version?: 'experimental' | 'canary'; + version?: 'experimental' | 'canary' | 'rc'; experimental?: boolean; status?: string; description?: string; @@ -46,6 +46,12 @@ function PageHeading({ className="ms-4 mt-1 text-gray-50 dark:text-gray-40 inline-block w-6 h-6 align-[-1px]" /> )} + {version === 'rc' && ( + + )} {version === 'experimental' && ( { }); // compiler inserted dependencies. ``` -With this code, the React Compiler can infer the dependencies for you and insert them automatically so you don't need to see or write them. With features like [the IDE extension](#compiler-ide-extension) and [`useEffectEvent`](/reference/react/experimental_useEffectEvent), we can provide a CodeLens to show you what the Compiler inserted for times you need to debug, or to optimize by removing a dependency. This helps reinforce the correct mental model for writing Effects, which can run at any time to synchronize your component or hook's state with something else. +With this code, the React Compiler can infer the dependencies for you and insert them automatically so you don't need to see or write them. With features like [the IDE extension](#compiler-ide-extension) and [`useEffectEvent`](/reference/react/useEffectEvent), we can provide a CodeLens to show you what the Compiler inserted for times you need to debug, or to optimize by removing a dependency. This helps reinforce the correct mental model for writing Effects, which can run at any time to synchronize your component or hook's state with something else. Our hope is that automatically inserting dependencies is not only easier to write, but that it also makes them easier to understand by forcing you to think in terms of what the Effect does, and not in component lifecycles. diff --git a/src/content/learn/escape-hatches.md b/src/content/learn/escape-hatches.md index 1ab156deb..decbe2768 100644 --- a/src/content/learn/escape-hatches.md +++ b/src/content/learn/escape-hatches.md @@ -455,8 +455,8 @@ Bu ideal değildir. Sohbete yalnızca `roomId` değiştiğinde tekrar bağlanmak ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest", "toastify-js": "1.12.0" }, @@ -471,7 +471,7 @@ Bu ideal değildir. Sohbete yalnızca `roomId` değiştiğinde tekrar bağlanmak ```js import { useState, useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; import { createConnection, sendMessage } from './chat.js'; import { showNotification } from './notifications.js'; diff --git a/src/content/learn/removing-effect-dependencies.md b/src/content/learn/removing-effect-dependencies.md index 2134289ff..e650c20a6 100644 --- a/src/content/learn/removing-effect-dependencies.md +++ b/src/content/learn/removing-effect-dependencies.md @@ -610,11 +610,13 @@ function ChatRoom({ roomId }) { ### Bir değeri, değişikliklerine "tepki vermeden" okumak mı istiyorsunuz? {/*do-you-want-to-read-a-value-without-reacting-to-its-changes*/} - + -Bu bölümde, React'in kararlı bir sürümünde henüz yayınlanmamış **deneysel bir API** açıklanmaktadır. +**`useEffectEvent` API’si şu anda yalnızca React’in Canary ve Experimental kanallarında kullanılabilir.** - +[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels) + + Kullanıcı yeni bir mesaj aldığında `isMuted` değeri `true` olmadığı sürece bir ses çalmak istediğinizi varsayalım: @@ -1262,8 +1264,8 @@ Efektin içinde reaktif olmaması gereken bir kod satırı var mı? Reaktif olma ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest" }, "scripts": { @@ -1277,7 +1279,7 @@ Efektin içinde reaktif olmaması gereken bir kod satırı var mı? Reaktif olma ```js import { useState, useEffect, useRef } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; import { FadeInAnimation } from './animation.js'; function Welcome({ duration }) { @@ -1389,8 +1391,8 @@ Efektinizin `duration` ın en son değerini okuması gerekir, ancak `duration`da ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest" }, "scripts": { @@ -1405,7 +1407,7 @@ Efektinizin `duration` ın en son değerini okuması gerekir, ancak `duration`da ```js import { useState, useEffect, useRef } from 'react'; import { FadeInAnimation } from './animation.js'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; function Welcome({ duration }) { const ref = useRef(null); @@ -1825,8 +1827,8 @@ Bu fonksiyonlardan bir diğeri yalnızca içe aktarılan bir API yöntemine baz ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest", "toastify-js": "1.12.0" }, @@ -1907,7 +1909,7 @@ export default function App() { ```js src/ChatRoom.js active import { useState, useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; export default function ChatRoom({ roomId, createConnection, onMessage }) { useEffect(() => { @@ -2120,8 +2122,8 @@ Sonuç olarak, sohbet yalnızca anlamlı bir şey (`roomId` veya `isEncrypted`) ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest", "toastify-js": "1.12.0" }, @@ -2189,7 +2191,7 @@ export default function App() { ```js src/ChatRoom.js active import { useState, useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; import { createEncryptedConnection, createUnencryptedConnection, diff --git a/src/content/learn/reusing-logic-with-custom-hooks.md b/src/content/learn/reusing-logic-with-custom-hooks.md index 871643589..dc1171998 100644 --- a/src/content/learn/reusing-logic-with-custom-hooks.md +++ b/src/content/learn/reusing-logic-with-custom-hooks.md @@ -837,11 +837,13 @@ export default function ChatRoom({ roomId }) { ### Olay yöneticilerini özel Hook'lara geçirme {/*passing-event-handlers-to-custom-hooks*/} - + -Bu bölüm **henüz kararlı bir sürümde yayınlanmamış olan deneysel bir API'yi** açıklar. +**`useEffectEvent` API'si şu anda yalnızca React'in Canary ve Experimental kanallarında mevcuttur.** - +[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels) + + `useChatRoom`'u daha fazla bileşende kullanmaya başladıkça, bileşenlerin onun davranışını özelleştirmesine izin vermek isteyebilirsiniz. Örneğin, şu anda, bir mesaj geldiğinde ne yapılacağının mantığı Hook'un içine sabit kodlanmış durumda: @@ -985,7 +987,7 @@ export default function ChatRoom({ roomId }) { ```js src/useChatRoom.js import { useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; import { createConnection } from './chat.js'; export function useChatRoom({ serverUrl, roomId, onReceiveMessage }) { @@ -1070,8 +1072,8 @@ export function showNotification(message, theme = 'dark') { ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest", "toastify-js": "1.12.0" }, @@ -1668,7 +1670,7 @@ export default function App() { ```js src/useFadeIn.js active import { useState, useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; export function useFadeIn(ref, duration) { const [isRunning, setIsRunning] = useState(true); @@ -1721,8 +1723,8 @@ html, body { min-height: 300px; } ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest" }, "scripts": { @@ -2216,8 +2218,8 @@ Görünen o ki `useInterval` Hook'unuz argüman olarak bir olay dinleyicisi alı ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest" }, "scripts": { @@ -2260,7 +2262,7 @@ export function useCounter(delay) { ```js src/useInterval.js import { useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; export function useInterval(onTick, delay) { useEffect(() => { @@ -2287,8 +2289,8 @@ Bu değişiklikle birlikte, her iki interval de beklediğiniz gibi çalışır v ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest" }, "scripts": { @@ -2332,7 +2334,7 @@ export function useCounter(delay) { ```js src/useInterval.js active import { useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; export function useInterval(callback, delay) { const onTick = useEffectEvent(callback); diff --git a/src/content/learn/separating-events-from-effects.md b/src/content/learn/separating-events-from-effects.md index ca416ca0e..084cfa9a5 100644 --- a/src/content/learn/separating-events-from-effects.md +++ b/src/content/learn/separating-events-from-effects.md @@ -400,13 +400,15 @@ Bu reaktif olmayan mantığı, etrafındaki reaktif Efektten ayırmak için bir ### Bir Efekt Olayı Bildirme {/*declaring-an-effect-event*/} - + -Bu bölümde, React'in kararlı bir sürümünde henüz yayınlanmamış **deneysel bir API** açıklanmaktadır. +**`useEffectEvent` API'si şu anda yalnızca React'in Canary ve Experimental kanallarında kullanılabilir.** - +[React’in release kanalları hakkında daha fazla bilgi edinin.](/community/versioning-policy#all-release-channels) -Bu reaktif olmayan mantığı Efektinizden çıkarmak için [`useEffectEvent`](/reference/react/experimental_useEffectEvent) adlı özel bir Hook kullanın: + + +Bu non-reactive mantığı Effect’inden çıkarmak için [`useEffectEvent`](/reference/react/useEffectEvent) adında özel bir Hook kullan: ```js {1,4-6} import { useEffect, useEffectEvent } from 'react'; @@ -448,8 +450,8 @@ Yeni davranışın beklediğiniz gibi çalıştığını doğrulayın: ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest", "toastify-js": "1.12.0" }, @@ -464,7 +466,7 @@ Yeni davranışın beklediğiniz gibi çalıştığını doğrulayın: ```js import { useState, useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; import { createConnection, sendMessage } from './chat.js'; import { showNotification } from './notifications.js'; @@ -578,11 +580,13 @@ Efekt olaylarını olay yöneticilerine çok benzer olarak düşünebilirsiniz. ### Efekt olayları ile en son propları ve state okuma {/*reading-latest-props-and-state-with-effect-events*/} - + + +**`useEffectEvent` API'si şu anda yalnızca React'in Canary ve Experimental kanallarında mevcuttur.** -Bu bölümde, React'in kararlı bir sürümünde henüz yayınlanmamış **deneysel bir API** açıklanmaktadır. +[React’in release kanalları hakkında daha fazla bilgi edinin.](/community/versioning-policy#all-release-channels) - + Efekt olayları, bağımlılık bağlayıcısını bastırmak isteyebileceğiniz birçok modeli düzeltmenize olanak tanır. @@ -802,8 +806,8 @@ Orijinal kodun yazarı, Effect'in herhangi bir reaktif değere bağlı olmadığ ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest" }, "scripts": { @@ -817,7 +821,7 @@ Orijinal kodun yazarı, Effect'in herhangi bir reaktif değere bağlı olmadığ ```js import { useState, useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; export default function App() { const [position, setPosition] = useState({ x: 0, y: 0 }); @@ -877,11 +881,13 @@ Linteri bastırmanın diğer doğru alternatifleri için [Efekt Bağımlılıkla ### Efekt Olaylarının Sınırlamaları {/*limitations-of-effect-events*/} - + + +**`useEffectEvent` API'si şu anda yalnızca React'in Canary ve Experimental kanallarında mevcuttur.** -Bu bölümde, React'in kararlı bir sürümünde henüz yayınlanmamış **deneysel bir API** açıklanmaktadır. +[React’in release kanalları hakkında daha fazla bilgi edinin.](/community/versioning-policy#all-release-channels) - + Efekt Olayları, kullanma şekliniz açısından oldukça sınırlıdır: @@ -975,8 +981,8 @@ Bu kodu düzeltmek için kurallara uymak yeterlidir. ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest" }, "scripts": { @@ -1045,8 +1051,8 @@ Suppression yorumunu kaldırırsanız, React size bu Efektin kodunun `increment` ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest" }, "scripts": { @@ -1123,8 +1129,8 @@ Görünüşe göre zamanlayıcıyı kuran Efekt `increment` değerine "tepki" ve ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest" }, "scripts": { @@ -1138,7 +1144,7 @@ Görünüşe göre zamanlayıcıyı kuran Efekt `increment` değerine "tepki" ve ```js import { useState, useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; export default function Timer() { const [count, setCount] = useState(0); @@ -1192,8 +1198,8 @@ Sorunu çözmek için, Efektten bir `onTick` Efekt olayı çıkarın: ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest" }, "scripts": { @@ -1207,7 +1213,7 @@ Sorunu çözmek için, Efektten bir `onTick` Efekt olayı çıkarın: ```js import { useState, useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; export default function Timer() { const [count, setCount] = useState(0); @@ -1273,8 +1279,8 @@ Effect olayları içindeki kod reaktif değildir. `setInterval` çağrısının ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest" }, "scripts": { @@ -1288,7 +1294,7 @@ Effect olayları içindeki kod reaktif değildir. `setInterval` çağrısının ```js import { useState, useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; export default function Timer() { const [count, setCount] = useState(0); @@ -1359,8 +1365,8 @@ Yukarıdaki örnekle ilgili sorun, kodun gerçekte ne yapması gerektiğini dü ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest" }, "scripts": { @@ -1374,7 +1380,7 @@ Yukarıdaki örnekle ilgili sorun, kodun gerçekte ne yapması gerektiğini dü ```js import { useState, useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; export default function Timer() { const [count, setCount] = useState(0); @@ -1455,8 +1461,8 @@ Efektiniz hangi odaya bağlı olduğunu bilir. Efekt Olayınıza aktarmak isteye ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest", "toastify-js": "1.12.0" }, @@ -1471,7 +1477,7 @@ Efektiniz hangi odaya bağlı olduğunu bilir. Efekt Olayınıza aktarmak isteye ```js import { useState, useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; import { createConnection, sendMessage } from './chat.js'; import { showNotification } from './notifications.js'; @@ -1596,8 +1602,8 @@ Sorunu çözmek için, Efekt olayı içinde *en son* `roomId`yi okumak yerine, a ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest", "toastify-js": "1.12.0" }, @@ -1612,7 +1618,7 @@ Sorunu çözmek için, Efekt olayı içinde *en son* `roomId`yi okumak yerine, a ```js import { useState, useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; import { createConnection, sendMessage } from './chat.js'; import { showNotification } from './notifications.js'; @@ -1733,8 +1739,8 @@ Ek zorluğu çözmek için, bildirim zaman aşımı kimliğini kaydedin ve Efekt ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest", "toastify-js": "1.12.0" }, @@ -1749,7 +1755,7 @@ Ek zorluğu çözmek için, bildirim zaman aşımı kimliğini kaydedin ve Efekt ```js import { useState, useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; import { createConnection, sendMessage } from './chat.js'; import { showNotification } from './notifications.js'; diff --git a/src/content/reference/eslint-plugin-react-hooks/index.md b/src/content/reference/eslint-plugin-react-hooks/index.md index 6494bd78f..21e1222d8 100644 --- a/src/content/reference/eslint-plugin-react-hooks/index.md +++ b/src/content/reference/eslint-plugin-react-hooks/index.md @@ -1,5 +1,6 @@ --- title: eslint-plugin-react-hooks +version: rc --- @@ -8,6 +9,14 @@ title: eslint-plugin-react-hooks + + +These docs include rules available in the RC version of `eslint-plugin-react-hooks`. + +You can try them by upgrading the lint plugin [to the most recent RC version](/learn/react-compiler/installation#eslint-integration). + + + This plugin helps you catch violations of React's rules at build time, ensuring your components and hooks follow React's rules for correctness and performance. The lints cover both fundamental React patterns (exhaustive-deps and rules-of-hooks) and issues flagged by React Compiler. React Compiler diagnostics are automatically surfaced by this ESLint plugin, and can be used even if your app hasn't adopted the compiler yet. @@ -18,10 +27,16 @@ What this means for linting, is that you don’t need to fix all violations imme ## Available Lints {/*available-lints*/} -* [`component-hook-factories`](/reference/eslint-plugin-react-hooks/lints/component-hook-factories) - Validates against higher order functions defining nested components or hooks +These rules are available in the stable version of `eslint-plugin-react-hooks`: + +* [`exhaustive-deps`](/reference/eslint-plugin-react-hooks/lints/exhaustive-deps) - Validates that dependency arrays for React hooks contain all necessary dependencies +* [`rules-of-hooks`](/reference/eslint-plugin-react-hooks/lints/rules-of-hooks) - Validates that components and hooks follow the Rules of Hooks + +These rules are available in the RC version of `eslint-plugin-react-hooks`: + +* [`component-hook-factories`](/reference/eslint-plugin-react-hooks/lints/component-hook-factories) - Validates higher order functions defining nested components or hooks * [`config`](/reference/eslint-plugin-react-hooks/lints/config) - Validates the compiler configuration options * [`error-boundaries`](/reference/eslint-plugin-react-hooks/lints/error-boundaries) - Validates usage of Error Boundaries instead of try/catch for child errors -* [`exhaustive-deps`](/reference/eslint-plugin-react-hooks/lints/exhaustive-deps) - Validates that dependency arrays for React hooks contain all necessary dependencies * [`gating`](/reference/eslint-plugin-react-hooks/lints/gating) - Validates configuration of gating mode * [`globals`](/reference/eslint-plugin-react-hooks/lints/globals) - Validates against assignment/mutation of globals during render * [`immutability`](/reference/eslint-plugin-react-hooks/lints/immutability) - Validates against mutating props, state, and other immutable values @@ -29,7 +44,6 @@ What this means for linting, is that you don’t need to fix all violations imme * [`preserve-manual-memoization`](/reference/eslint-plugin-react-hooks/lints/preserve-manual-memoization) - Validates that existing manual memoization is preserved by the compiler * [`purity`](/reference/eslint-plugin-react-hooks/lints/purity) - Validates that components/hooks are pure by checking known-impure functions * [`refs`](/reference/eslint-plugin-react-hooks/lints/refs) - Validates correct usage of refs, not reading/writing during render -* [`rules-of-hooks`](/reference/eslint-plugin-react-hooks/lints/rules-of-hooks) - Validates that components and hooks follow the Rules of Hooks * [`set-state-in-effect`](/reference/eslint-plugin-react-hooks/lints/set-state-in-effect) - Validates against calling setState synchronously in an effect * [`set-state-in-render`](/reference/eslint-plugin-react-hooks/lints/set-state-in-render) - Validates against setting state during render * [`static-components`](/reference/eslint-plugin-react-hooks/lints/static-components) - Validates that components are static, not recreated every render diff --git a/src/content/reference/eslint-plugin-react-hooks/lints/component-hook-factories.md b/src/content/reference/eslint-plugin-react-hooks/lints/component-hook-factories.md index 537903abd..44d23d758 100644 --- a/src/content/reference/eslint-plugin-react-hooks/lints/component-hook-factories.md +++ b/src/content/reference/eslint-plugin-react-hooks/lints/component-hook-factories.md @@ -1,5 +1,6 @@ --- title: component-hook-factories +version: rc --- @@ -8,6 +9,14 @@ Validates against higher order functions defining nested components or hooks. Co + + +This rule is available in the RC version of `eslint-plugin-react-hooks`. + +You can try it by upgrading the lint plugin [to the most recent RC version](/learn/react-compiler/installation#eslint-integration). + + + ## Rule Details {/*rule-details*/} Defining components or hooks inside other functions creates new instances on every call. React treats each as a completely different component, destroying and recreating the entire component tree, losing all state, and causing performance problems. diff --git a/src/content/reference/eslint-plugin-react-hooks/lints/config.md b/src/content/reference/eslint-plugin-react-hooks/lints/config.md index 719e08412..f7e099752 100644 --- a/src/content/reference/eslint-plugin-react-hooks/lints/config.md +++ b/src/content/reference/eslint-plugin-react-hooks/lints/config.md @@ -1,5 +1,6 @@ --- title: config +version: rc --- @@ -8,6 +9,14 @@ Validates the compiler [configuration options](/reference/react-compiler/configu + + +This rule is available in the RC version of `eslint-plugin-react-hooks`. + +You can try it by upgrading the lint plugin [to the most recent RC version](/learn/react-compiler/installation#eslint-integration). + + + ## Rule Details {/*rule-details*/} React Compiler accepts various [configuration options](/reference/react-compiler/configuration) to control its behavior. This rule validates that your configuration uses correct option names and value types, preventing silent failures from typos or incorrect settings. diff --git a/src/content/reference/eslint-plugin-react-hooks/lints/error-boundaries.md b/src/content/reference/eslint-plugin-react-hooks/lints/error-boundaries.md index 830098e5b..bd013f532 100644 --- a/src/content/reference/eslint-plugin-react-hooks/lints/error-boundaries.md +++ b/src/content/reference/eslint-plugin-react-hooks/lints/error-boundaries.md @@ -1,5 +1,6 @@ --- title: error-boundaries +version: rc --- @@ -8,6 +9,14 @@ Validates usage of Error Boundaries instead of try/catch for errors in child com + + +This rule is available in the RC version of `eslint-plugin-react-hooks`. + +You can try it by upgrading the lint plugin [to the most recent RC version](/learn/react-compiler/installation#eslint-integration). + + + ## Rule Details {/*rule-details*/} Try/catch blocks can't catch errors that happen during React's rendering process. Errors thrown in rendering methods or hooks bubble up through the component tree. Only [Error Boundaries](/reference/react/Component#catching-rendering-errors-with-an-error-boundary) can catch these errors. diff --git a/src/content/reference/eslint-plugin-react-hooks/lints/gating.md b/src/content/reference/eslint-plugin-react-hooks/lints/gating.md index 3bd662a86..fdbbadf0e 100644 --- a/src/content/reference/eslint-plugin-react-hooks/lints/gating.md +++ b/src/content/reference/eslint-plugin-react-hooks/lints/gating.md @@ -1,5 +1,6 @@ --- title: gating +version: rc --- @@ -8,6 +9,14 @@ Validates configuration of [gating mode](/reference/react-compiler/gating). + + +This rule is available in the RC version of `eslint-plugin-react-hooks`. + +You can try it by upgrading the lint plugin [to the most recent RC version](/learn/react-compiler/installation#eslint-integration). + + + ## Rule Details {/*rule-details*/} Gating mode lets you gradually adopt React Compiler by marking specific components for optimization. This rule ensures your gating configuration is valid so the compiler knows which components to process. diff --git a/src/content/reference/eslint-plugin-react-hooks/lints/globals.md b/src/content/reference/eslint-plugin-react-hooks/lints/globals.md index fe0cbe008..b9a20d4db 100644 --- a/src/content/reference/eslint-plugin-react-hooks/lints/globals.md +++ b/src/content/reference/eslint-plugin-react-hooks/lints/globals.md @@ -1,5 +1,6 @@ --- title: globals +version: rc --- @@ -8,6 +9,14 @@ Validates against assignment/mutation of globals during render, part of ensuring + + +This rule is available in the RC version of `eslint-plugin-react-hooks`. + +You can try it by upgrading the lint plugin [to the most recent RC version](/learn/react-compiler/installation#eslint-integration). + + + ## Rule Details {/*rule-details*/} Global variables exist outside React's control. When you modify them during render, you break React's assumption that rendering is pure. This can cause components to behave differently in development vs production, break Fast Refresh, and make your app impossible to optimize with features like React Compiler. diff --git a/src/content/reference/eslint-plugin-react-hooks/lints/immutability.md b/src/content/reference/eslint-plugin-react-hooks/lints/immutability.md index 2314cde06..9376271eb 100644 --- a/src/content/reference/eslint-plugin-react-hooks/lints/immutability.md +++ b/src/content/reference/eslint-plugin-react-hooks/lints/immutability.md @@ -1,5 +1,6 @@ --- title: immutability +version: rc --- @@ -8,6 +9,14 @@ Validates against mutating props, state, and other values that [are immutable](/ + + +This rule is available in the RC version of `eslint-plugin-react-hooks`. + +You can try it by upgrading the lint plugin [to the most recent RC version](/learn/react-compiler/installation#eslint-integration). + + + ## Rule Details {/*rule-details*/} A component’s props and state are immutable snapshots. Never mutate them directly. Instead, pass new props down, and use the setter function from `useState`. diff --git a/src/content/reference/eslint-plugin-react-hooks/lints/incompatible-library.md b/src/content/reference/eslint-plugin-react-hooks/lints/incompatible-library.md index e057e1978..aa5877503 100644 --- a/src/content/reference/eslint-plugin-react-hooks/lints/incompatible-library.md +++ b/src/content/reference/eslint-plugin-react-hooks/lints/incompatible-library.md @@ -1,5 +1,6 @@ --- title: incompatible-library +version: rc --- @@ -8,6 +9,14 @@ Validates against usage of libraries which are incompatible with memoization (ma + + +This rule is available in the RC version of `eslint-plugin-react-hooks`. + +You can try it by upgrading the lint plugin [to the most recent RC version](/learn/react-compiler/installation#eslint-integration). + + + These libraries were designed before React's memoization rules were fully documented. They made the correct choices at the time to optimize for ergonomic ways to keep components just the right amount of reactive as app state changes. While these legacy patterns worked, we have since discovered that it's incompatible with React's programming model. We will continue working with library authors to migrate these libraries to use patterns that follow the Rules of React. diff --git a/src/content/reference/eslint-plugin-react-hooks/lints/preserve-manual-memoization.md b/src/content/reference/eslint-plugin-react-hooks/lints/preserve-manual-memoization.md index 93b582b1e..2f296ac56 100644 --- a/src/content/reference/eslint-plugin-react-hooks/lints/preserve-manual-memoization.md +++ b/src/content/reference/eslint-plugin-react-hooks/lints/preserve-manual-memoization.md @@ -1,5 +1,6 @@ --- title: preserve-manual-memoization +version: rc --- @@ -8,6 +9,14 @@ Validates that existing manual memoization is preserved by the compiler. React C + + +This rule is available in the RC version of `eslint-plugin-react-hooks`. + +You can try it by upgrading the lint plugin [to the most recent RC version](/learn/react-compiler/installation#eslint-integration). + + + ## Rule Details {/*rule-details*/} React Compiler preserves your existing `useMemo`, `useCallback`, and `React.memo` calls. If you've manually memoized something, the compiler assumes you had a good reason and won't remove it. However, incomplete dependencies prevent the compiler from understanding your code's data flow and applying further optimizations. diff --git a/src/content/reference/eslint-plugin-react-hooks/lints/purity.md b/src/content/reference/eslint-plugin-react-hooks/lints/purity.md index af8aacc61..14c870fb5 100644 --- a/src/content/reference/eslint-plugin-react-hooks/lints/purity.md +++ b/src/content/reference/eslint-plugin-react-hooks/lints/purity.md @@ -1,5 +1,6 @@ --- title: purity +version: rc --- @@ -8,6 +9,14 @@ Validates that [components/hooks are pure](/reference/rules/components-and-hooks + + +This rule is available in the RC version of `eslint-plugin-react-hooks`. + +You can try it by upgrading the lint plugin [to the most recent RC version](/learn/react-compiler/installation#eslint-integration). + + + ## Rule Details {/*rule-details*/} React components must be pure functions - given the same props, they should always return the same JSX. When components use functions like `Math.random()` or `Date.now()` during render, they produce different output each time, breaking React's assumptions and causing bugs like hydration mismatches, incorrect memoization, and unpredictable behavior. diff --git a/src/content/reference/eslint-plugin-react-hooks/lints/refs.md b/src/content/reference/eslint-plugin-react-hooks/lints/refs.md index 3108fdd89..78776ba57 100644 --- a/src/content/reference/eslint-plugin-react-hooks/lints/refs.md +++ b/src/content/reference/eslint-plugin-react-hooks/lints/refs.md @@ -1,5 +1,6 @@ --- title: refs +version: rc --- @@ -8,6 +9,14 @@ Validates correct usage of refs, not reading/writing during render. See the "pit + + +This rule is available in the RC version of `eslint-plugin-react-hooks`. + +You can try it by upgrading the lint plugin [to the most recent RC version](/learn/react-compiler/installation#eslint-integration). + + + ## Rule Details {/*rule-details*/} Refs hold values that aren't used for rendering. Unlike state, changing a ref doesn't trigger a re-render. Reading or writing `ref.current` during render breaks React's expectations. Refs might not be initialized when you try to read them, and their values can be stale or inconsistent. diff --git a/src/content/reference/eslint-plugin-react-hooks/lints/set-state-in-effect.md b/src/content/reference/eslint-plugin-react-hooks/lints/set-state-in-effect.md index 8c6a7cb47..df285fedf 100644 --- a/src/content/reference/eslint-plugin-react-hooks/lints/set-state-in-effect.md +++ b/src/content/reference/eslint-plugin-react-hooks/lints/set-state-in-effect.md @@ -1,5 +1,6 @@ --- title: set-state-in-effect +version: rc --- @@ -8,6 +9,14 @@ Validates against calling setState synchronously in an effect, which can lead to + + +This rule is available in the RC version of `eslint-plugin-react-hooks`. + +You can try it by upgrading the lint plugin [to the most recent RC version](/learn/react-compiler/installation#eslint-integration). + + + ## Rule Details {/*rule-details*/} Setting state immediately inside an effect forces React to restart the entire render cycle. When you update state in an effect, React must re-render your component, apply changes to the DOM, and then run effects again. This creates an extra render pass that could have been avoided by transforming data directly during render or deriving state from props. Transform data at the top level of your component instead. This code will naturally re-run when props or state change without triggering additional render cycles. diff --git a/src/content/reference/eslint-plugin-react-hooks/lints/set-state-in-render.md b/src/content/reference/eslint-plugin-react-hooks/lints/set-state-in-render.md index 61852990a..c8cfa22a0 100644 --- a/src/content/reference/eslint-plugin-react-hooks/lints/set-state-in-render.md +++ b/src/content/reference/eslint-plugin-react-hooks/lints/set-state-in-render.md @@ -1,5 +1,6 @@ --- title: set-state-in-render +version: rc --- @@ -8,6 +9,14 @@ Validates against setting state during render, which can trigger additional rend + + +This rule is available in the RC version of `eslint-plugin-react-hooks`. + +You can try it by upgrading the lint plugin [to the most recent RC version](/learn/react-compiler/installation#eslint-integration). + + + ## Rule Details {/*rule-details*/} Calling `setState` during render triggers another render before the current one finishes. This creates an infinite loop that crashes your app. diff --git a/src/content/reference/eslint-plugin-react-hooks/lints/static-components.md b/src/content/reference/eslint-plugin-react-hooks/lints/static-components.md index 709303612..06945838f 100644 --- a/src/content/reference/eslint-plugin-react-hooks/lints/static-components.md +++ b/src/content/reference/eslint-plugin-react-hooks/lints/static-components.md @@ -1,5 +1,6 @@ --- title: static-components +version: rc --- @@ -8,6 +9,14 @@ Validates that components are static, not recreated every render. Components tha + + +This rule is available in the RC version of `eslint-plugin-react-hooks`. + +You can try it by upgrading the lint plugin [to the most recent RC version](/learn/react-compiler/installation#eslint-integration). + + + ## Rule Details {/*rule-details*/} Components defined inside other components are recreated on every render. React sees each as a brand new component type, unmounting the old one and mounting the new one, destroying all state and DOM nodes in the process. diff --git a/src/content/reference/eslint-plugin-react-hooks/lints/unsupported-syntax.md b/src/content/reference/eslint-plugin-react-hooks/lints/unsupported-syntax.md index a3eefcdb2..3f3e04d53 100644 --- a/src/content/reference/eslint-plugin-react-hooks/lints/unsupported-syntax.md +++ b/src/content/reference/eslint-plugin-react-hooks/lints/unsupported-syntax.md @@ -1,5 +1,6 @@ --- title: unsupported-syntax +version: rc --- @@ -8,6 +9,14 @@ Validates against syntax that React Compiler does not support. If you need to, y + + +This rule is available in the RC version of `eslint-plugin-react-hooks`. + +You can try it by upgrading the lint plugin [to the most recent RC version](/learn/react-compiler/installation#eslint-integration). + + + ## Rule Details {/*rule-details*/} React Compiler needs to statically analyze your code to apply optimizations. Features like `eval` and `with` make it impossible to statically understand what the code does at compile time, so the compiler can't optimize components that use them. diff --git a/src/content/reference/eslint-plugin-react-hooks/lints/use-memo.md b/src/content/reference/eslint-plugin-react-hooks/lints/use-memo.md index 54bbe1579..db022a802 100644 --- a/src/content/reference/eslint-plugin-react-hooks/lints/use-memo.md +++ b/src/content/reference/eslint-plugin-react-hooks/lints/use-memo.md @@ -1,5 +1,6 @@ --- title: use-memo +version: rc --- @@ -8,6 +9,14 @@ Validates usage of the `useMemo` hook without a return value. See [`useMemo` doc + + +This rule is available in the RC version of `eslint-plugin-react-hooks`. + +You can try it by upgrading the lint plugin [to the most recent RC version](/learn/react-compiler/installation#eslint-integration). + + + ## Rule Details {/*rule-details*/} `useMemo` is for computing and caching expensive values, not for side effects. Without a return value, `useMemo` returns `undefined`, which defeats its purpose and likely indicates you're using the wrong hook. diff --git a/src/content/reference/react/ViewTransition.md b/src/content/reference/react/ViewTransition.md index 653a06a63..14ed136f9 100644 --- a/src/content/reference/react/ViewTransition.md +++ b/src/content/reference/react/ViewTransition.md @@ -159,7 +159,11 @@ Enter/Exit Transitions trigger when a `` is added or removed by ```js function Child() { - return Hi + return ( + +
Hi
+
+ ); } function Parent() { @@ -352,11 +356,7 @@ button:hover { ```js [3, 5] function Component() { - return ( -
- Hi -
- ); + return Hi; } ``` @@ -519,7 +519,6 @@ button:hover { background-image: conic-gradient(at top right, #c76a15, #a6423a, #2b3491); } .thumbnail.fullscreen { - height: 100%; width: 100%; } .video { diff --git a/src/content/reference/react/cache.md b/src/content/reference/react/cache.md index c82543479..04836f19d 100644 --- a/src/content/reference/react/cache.md +++ b/src/content/reference/react/cache.md @@ -1,6 +1,5 @@ --- title: cache -canary: true --- diff --git a/src/content/reference/react/experimental_useEffectEvent.md b/src/content/reference/react/experimental_useEffectEvent.md deleted file mode 100644 index 954cf6587..000000000 --- a/src/content/reference/react/experimental_useEffectEvent.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: experimental_useEffectEvent -version: experimental ---- - - - -**This API is experimental and is not available in a stable version of React yet.** - -You can try it by upgrading React packages to the most recent experimental version: - -- `react@experimental` -- `react-dom@experimental` -- `eslint-plugin-react-hooks@experimental` - -Experimental versions of React may contain bugs. Don't use them in production. - - - - - - -`useEffectEvent` is a React Hook that lets you extract non-reactive logic into an [Effect Event.](/learn/separating-events-from-effects#declaring-an-effect-event) - -```js -const onSomething = useEffectEvent(callback) -``` - - - - diff --git a/src/content/reference/react/useCallback.md b/src/content/reference/react/useCallback.md index a64696dd8..bfe82fa1d 100644 --- a/src/content/reference/react/useCallback.md +++ b/src/content/reference/react/useCallback.md @@ -52,7 +52,7 @@ export default function ProductPage({ productId, referrer, theme }) { İlk render'da, kendisine ilettiğiniz `fn` fonksiyonunu döndürür. -Sonraki render'larda, ya son render'dan önce kaydedilmiş (bağımlılıkları değişmediyse) `fn` fonksiyonunu ya da o anki render'da ilettiğiniz `fn` fonksiyonunu döndürür. +Sonraki render’lar sırasında, eğer bağımlılıklar değişmediyse önceki render’dan zaten saklanmış olan `fn` fonksiyonunu döndürür; aksi halde bu render sırasında ilettiğiniz `fn` fonksiyonunu döndürür. #### Dikkat edilmesi gerekenler {/*caveats*/} @@ -225,7 +225,7 @@ function useCallback(fn, dependencies) { Eğer uygulaman sen bu siteye benziyorsa ve etkileşimlerin çoğu kaba (örneğin bir sayfanın veya tüm bir bölümün değiştirilmesi gibi) ise, memoization genellikle gereksizdir. Öte yandan, eğer uygulaman bir çizim editörüne daha çok benziyorsa ve etkileşimlerin çoğu daha ayrıntılı (örneğin şekilleri taşımak gibi) ise, o zaman memoization’ı oldukça faydalı bulabilirsin. -Bir fonksiyonu `useCallback` ile önbelleğe almak yalnızca birkaç durum için faydalıdır: +`useCallback` ile bir fonksiyonu cache’lemek yalnızca birkaç durumda değerli olur: - [`memo`](/reference/react/memo)'ya sarılmış bir bileşene prop olarak geçersiniz. Değer değişmediyse render'ı atlamak istersiniz. Önbelleğe alma işlemi, yalnızca bağımlılıkları değiştiyse yeniden render tetikler. - Geçtiğiniz fonksiyon daha sonra bazı Hook'ların bağımlılığı olarak kullanırsınız. Örneğin, `useCallback`'e sarılmış başka bir fonksiyonun bağımlılığıdır ya da [`useEffect`](/reference/react/useEffect) için bu fonksiyona bağımlısınızdır. diff --git a/src/content/reference/react/useDeferredValue.md b/src/content/reference/react/useDeferredValue.md index d9021a2f4..855787077 100644 --- a/src/content/reference/react/useDeferredValue.md +++ b/src/content/reference/react/useDeferredValue.md @@ -420,7 +420,7 @@ input { margin: 10px; } Bunu iki adımda gerçekleşen bir süreç olarak düşünebilirsiniz: -1. **İlk olarak, React yeni `query` (`"ab"`) ancak eski `deferredQuery` (hala `"a")` ile yeniden render gerçekleştirir.** Sonuç listesine ilettiğiniz `deferredQuery` değeri *ertelenmiştir:* `query` değerinin "gerisindedir". +1. **Öncelikle, React yeni `query` (`"ab"`) ile yeniden render yapar fakat eski `deferredQuery` (`"a"`) ile kalır.** Sonuç listesine ilettiğiniz `deferredQuery` değeri *ertelemlidir:* yani `query` değerinin gerisinde kalır. 2. **Arka planda, React `query` ve `deferredQuery`'nin *her ikisini de* `"ab"` olarak yeniden render etmeye çalışır.** Bu render tamamlandığında, React ekranda gösterir. Ancak askıya alınırsa (`"ab"`'nin sonuçları henüz yüklenmediyse), React render'dan vazgeçer ve veriler yüklendikten sonra tekrar render etmeyi dener. Kullanıcı, veriler hazır olana kadar eski ertelenmiş değeri görmeye devam eder. diff --git a/src/content/reference/react/useEffect.md b/src/content/reference/react/useEffect.md index 3f10ae8b3..36e8c1454 100644 --- a/src/content/reference/react/useEffect.md +++ b/src/content/reference/react/useEffect.md @@ -1689,11 +1689,13 @@ button { margin-left: 10px; } ### Effect'te nihai prop'ları ve state'i okuma {/*reading-the-latest-props-and-state-from-an-effect*/} - + -Bu bölümde, React'in stabil sürümünde **henüz yayınlanmamış deneysel bir API** anlatılmaktadır. +**`useEffectEvent` API'si şu anda yalnızca React'in Canary ve Experimental kanallarında kullanılabilir.** - +[React’in release kanalları hakkında daha fazla bilgi edinin.](/community/versioning-policy#all-release-channels) + + Varsayılan olarak, Effect'ten reaktif bir değer okuduğunuz zaman bu değeri bağımlılık olarak eklemeniz gerekmektedir. Bu, Effect'inizin o değer her değiştiğinde "tepki" vermesini sağlar. Çoğu bağımlılık için istediğiniz davranış budur. @@ -1708,7 +1710,7 @@ function Page({ url, shoppingCart }) { } ``` -**Ya `url` her değiştiğinde yeni bir sayfa ziyareti kaydetmek istiyorsanız ancak sadece `shoppingCart` değiştiğinde kaydetmek istemiyorsanız?** [Reaktivite kurallarını](#specifying-reactive-dependencies) çiğnemeden `shoppingCart`'ı bağımlılıklardan çıkartamazsınız. Ancak, Effect içinden çağırılsa bile bir kod parçasının yapılan değişikliklere "tepki" vermesini *istemediğinizi* ifade edebilirsiniz. [`useEffectEvent`](/reference/react/experimental_useEffectEvent) Hook'u ile [*Effect Olayı* bildirin](/learn/separating-events-from-effects#declaring-an-effect-event) ve `shoppingCart`'ı okuyan kodu onun içine taşıyın: + **Peki ya her `url` değişiminden sonra yeni bir sayfa ziyareti kaydetmek istiyorsunuz ama yalnızca `shoppingCart` değiştiğinde bunu yapmak istemiyorsunuz?** `shoppingCart`’ı bağımlılıklardan çıkarmak, [reaktivite kurallarını](#specifying-reactive-dependencies) bozacağı için mümkün değildir. Ancak bir kod parçasının Effect içinde çağrılsa bile değişimlere "tepki vermemesini" ifade edebilirsiniz. [`useEffectEvent`](/reference/react/useEffectEvent) Hook’u ile bir *Effect Event* [bildirin](/learn/separating-events-from-effects#declaring-an-effect-event) ve `shoppingCart`’ı okuyan kodu bunun içine taşıyın: ```js {2-4,7,8} function Page({ url, shoppingCart }) { diff --git a/src/content/reference/react/useEffectEvent.md b/src/content/reference/react/useEffectEvent.md new file mode 100644 index 000000000..8792f7ffb --- /dev/null +++ b/src/content/reference/react/useEffectEvent.md @@ -0,0 +1,102 @@ +--- +title: useEffectEvent +version: canary +--- + + + + +**The `useEffectEvent` API is currently only available in React’s Canary and Experimental channels.** + +[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels) + + + + + +`useEffectEvent` is a React Hook that lets you extract non-reactive logic from your Effects into a reusable function called an [Effect Event](/learn/separating-events-from-effects#declaring-an-effect-event). + +```js +const onSomething = useEffectEvent(callback) +``` + + + + + +## Reference {/*reference*/} + +### `useEffectEvent(callback)` {/*useeffectevent*/} + +Call `useEffectEvent` at the top level of your component to declare an Effect Event. Effect Events are functions you can call inside Effects, such as `useEffect`: + +```js {4-6,11} +import { useEffectEvent, useEffect } from 'react'; + +function ChatRoom({ roomId, theme }) { + const onConnected = useEffectEvent(() => { + showNotification('Connected!', theme); + }); + + useEffect(() => { + const connection = createConnection(serverUrl, roomId); + connection.on('connected', () => { + onConnected(); + }); + connection.connect(); + return () => connection.disconnect(); + }, [roomId]); + + // ... +} +``` + +[See more examples below.](#usage) + +#### Parameters {/*parameters*/} + +- `callback`: A function containing the logic for your Effect Event. When you define an Effect Event with `useEffectEvent`, the `callback` always accesses the latest values from props and state when it is invoked. This helps avoid issues with stale closures. + +#### Returns {/*returns*/} + +Returns an Effect Event function. You can call this function inside `useEffect`, `useLayoutEffect`, or `useInsertionEffect`. + +#### Caveats {/*caveats*/} + +- **Only call inside Effects:** Effect Events should only be called within Effects. Define them just before the Effect that uses them. Do not pass them to other components or hooks. +- **Not a dependency shortcut:** Do not use `useEffectEvent` to avoid specifying dependencies in your Effect's dependency array. This can hide bugs and make your code harder to understand. Prefer explicit dependencies or use refs to compare previous values if needed. +- **Use for non-reactive logic:** Only use `useEffectEvent` to extract logic that does not depend on changing values. + +___ + +## Usage {/*usage*/} + +### Reading the latest props and state {/*reading-the-latest-props-and-state*/} + +Typically, when you access a reactive value inside an Effect, you must include it in the dependency array. This makes sure your Effect runs again whenever that value changes, which is usually the desired behavior. + +But in some cases, you may want to read the most recent props or state inside an Effect without causing the Effect to re-run when those values change. + +To [read the latest props or state](/learn/separating-events-from-effects#reading-latest-props-and-state-with-effect-events) in your Effect, without making those values reactive, include them in an Effect Event. + +```js {7-9,12} +import { useEffect, useContext, useEffectEvent } from 'react'; + +function Page({ url }) { + const { items } = useContext(ShoppingCartContext); + const numberOfItems = items.length; + + const onNavigate = useEffectEvent((visitedUrl) => { + logVisit(visitedUrl, numberOfItems); + }); + + useEffect(() => { + onNavigate(url); + }, [url]); + + // ... +} +``` + +You can pass reactive values like `url` as arguments to the Effect Event. This lets you access the latest values without making your Effect re-run for every change. + diff --git a/src/sidebarReference.json b/src/sidebarReference.json index c5d3effeb..f227e084e 100644 --- a/src/sidebarReference.json +++ b/src/sidebarReference.json @@ -38,6 +38,11 @@ "title": "useEffect", "path": "/reference/react/useEffect" }, + { + "title": "useEffectEvent", + "path": "/reference/react/useEffectEvent", + "version": "canary" + }, { "title": "useId", "path": "/reference/react/useId" @@ -381,73 +386,90 @@ "title": "Lints", "path": "/reference/eslint-plugin-react-hooks", "routes": [ + { + "title": "exhaustive-deps", + "path": "/reference/eslint-plugin-react-hooks/lints/exhaustive-deps" + }, + { + "title": "rules-of-hooks", + "path": "/reference/eslint-plugin-react-hooks/lints/rules-of-hooks" + }, { "title": "component-hook-factories", - "path": "/reference/eslint-plugin-react-hooks/lints/component-hook-factories" + "path": "/reference/eslint-plugin-react-hooks/lints/component-hook-factories", + "version": "rc" }, { "title": "config", - "path": "/reference/eslint-plugin-react-hooks/lints/config" + "path": "/reference/eslint-plugin-react-hooks/lints/config", + "version": "rc" }, { "title": "error-boundaries", - "path": "/reference/eslint-plugin-react-hooks/lints/error-boundaries" - }, - { - "title": "exhaustive-deps", - "path": "/reference/eslint-plugin-react-hooks/lints/exhaustive-deps" + "path": "/reference/eslint-plugin-react-hooks/lints/error-boundaries", + "version": "rc" }, { "title": "gating", - "path": "/reference/eslint-plugin-react-hooks/lints/gating" + "path": "/reference/eslint-plugin-react-hooks/lints/gating", + "version": "rc" }, { "title": "globals", - "path": "/reference/eslint-plugin-react-hooks/lints/globals" + "path": "/reference/eslint-plugin-react-hooks/lints/globals", + "version": "rc" }, { "title": "immutability", - "path": "/reference/eslint-plugin-react-hooks/lints/immutability" + "path": "/reference/eslint-plugin-react-hooks/lints/immutability", + "version": "rc" }, { "title": "incompatible-library", - "path": "/reference/eslint-plugin-react-hooks/lints/incompatible-library" + "path": "/reference/eslint-plugin-react-hooks/lints/incompatible-library", + "version": "rc" }, + { "title": "preserve-manual-memoization", - "path": "/reference/eslint-plugin-react-hooks/lints/preserve-manual-memoization" + "path": "/reference/eslint-plugin-react-hooks/lints/preserve-manual-memoization", + "version": "rc" }, { "title": "purity", - "path": "/reference/eslint-plugin-react-hooks/lints/purity" + "path": "/reference/eslint-plugin-react-hooks/lints/purity", + "version": "rc" }, { "title": "refs", - "path": "/reference/eslint-plugin-react-hooks/lints/refs" - }, - { - "title": "rules-of-hooks", - "path": "/reference/eslint-plugin-react-hooks/lints/rules-of-hooks" + "path": "/reference/eslint-plugin-react-hooks/lints/refs", + "version": "rc" }, + { "title": "set-state-in-effect", - "path": "/reference/eslint-plugin-react-hooks/lints/set-state-in-effect" + "path": "/reference/eslint-plugin-react-hooks/lints/set-state-in-effect", + "version": "rc" }, { "title": "set-state-in-render", - "path": "/reference/eslint-plugin-react-hooks/lints/set-state-in-render" + "path": "/reference/eslint-plugin-react-hooks/lints/set-state-in-render", + "version": "rc" }, { "title": "static-components", - "path": "/reference/eslint-plugin-react-hooks/lints/static-components" + "path": "/reference/eslint-plugin-react-hooks/lints/static-components", + "version": "rc" }, { "title": "unsupported-syntax", - "path": "/reference/eslint-plugin-react-hooks/lints/unsupported-syntax" + "path": "/reference/eslint-plugin-react-hooks/lints/unsupported-syntax", + "version": "rc" }, { "title": "use-memo", - "path": "/reference/eslint-plugin-react-hooks/lints/use-memo" + "path": "/reference/eslint-plugin-react-hooks/lints/use-memo", + "version": "rc" } ] }, diff --git a/vercel.json b/vercel.json index fcc45e663..773cf7770 100644 --- a/vercel.json +++ b/vercel.json @@ -248,6 +248,11 @@ "source": "/feed.xml", "destination": "/rss.xml", "permanent": true + }, + { + "source": "/reference/react/experimental_useEffectEvent", + "destination": "/reference/react/useEffectEvent", + "permanent": true } ], "headers": [