CINXE.COM
<!DOCTYPE html><html lang="en" dir="ltr"><head><meta charSet="utf-8" data-next-head=""/><meta name="viewport" content="width=device-width, initial-scale=1" data-next-head=""/><script async="" src="https://www.googletagmanager.com/gtag/js?id=G-B1E83PJ3RT"></script><title data-next-head="">Reusing Logic with Custom Hooks – React</title><link rel="canonical" href="https://react.dev/learn/reusing-logic-with-custom-hooks" data-next-head=""/><link rel="alternate" href="https://react.dev/learn/reusing-logic-with-custom-hooks" hrefLang="x-default" data-next-head=""/><link rel="alternate" hrefLang="en" href="https://react.dev/learn/reusing-logic-with-custom-hooks" data-next-head=""/><link rel="alternate" hrefLang="zh-hans" href="https://zh-hans.react.dev/learn/reusing-logic-with-custom-hooks" data-next-head=""/><link rel="alternate" hrefLang="es" href="https://es.react.dev/learn/reusing-logic-with-custom-hooks" data-next-head=""/><link rel="alternate" hrefLang="fr" href="https://fr.react.dev/learn/reusing-logic-with-custom-hooks" data-next-head=""/><link rel="alternate" hrefLang="ja" href="https://ja.react.dev/learn/reusing-logic-with-custom-hooks" data-next-head=""/><link rel="alternate" hrefLang="tr" href="https://tr.react.dev/learn/reusing-logic-with-custom-hooks" data-next-head=""/><link rel="alternate" hrefLang="ko" href="https://ko.react.dev/learn/reusing-logic-with-custom-hooks" data-next-head=""/><meta property="fb:app_id" content="623268441017527" data-next-head=""/><meta property="og:type" content="website" data-next-head=""/><meta property="og:url" content="https://react.dev/learn/reusing-logic-with-custom-hooks" data-next-head=""/><meta property="og:title" content="Reusing Logic with Custom Hooks – React" data-next-head=""/><meta property="og:description" content="The library for web and native user interfaces" data-next-head=""/><meta property="og:image" content="https://react.dev/images/og-learn.png" data-next-head=""/><meta name="twitter:card" content="summary_large_image" data-next-head=""/><meta name="twitter:site" content="@reactjs" data-next-head=""/><meta name="twitter:creator" content="@reactjs" data-next-head=""/><meta name="twitter:title" content="Reusing Logic with Custom Hooks – React" data-next-head=""/><meta name="twitter:description" content="The library for web and native user interfaces" data-next-head=""/><meta name="twitter:image" content="https://react.dev/images/og-learn.png" data-next-head=""/><meta name="google-site-verification" content="sIlAGs48RulR4DdP95YSWNKZIEtCqQmRjzn-Zq-CcD0" data-next-head=""/><meta name="algolia-search-order" content="50" data-next-head=""/><link rel="preload" href="/fonts/Source-Code-Pro-Regular.woff2" as="font" type="font/woff2" crossorigin="anonymous" data-next-head=""/><link rel="preload" href="https://react.dev/fonts/Optimistic_Display_W_Md.woff2" as="font" type="font/woff2" crossorigin="anonymous" data-next-head=""/><link rel="preload" href="https://react.dev/fonts/Optimistic_Display_W_SBd.woff2" as="font" type="font/woff2" crossorigin="anonymous" data-next-head=""/><link rel="preload" href="https://react.dev/fonts/Optimistic_Display_W_Bd.woff2" as="font" type="font/woff2" crossorigin="anonymous" data-next-head=""/><link rel="preload" href="https://react.dev/fonts/Optimistic_Text_W_Md.woff2" as="font" type="font/woff2" crossorigin="anonymous" data-next-head=""/><link rel="preload" href="https://react.dev/fonts/Optimistic_Text_W_Bd.woff2" as="font" type="font/woff2" crossorigin="anonymous" data-next-head=""/><link rel="preload" href="https://react.dev/fonts/Optimistic_Text_W_Rg.woff2" as="font" type="font/woff2" crossorigin="anonymous" data-next-head=""/><link rel="preload" href="https://react.dev/fonts/Optimistic_Text_W_It.woff2" as="font" type="font/woff2" crossorigin="anonymous" data-next-head=""/><link rel="preconnect" href="https://1FCF9AYYAT-dsn.algolia.net" data-next-head=""/><link rel="preload" href="/_next/static/css/30a0450f062b33f4.css" as="style"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"/><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"/><link rel="manifest" href="/site.webmanifest"/><link rel="mask-icon" href="/safari-pinned-tab.svg" color="#404756"/><meta name="msapplication-TileColor" content="#2b5797"/><meta name="theme-color" content="#23272f"/><link rel="preload" as="image" imageSrcSet="/_next/image?url=%2Fimages%2Fuwu.png&w=64&q=75 1x, /_next/image?url=%2Fimages%2Fuwu.png&w=128&q=75 2x" data-next-head=""/><link rel="stylesheet" href="/_next/static/css/30a0450f062b33f4.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" noModule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-35e65ce5d83d5425.js" defer=""></script><script src="/_next/static/chunks/framework-76ad44d61c11f057.js" defer=""></script><script src="/_next/static/chunks/main-6b2aa10951d4c273.js" defer=""></script><script src="/_next/static/chunks/pages/_app-6d7b64b3b21a9551.js" defer=""></script><script src="/_next/static/chunks/668-e65a23e6848f265b.js" defer=""></script><script src="/_next/static/chunks/679-d6cf26e485b33427.js" defer=""></script><script src="/_next/static/chunks/pages/%5B%5B...markdownPath%5D%5D-df3d4d0e644b3e5c.js" defer=""></script><script src="/_next/static/_Fz6u7K2TvuR-_M7SFBnH/_buildManifest.js" defer=""></script><script src="/_next/static/_Fz6u7K2TvuR-_M7SFBnH/_ssgManifest.js" defer=""></script></head><script>window.dataLayer = window.dataLayer || [];function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'G-B1E83PJ3RT');</script><body class="font-text font-medium antialiased text-lg bg-wash dark:bg-wash-dark text-secondary dark:text-secondary-dark leading-base"><script> (function () { try { let logShown = false; function setUwu(isUwu) { try { if (isUwu) { localStorage.setItem('uwu', true); document.documentElement.classList.add('uwu'); if (!logShown) { console.log('uwu mode! turn off with ?uwu=0'); console.log('logo credit to @sawaratsuki1004 via https://github.com/SAWARATSUKI/ServiceLogos'); logShown = true; } } else { localStorage.removeItem('uwu'); document.documentElement.classList.remove('uwu'); console.log('uwu mode off. turn on with ?uwu'); } } catch (err) { } } window.__setUwu = setUwu; function checkQueryParam() { const params = new URLSearchParams(window.location.search); const value = params.get('uwu'); switch(value) { case '': case 'true': case '1': return true; case 'false': case '0': return false; default: return null; } } function checkLocalStorage() { try { return localStorage.getItem('uwu') === 'true'; } catch (err) { return false; } } const uwuQueryParam = checkQueryParam(); if (uwuQueryParam != null) { setUwu(uwuQueryParam); } else if (checkLocalStorage()) { document.documentElement.classList.add('uwu'); } } catch (err) { } })(); </script><script> (function () { function setTheme(newTheme) { window.__theme = newTheme; if (newTheme === 'dark') { document.documentElement.classList.add('dark'); } else if (newTheme === 'light') { document.documentElement.classList.remove('dark'); } } var preferredTheme; try { preferredTheme = localStorage.getItem('theme'); } catch (err) { } window.__setPreferredTheme = function(newTheme) { preferredTheme = newTheme; setTheme(newTheme); try { localStorage.setItem('theme', newTheme); } catch (err) { } }; var initialTheme = preferredTheme; var darkQuery = window.matchMedia('(prefers-color-scheme: dark)'); if (!initialTheme) { initialTheme = darkQuery.matches ? 'dark' : 'light'; } setTheme(initialTheme); darkQuery.addEventListener('change', function (e) { if (!preferredTheme) { setTheme(e.matches ? 'dark' : 'light'); } }); // Detect whether the browser is Mac to display platform specific content // An example of such content can be the keyboard shortcut displayed in the search bar document.documentElement.classList.add( window.navigator.platform.includes('Mac') ? "platform-mac" : "platform-win" ); })(); </script><link rel="preload" as="image" imageSrcSet="/_next/image?url=%2Fimages%2Fuwu.png&w=64&q=75 1x, /_next/image?url=%2Fimages%2Fuwu.png&w=128&q=75 2x"/><div id="__next"><div></div><div class="z-40 sticky top-0"><nav class="duration-300 backdrop-filter backdrop-blur-lg backdrop-saturate-200 transition-shadow bg-opacity-90 items-center w-full flex justify-between bg-wash dark:bg-wash-dark dark:bg-opacity-95 px-1.5 lg:pe-5 lg:ps-4 z-40"><div class="flex items-center justify-between w-full h-16 gap-0 sm:gap-3"><div class="flex flex-row 3xl:flex-1 items-centers"><button type="button" aria-label="Menu" class="active:scale-95 transition-transform flex lg:hidden w-12 h-12 rounded-full items-center justify-center hover:bg-primary/5 hover:dark:bg-primary-dark/5 outline-link"><svg width="1.33em" height="1.33em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg></button><span data-state="closed" class="flex items-center" style="-webkit-touch-callout:none"><div class="flex items-center"><div class="uwu-visible flex items-center justify-center h-full"><a href="/"><img alt="logo by @sawaratsuki1004" title="logo by @sawaratsuki1004" width="63" height="32" decoding="async" data-nimg="1" class="h-8" style="color:transparent" srcSet="/_next/image?url=%2Fimages%2Fuwu.png&w=64&q=75 1x, /_next/image?url=%2Fimages%2Fuwu.png&w=128&q=75 2x" src="/_next/image?url=%2Fimages%2Fuwu.png&w=128&q=75"/></a></div><div class="uwu-hidden"><a class="active:scale-95 overflow-hidden transition-transform relative items-center text-primary dark:text-primary-dark p-1 whitespace-nowrap outline-link rounded-full 3xl:rounded-xl inline-flex text-lg font-normal gap-2" href="/"><svg width="100%" height="100%" viewBox="-10.5 -9.45 21 18.9" fill="none" xmlns="http://www.w3.org/2000/svg" class="text-sm me-0 w-10 h-10 text-brand dark:text-brand-dark flex origin-center transition-all ease-in-out"><circle cx="0" cy="0" r="2" fill="currentColor"></circle><g stroke="currentColor" stroke-width="1" fill="none"><ellipse rx="10" ry="4.5"></ellipse><ellipse rx="10" ry="4.5" transform="rotate(60)"></ellipse><ellipse rx="10" ry="4.5" transform="rotate(120)"></ellipse></g></svg><span class="sr-only 3xl:not-sr-only">React</span></a></div></div></span><div class="flex flex-column justify-center items-center"><a class=" flex py-2 flex-column justify-center items-center text-gray-50 dark:text-gray-30 hover:text-link hover:dark:text-link-dark hover:underline text-sm ms-1 cursor-pointer" href="/versions">v<!-- -->19</a></div></div><div class="items-center justify-center flex-1 hidden w-full md:flex 3xl:w-auto 3xl:shrink-0 3xl:justify-center"><button type="button" class="flex 3xl:w-[56rem] 3xl:mx-0 relative ps-4 pe-1 py-1 h-10 bg-gray-30/20 dark:bg-gray-40/20 outline-none focus:outline-link betterhover:hover:bg-opacity-80 pointer items-center text-start w-full text-gray-30 rounded-full align-middle text-base"><svg width="1em" height="1em" viewBox="0 0 20 20" class="align-middle me-3 text-gray-30 shrink-0 group-betterhover:hover:text-gray-70"><path d="M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z" stroke="currentColor" fill="none" stroke-width="2" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"></path></svg>Search<span class="hidden ms-auto sm:flex item-center me-1"><kbd class="w-5 h-5 border border-transparent me-1 bg-wash dark:bg-wash-dark text-gray-30 align-middle p-0 inline-flex justify-center items-center text-xs text-center rounded-md" data-platform="mac">⌘</kbd><kbd class="w-10 h-5 border border-transparent me-1 bg-wash dark:bg-wash-dark text-gray-30 align-middle p-0 inline-flex justify-center items-center text-xs text-center rounded-md" data-platform="win">Ctrl</kbd><kbd class="w-5 h-5 border border-transparent me-1 bg-wash dark:bg-wash-dark text-gray-30 align-middle p-0 inline-flex justify-center items-center text-xs text-center rounded-md">K</kbd></span></button></div><div class="text-base justify-center items-center gap-1.5 flex 3xl:flex-1 flex-row 3xl:justify-end"><div class="mx-2.5 gap-1.5 hidden lg:flex"><div class="flex flex-auto sm:flex-1"><a class="active:scale-95 transition-transform w-full text-center outline-link py-1.5 px-1.5 xs:px-3 sm:px-4 rounded-full capitalize whitespace-nowrap bg-highlight dark:bg-highlight-dark text-link dark:text-link-dark" href="/learn">Learn</a></div><div class="flex flex-auto sm:flex-1"><a class="active:scale-95 transition-transform w-full text-center outline-link py-1.5 px-1.5 xs:px-3 sm:px-4 rounded-full capitalize whitespace-nowrap hover:bg-primary/5 hover:dark:bg-primary-dark/5" href="/reference/react">Reference</a></div><div class="flex flex-auto sm:flex-1"><a class="active:scale-95 transition-transform w-full text-center outline-link py-1.5 px-1.5 xs:px-3 sm:px-4 rounded-full capitalize whitespace-nowrap hover:bg-primary/5 hover:dark:bg-primary-dark/5" href="/community">Community</a></div><div class="flex flex-auto sm:flex-1"><a class="active:scale-95 transition-transform w-full text-center outline-link py-1.5 px-1.5 xs:px-3 sm:px-4 rounded-full capitalize whitespace-nowrap hover:bg-primary/5 hover:dark:bg-primary-dark/5" href="/blog">Blog</a></div></div><div class="flex w-full md:hidden"></div><div class="flex items-center -space-x-2.5 xs:space-x-0 "><div class="flex md:hidden"><button aria-label="Search" type="button" class="flex items-center justify-center w-12 h-12 transition-transform rounded-full active:scale-95 md:hidden hover:bg-secondary-button hover:dark:bg-secondary-button-dark outline-link"><svg width="1em" height="1em" viewBox="0 0 20 20" class="w-5 h-5 align-middle"><path d="M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z" stroke="currentColor" fill="none" stroke-width="2" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"></path></svg></button></div><div class="flex dark:hidden"><button type="button" aria-label="Use Dark Mode" class="flex items-center justify-center w-12 h-12 transition-transform rounded-full active:scale-95 hover:bg-primary/5 hover:dark:bg-primary-dark/5 outline-link"><svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 32 32"><g fill="none" fill-rule="evenodd" transform="translate(-440 -200)"><path fill="currentColor" fill-rule="nonzero" stroke="currentColor" stroke-width="0.5" d="M102,21 C102,18.1017141 103.307179,15.4198295 105.51735,13.6246624 C106.001939,13.2310647 105.821611,12.4522936 105.21334,12.3117518 C104.322006,12.1058078 103.414758,12 102.5,12 C95.8722864,12 90.5,17.3722864 90.5,24 C90.5,30.6277136 95.8722864,36 102.5,36 C106.090868,36 109.423902,34.4109093 111.690274,31.7128995 C112.091837,31.2348572 111.767653,30.5041211 111.143759,30.4810139 C106.047479,30.2922628 102,26.1097349 102,21 Z M102.5,34.5 C96.7007136,34.5 92,29.7992864 92,24 C92,18.2007136 96.7007136,13.5 102.5,13.5 C102.807386,13.5 103.113925,13.5136793 103.419249,13.5407785 C101.566047,15.5446378 100.5,18.185162 100.5,21 C100.5,26.3198526 104.287549,30.7714322 109.339814,31.7756638 L109.516565,31.8092927 C107.615276,33.5209452 105.138081,34.5 102.5,34.5 Z" transform="translate(354.5 192)"></path><polygon points="444 228 468 228 468 204 444 204"></polygon></g></svg></button></div><div class="hidden dark:flex"><button type="button" aria-label="Use Light Mode" class="flex items-center justify-center w-12 h-12 transition-transform rounded-full active:scale-95 hover:bg-primary/5 hover:dark:bg-primary-dark/5 outline-link"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><g fill="none" fill-rule="evenodd" transform="translate(-442 -200)"><g fill="currentColor" transform="translate(356 144)"><path fill-rule="nonzero" d="M108.5 24C108.5 27.5902136 105.590214 30.5 102 30.5 98.4097864 30.5 95.5 27.5902136 95.5 24 95.5 20.4097864 98.4097864 17.5 102 17.5 105.590214 17.5 108.5 20.4097864 108.5 24zM107 24C107 21.2382136 104.761786 19 102 19 99.2382136 19 97 21.2382136 97 24 97 26.7617864 99.2382136 29 102 29 104.761786 29 107 26.7617864 107 24zM101 12.75L101 14.75C101 15.1642136 101.335786 15.5 101.75 15.5 102.164214 15.5 102.5 15.1642136 102.5 14.75L102.5 12.75C102.5 12.3357864 102.164214 12 101.75 12 101.335786 12 101 12.3357864 101 12.75zM95.7255165 14.6323616L96.7485165 16.4038616C96.9556573 16.7625614 97.4143618 16.8854243 97.7730616 16.6782835 98.1317614 16.4711427 98.2546243 16.0124382 98.0474835 15.6537384L97.0244835 13.8822384C96.8173427 13.5235386 96.3586382 13.4006757 95.9999384 13.6078165 95.6412386 13.8149573 95.5183757 14.2736618 95.7255165 14.6323616zM91.8822384 19.0244835L93.6537384 20.0474835C94.0124382 20.2546243 94.4711427 20.1317614 94.6782835 19.7730616 94.8854243 19.4143618 94.7625614 18.9556573 94.4038616 18.7485165L92.6323616 17.7255165C92.2736618 17.5183757 91.8149573 17.6412386 91.6078165 17.9999384 91.4006757 18.3586382 91.5235386 18.8173427 91.8822384 19.0244835zM90.75 25L92.75 25C93.1642136 25 93.5 24.6642136 93.5 24.25 93.5 23.8357864 93.1642136 23.5 92.75 23.5L90.75 23.5C90.3357864 23.5 90 23.8357864 90 24.25 90 24.6642136 90.3357864 25 90.75 25zM92.6323616 30.2744835L94.4038616 29.2514835C94.7625614 29.0443427 94.8854243 28.5856382 94.6782835 28.2269384 94.4711427 27.8682386 94.0124382 27.7453757 93.6537384 27.9525165L91.8822384 28.9755165C91.5235386 29.1826573 91.4006757 29.6413618 91.6078165 30.0000616 91.8149573 30.3587614 92.2736618 30.4816243 92.6323616 30.2744835zM97.0244835 34.1177616L98.0474835 32.3462616C98.2546243 31.9875618 98.1317614 31.5288573 97.7730616 31.3217165 97.4143618 31.1145757 96.9556573 31.2374386 96.7485165 31.5961384L95.7255165 33.3676384C95.5183757 33.7263382 95.6412386 34.1850427 95.9999384 34.3921835 96.3586382 34.5993243 96.8173427 34.4764614 97.0244835 34.1177616zM103 35.25L103 33.25C103 32.8357864 102.664214 32.5 102.25 32.5 101.835786 32.5 101.5 32.8357864 101.5 33.25L101.5 35.25C101.5 35.6642136 101.835786 36 102.25 36 102.664214 36 103 35.6642136 103 35.25zM108.274483 33.3676384L107.251483 31.5961384C107.044343 31.2374386 106.585638 31.1145757 106.226938 31.3217165 105.868239 31.5288573 105.745376 31.9875618 105.952517 32.3462616L106.975517 34.1177616C107.182657 34.4764614 107.641362 34.5993243 108.000062 34.3921835 108.358761 34.1850427 108.481624 33.7263382 108.274483 33.3676384zM112.117762 28.9755165L110.346262 27.9525165C109.987562 27.7453757 109.528857 27.8682386 109.321717 28.2269384 109.114576 28.5856382 109.237439 29.0443427 109.596138 29.2514835L111.367638 30.2744835C111.726338 30.4816243 112.185043 30.3587614 112.392183 30.0000616 112.599324 29.6413618 112.476461 29.1826573 112.117762 28.9755165zM113.25 23L111.25 23C110.835786 23 110.5 23.3357864 110.5 23.75 110.5 24.1642136 110.835786 24.5 111.25 24.5L113.25 24.5C113.664214 24.5 114 24.1642136 114 23.75 114 23.3357864 113.664214 23 113.25 23zM111.367638 17.7255165L109.596138 18.7485165C109.237439 18.9556573 109.114576 19.4143618 109.321717 19.7730616 109.528857 20.1317614 109.987562 20.2546243 110.346262 20.0474835L112.117762 19.0244835C112.476461 18.8173427 112.599324 18.3586382 112.392183 17.9999384 112.185043 17.6412386 111.726338 17.5183757 111.367638 17.7255165zM106.975517 13.8822384L105.952517 15.6537384C105.745376 16.0124382 105.868239 16.4711427 106.226938 16.6782835 106.585638 16.8854243 107.044343 16.7625614 107.251483 16.4038616L108.274483 14.6323616C108.481624 14.2736618 108.358761 13.8149573 108.000062 13.6078165 107.641362 13.4006757 107.182657 13.5235386 106.975517 13.8822384z" transform="translate(0 48)" stroke="currentColor" stroke-width="0.25"></path><path d="M98.6123,60.1372 C98.6123,59.3552 98.8753,58.6427 99.3368,58.0942 C99.5293,57.8657 99.3933,57.5092 99.0943,57.5017 C99.0793,57.5012 99.0633,57.5007 99.0483,57.5007 C97.1578,57.4747 95.5418,59.0312 95.5008,60.9217 C95.4578,62.8907 97.0408,64.5002 98.9998,64.5002 C99.7793,64.5002 100.4983,64.2452 101.0798,63.8142 C101.3183,63.6372 101.2358,63.2627 100.9478,63.1897 C99.5923,62.8457 98.6123,61.6072 98.6123,60.1372" transform="translate(3 11)"></path></g><polygon points="444 228 468 228 468 204 444 204"></polygon></g></svg></button></div><div class="flex"><a class="active:scale-95 transition-transform flex w-12 h-12 rounded-full items-center justify-center hover:bg-primary/5 hover:dark:bg-primary-dark/5 outline-link" aria-label="Translations" href="/community/translations"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d=" M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z "></path></svg></a></div><div class="flex"><a class="flex items-center justify-center w-12 h-12 transition-transform rounded-full active:scale-95 hover:bg-primary/5 hover:dark:bg-primary-dark/5 outline-link" target="_blank" rel="noreferrer noopener" aria-label="Open on GitHub" href="https://github.com/facebook/react/releases"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24"><g fill="currentColor"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"></path></g></svg></a></div></div></div></div></nav></div><div class="grid grid-cols-only-content lg:grid-cols-sidebar-content 2xl:grid-cols-sidebar-content-toc"><div class="lg:-mt-16 z-10"><div class="fixed top-0 py-0 shadow lg:pt-16 lg:sticky start-0 end-0 lg:shadow-none"><div class="sticky top-0 lg:bottom-0 lg:h-[calc(100vh-4rem)] flex flex-col"><div class="overflow-y-scroll no-bg-scrollbar lg:w-[342px] grow bg-wash dark:bg-wash-dark" style="overscroll-behavior:contain"><aside class="lg:grow flex-col w-full pb-8 lg:pb-0 lg:max-w-custom-xs z-10 hidden lg:block"><nav role="navigation" style="--bg-opacity:.2" class="w-full pt-6 scrolling-touch lg:h-auto grow pe-0 lg:pe-5 lg:pb-16 md:pt-4 lg:pt-4 scrolling-gpu"><!--$--><ul><h3 class="mb-1 text-sm font-bold ms-5 text-tertiary dark:text-tertiary-dark">GET STARTED</h3><li><a title="Quick Start" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between ps-5 text-base font-bold text-primary dark:text-primary-dark" href="/learn"><div>Quick Start<!-- --> </div><span class="pe-1 text-tertiary dark:text-tertiary-dark"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" class="duration-100 ease-in transition -rotate-90 rtl:rotate-90" style="min-width:20px;min-height:20px"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg></span></a><div class="opacity-50" style="transition:opacity 250ms ease-in-out"><div id="react-collapsed-panel-:R8im6:" aria-hidden="true" role="region" style="box-sizing:border-box;display:none;height:0px;overflow:hidden"><ul><li><a title="Tutorial: Tic-Tac-Toe" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/tutorial-tic-tac-toe"><div>Tutorial: Tic-Tac-Toe<!-- --> </div></a></li><li><a title="Thinking in React" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/thinking-in-react"><div>Thinking in React<!-- --> </div></a></li></ul></div></div></li><li><a title="Installation" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between ps-5 text-base font-bold text-primary dark:text-primary-dark" href="/learn/installation"><div>Installation<!-- --> </div><span class="pe-1 text-tertiary dark:text-tertiary-dark"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" class="duration-100 ease-in transition -rotate-90 rtl:rotate-90" style="min-width:20px;min-height:20px"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg></span></a><div class="opacity-50" style="transition:opacity 250ms ease-in-out"><div id="react-collapsed-panel-:R8qm6:" aria-hidden="true" role="region" style="box-sizing:border-box;display:none;height:0px;overflow:hidden"><ul><li><a title="Creating a React App" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/creating-a-react-app"><div>Creating a React App<!-- --> </div></a></li><li><a title="Build a React App from Scratch" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/build-a-react-app-from-scratch"><div>Build a React App from Scratch<!-- --> </div></a></li><li><a title="Add React to an Existing Project" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/add-react-to-an-existing-project"><div>Add React to an Existing Project<!-- --> </div></a></li></ul></div></div></li><li><a title="Setup" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between ps-5 text-base font-bold text-primary dark:text-primary-dark" href="/learn/setup"><div>Setup<!-- --> </div><span class="pe-1 text-tertiary dark:text-tertiary-dark"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" class="duration-100 ease-in transition -rotate-90 rtl:rotate-90" style="min-width:20px;min-height:20px"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg></span></a><div class="opacity-50" style="transition:opacity 250ms ease-in-out"><div id="react-collapsed-panel-:R92m6:" aria-hidden="true" role="region" style="box-sizing:border-box;display:none;height:0px;overflow:hidden"><ul><li><a title="Editor Setup" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/editor-setup"><div>Editor Setup<!-- --> </div></a></li><li><a title="Using TypeScript" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/typescript"><div>Using TypeScript<!-- --> </div></a></li><li><a title="React Developer Tools" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/react-developer-tools"><div>React Developer Tools<!-- --> </div></a></li><li><a title="React Compiler" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/react-compiler"><div>React Compiler<!-- --> </div></a></li></ul></div></div></li><li role="separator" class="mt-4 mb-2 ms-5 border-b border-border dark:border-border-dark"></li><h3 class="mb-1 text-sm font-bold ms-5 text-tertiary dark:text-tertiary-dark mt-2">LEARN REACT</h3><li><a title="Describing the UI" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between ps-5 text-base font-bold text-primary dark:text-primary-dark" href="/learn/describing-the-ui"><div>Describing the UI<!-- --> </div><span class="pe-1 text-tertiary dark:text-tertiary-dark"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" class="duration-100 ease-in transition -rotate-90 rtl:rotate-90" style="min-width:20px;min-height:20px"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg></span></a><div class="opacity-50" style="transition:opacity 250ms ease-in-out"><div id="react-collapsed-panel-:R9im6:" aria-hidden="true" role="region" style="box-sizing:border-box;display:none;height:0px;overflow:hidden"><ul><li><a title="Your First Component" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/your-first-component"><div>Your First Component<!-- --> </div></a></li><li><a title="Importing and Exporting Components" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/importing-and-exporting-components"><div>Importing and Exporting Components<!-- --> </div></a></li><li><a title="Writing Markup with JSX" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/writing-markup-with-jsx"><div>Writing Markup with JSX<!-- --> </div></a></li><li><a title="JavaScript in JSX with Curly Braces" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/javascript-in-jsx-with-curly-braces"><div>JavaScript in JSX with Curly Braces<!-- --> </div></a></li><li><a title="Passing Props to a Component" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/passing-props-to-a-component"><div>Passing Props to a Component<!-- --> </div></a></li><li><a title="Conditional Rendering" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/conditional-rendering"><div>Conditional Rendering<!-- --> </div></a></li><li><a title="Rendering Lists" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/rendering-lists"><div>Rendering Lists<!-- --> </div></a></li><li><a title="Keeping Components Pure" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/keeping-components-pure"><div>Keeping Components Pure<!-- --> </div></a></li><li><a title="Your UI as a Tree" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/understanding-your-ui-as-a-tree"><div>Your UI as a Tree<!-- --> </div></a></li></ul></div></div></li><li><a title="Adding Interactivity" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between ps-5 text-base font-bold text-primary dark:text-primary-dark" href="/learn/adding-interactivity"><div>Adding Interactivity<!-- --> </div><span class="pe-1 text-tertiary dark:text-tertiary-dark"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" class="duration-100 ease-in transition -rotate-90 rtl:rotate-90" style="min-width:20px;min-height:20px"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg></span></a><div class="opacity-50" style="transition:opacity 250ms ease-in-out"><div id="react-collapsed-panel-:R9qm6:" aria-hidden="true" role="region" style="box-sizing:border-box;display:none;height:0px;overflow:hidden"><ul><li><a title="Responding to Events" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/responding-to-events"><div>Responding to Events<!-- --> </div></a></li><li><a title="State: A Component's Memory" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/state-a-components-memory"><div>State: A Component's Memory<!-- --> </div></a></li><li><a title="Render and Commit" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/render-and-commit"><div>Render and Commit<!-- --> </div></a></li><li><a title="State as a Snapshot" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/state-as-a-snapshot"><div>State as a Snapshot<!-- --> </div></a></li><li><a title="Queueing a Series of State Updates" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/queueing-a-series-of-state-updates"><div>Queueing a Series of State Updates<!-- --> </div></a></li><li><a title="Updating Objects in State" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/updating-objects-in-state"><div>Updating Objects in State<!-- --> </div></a></li><li><a title="Updating Arrays in State" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/updating-arrays-in-state"><div>Updating Arrays in State<!-- --> </div></a></li></ul></div></div></li><li><a title="Managing State" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between ps-5 text-base font-bold text-primary dark:text-primary-dark" href="/learn/managing-state"><div>Managing State<!-- --> </div><span class="pe-1 text-tertiary dark:text-tertiary-dark"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" class="duration-100 ease-in transition -rotate-90 rtl:rotate-90" style="min-width:20px;min-height:20px"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg></span></a><div class="opacity-50" style="transition:opacity 250ms ease-in-out"><div id="react-collapsed-panel-:Ra2m6:" aria-hidden="true" role="region" style="box-sizing:border-box;display:none;height:0px;overflow:hidden"><ul><li><a title="Reacting to Input with State" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/reacting-to-input-with-state"><div>Reacting to Input with State<!-- --> </div></a></li><li><a title="Choosing the State Structure" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/choosing-the-state-structure"><div>Choosing the State Structure<!-- --> </div></a></li><li><a title="Sharing State Between Components" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/sharing-state-between-components"><div>Sharing State Between Components<!-- --> </div></a></li><li><a title="Preserving and Resetting State" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/preserving-and-resetting-state"><div>Preserving and Resetting State<!-- --> </div></a></li><li><a title="Extracting State Logic into a Reducer" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/extracting-state-logic-into-a-reducer"><div>Extracting State Logic into a Reducer<!-- --> </div></a></li><li><a title="Passing Data Deeply with Context" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/passing-data-deeply-with-context"><div>Passing Data Deeply with Context<!-- --> </div></a></li><li><a title="Scaling Up with Reducer and Context" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/scaling-up-with-reducer-and-context"><div>Scaling Up with Reducer and Context<!-- --> </div></a></li></ul></div></div></li><li><a title="Escape Hatches" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between ps-5 text-base font-bold text-primary dark:text-primary-dark" href="/learn/escape-hatches"><div>Escape Hatches<!-- --> </div><span class="pe-1 text-link dark:text-link-dark"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" class="duration-100 ease-in transition rotate-0" style="min-width:20px;min-height:20px"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg></span></a><div class="opacity-100" style="transition:opacity 250ms ease-in-out"><div id="react-collapsed-panel-:Raam6:" aria-hidden="false" role="region" style="box-sizing:border-box"><ul><li><a title="Referencing Values with Refs" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/referencing-values-with-refs"><div>Referencing Values with Refs<!-- --> </div></a></li><li><a title="Manipulating the DOM with Refs" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/manipulating-the-dom-with-refs"><div>Manipulating the DOM with Refs<!-- --> </div></a></li><li><a title="Synchronizing with Effects" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/synchronizing-with-effects"><div>Synchronizing with Effects<!-- --> </div></a></li><li><a title="You Might Not Need an Effect" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/you-might-not-need-an-effect"><div>You Might Not Need an Effect<!-- --> </div></a></li><li><a title="Lifecycle of Reactive Effects" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/lifecycle-of-reactive-effects"><div>Lifecycle of Reactive Effects<!-- --> </div></a></li><li><a title="Separating Events from Effects" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/separating-events-from-effects"><div>Separating Events from Effects<!-- --> </div></a></li><li><a title="Removing Effect Dependencies" target="" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-secondary dark:text-secondary-dark" href="/learn/removing-effect-dependencies"><div>Removing Effect Dependencies<!-- --> </div></a></li><li><a title="Reusing Logic with Custom Hooks" target="" aria-current="page" class="p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between text-sm ps-6 ps-5 text-base text-link dark:text-link-dark bg-highlight dark:bg-highlight-dark border-blue-40 hover:bg-highlight hover:text-link dark:hover:bg-highlight-dark dark:hover:text-link-dark" href="/learn/reusing-logic-with-custom-hooks"><div>Reusing Logic with Custom Hooks<!-- --> </div></a></li></ul></div></div></li></ul><!--/$--><div class="h-20"></div></nav><div class="fixed bottom-0 hidden lg:block"><div class="max-w-custom-xs w-80 lg:w-auto py-3 shadow-lg rounded-lg m-4 bg-wash dark:bg-gray-95 px-4 flex"><p class="w-full text-lg font-bold text-primary dark:text-primary-dark me-4">Is this page useful?</p><button aria-label="Yes" class="px-3 rounded-lg bg-secondary-button dark:bg-secondary-button-dark text-primary dark:text-primary-dark me-2"><svg width="16" height="18" viewBox="0 0 16 18" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M9.36603 0.384603C9.36605 0.384617 9.36601 0.384588 9.36603 0.384603L9.45902 0.453415C9.99732 0.851783 10.3873 1.42386 10.5654 2.07648C10.7435 2.72909 10.6993 3.42385 10.44 4.04763L9.27065 6.86008H12.6316C13.5249 6.86008 14.3817 7.22121 15.0134 7.86402C15.6451 8.50683 16 9.37868 16 10.2877V13.7154C16 14.8518 15.5564 15.9416 14.7668 16.7451C13.9771 17.5486 12.9062 18 11.7895 18H5.05263C3.71259 18 2.42743 17.4583 1.47988 16.4941C0.532325 15.5299 0 14.2221 0 12.8585V11.2511C2.40928e-06 9.87711 0.463526 8.54479 1.31308 7.47688L6.66804 0.745592C6.98662 0.345136 7.44414 0.08434 7.94623 0.0171605C8.4483 -0.0500155 8.95656 0.0815891 9.36603 0.384603ZM8.37542 1.77064C8.31492 1.72587 8.23987 1.70646 8.16579 1.71637C8.09171 1.72628 8.02415 1.76477 7.97708 1.82393L2.62213 8.55522C2.0153 9.31801 1.68421 10.2697 1.68421 11.2511V12.8585C1.68421 13.7676 2.03909 14.6394 2.67079 15.2822C3.30249 15.925 4.15927 16.2862 5.05263 16.2862H11.7895C12.4595 16.2862 13.1021 16.0153 13.5759 15.5332C14.0496 15.0511 14.3158 14.3972 14.3158 13.7154V10.2877C14.3158 9.83321 14.1383 9.39729 13.8225 9.07588C13.5066 8.75448 13.0783 8.57392 12.6316 8.57392H8C7.71763 8.57392 7.45405 8.4299 7.29806 8.19039C7.14206 7.95087 7.11442 7.64774 7.22445 7.38311L8.88886 3.37986C9 3.11253 9.01896 2.81477 8.94262 2.53507C8.8663 2.25541 8.69921 2.01027 8.46853 1.83954L8.37542 1.77064Z" fill="currentColor"></path></svg></button><button aria-label="No" class="px-3 rounded-lg bg-secondary-button dark:bg-secondary-button-dark text-primary dark:text-primary-dark"><svg width="16" height="18" viewBox="0 0 16 18" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M6.63397 17.6154C6.63395 17.6154 6.63399 17.6154 6.63397 17.6154L6.54098 17.5466C6.00268 17.1482 5.61269 16.5761 5.43458 15.9235C5.25648 15.2709 5.30069 14.5761 5.56004 13.9524L6.72935 11.1399L3.36842 11.1399C2.47506 11.1399 1.61829 10.7788 0.986585 10.136C0.354883 9.49316 8.1991e-07 8.62132 8.99384e-07 7.71225L1.19904e-06 4.28458C1.29838e-06 3.14824 0.443605 2.05844 1.23323 1.25492C2.02286 0.451403 3.09383 -1.12829e-06 4.21053 -1.03067e-06L10.9474 -4.41715e-07C12.2874 -3.24565e-07 13.5726 0.541687 14.5201 1.50591C15.4677 2.47013 16 3.77789 16 5.1415L16 6.74893C16 8.12289 15.5365 9.45521 14.6869 10.5231L9.33196 17.2544C9.01338 17.6549 8.55586 17.9157 8.05377 17.9828C7.5517 18.05 7.04344 17.9184 6.63397 17.6154ZM7.62458 16.2294C7.68508 16.2741 7.76013 16.2935 7.83421 16.2836C7.90829 16.2737 7.97585 16.2352 8.02292 16.1761L13.3779 9.44478C13.9847 8.68199 14.3158 7.73033 14.3158 6.74892L14.3158 5.1415C14.3158 4.23242 13.9609 3.36058 13.3292 2.71777C12.6975 2.07496 11.8407 1.71383 10.9474 1.71383L4.21053 1.71383C3.5405 1.71383 2.89793 1.98468 2.42415 2.46679C1.95038 2.94889 1.68421 3.60277 1.68421 4.28458L1.68421 7.71225C1.68421 8.16679 1.86166 8.60271 2.1775 8.92411C2.49335 9.24552 2.92174 9.42608 3.36842 9.42608L8 9.42608C8.28237 9.42608 8.54595 9.5701 8.70195 9.80961C8.85794 10.0491 8.88558 10.3523 8.77555 10.6169L7.11114 14.6201C7 14.8875 6.98105 15.1852 7.05738 15.4649C7.1337 15.7446 7.30079 15.9897 7.53147 16.1605L7.62458 16.2294Z" fill="currentColor"></path></svg></button></div></div></aside></div></div></div></div><!--$--><main class="min-w-0 isolate"><article class="font-normal break-words text-primary dark:text-primary-dark"><div class="ps-0"><div class=""><div class="px-5 sm:px-12 pt-3.5"><div class="max-w-4xl ms-0 2xl:mx-auto"><div class="flex flex-wrap"><div class="flex mb-3 mt-0.5 items-center"><a class="text-link dark:text-link-dark text-sm tracking-wide font-bold uppercase me-1 hover:underline" href="/learn">Learn React</a><span class="inline-block me-1 text-link dark:text-link-dark text-lg rtl:rotate-180"><svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6.86612 13.6161C6.37796 14.1043 6.37796 14.8957 6.86612 15.3839C7.35427 15.872 8.14572 15.872 8.63388 15.3839L13.1339 10.8839C13.622 10.3957 13.622 9.60428 13.1339 9.11612L8.63388 4.61612C8.14572 4.12797 7.35427 4.12797 6.86612 4.61612C6.37796 5.10428 6.37796 5.89573 6.86612 6.38388L10.4822 10L6.86612 13.6161Z" fill="currentColor"></path></svg></span></div><div class="flex mb-3 mt-0.5 items-center"><a class="text-link dark:text-link-dark text-sm tracking-wide font-bold uppercase me-1 hover:underline" href="/learn/escape-hatches">Escape Hatches</a><span class="inline-block me-1 text-link dark:text-link-dark text-lg rtl:rotate-180"><svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6.86612 13.6161C6.37796 14.1043 6.37796 14.8957 6.86612 15.3839C7.35427 15.872 8.14572 15.872 8.63388 15.3839L13.1339 10.8839C13.622 10.3957 13.622 9.60428 13.1339 9.11612L8.63388 4.61612C8.14572 4.12797 7.35427 4.12797 6.86612 4.61612C6.37796 5.10428 6.37796 5.89573 6.86612 6.38388L10.4822 10L6.86612 13.6161Z" fill="currentColor"></path></svg></span></div></div><h1 class="mdx-heading mt-0 text-primary dark:text-primary-dark -mx-.5 break-words text-5xl font-display font-bold leading-tight">Reusing Logic with Custom Hooks<a href="#undefined" aria-label="Link for this heading" title="Link for this heading" class="mdx-header-anchor hidden"><svg width="1em" height="1em" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="text-gray-70 ms-2 h-5 w-5"><g fill="currentColor" fill-rule="evenodd"><path d="M7.778 7.975a2.5 2.5 0 0 0 .347-3.837L6.017 2.03a2.498 2.498 0 0 0-3.542-.007 2.5 2.5 0 0 0 .006 3.543l1.153 1.15c.07-.29.154-.563.25-.773.036-.077.084-.16.14-.25L3.18 4.85a1.496 1.496 0 0 1 .002-2.12 1.496 1.496 0 0 1 2.12 0l2.124 2.123a1.496 1.496 0 0 1-.333 2.37c.16.246.42.504.685.752z"></path><path d="M5.657 4.557a2.5 2.5 0 0 0-.347 3.837l2.108 2.108a2.498 2.498 0 0 0 3.542.007 2.5 2.5 0 0 0-.006-3.543L9.802 5.815c-.07.29-.154.565-.25.774-.036.076-.084.16-.14.25l.842.84c.585.587.59 1.532 0 2.122-.587.585-1.532.59-2.12 0L6.008 7.68a1.496 1.496 0 0 1 .332-2.372c-.16-.245-.42-.503-.685-.75z"></path></g></svg></a></h1></div></div></div><div class="px-5 sm:px-12"><div class="max-w-7xl mx-auto"><div class="max-w-4xl ms-0 2xl:mx-auto"><div class="font-display text-xl text-primary dark:text-primary-dark leading-relaxed"><p class="whitespace-pre-wrap my-4">React comes with several built-in Hooks like <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useState</code>, <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useContext</code>, and <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useEffect</code>. Sometimes, you’ll wish that there was a Hook for some more specific purpose: for example, to fetch data, to keep track of whether the user is online, or to connect to a chat room. You might not find these Hooks in React, but you can create your own Hooks for your application’s needs.</p></div> <div class="p-6 xl:p-8 pb-4 xl:pb-6 bg-card dark:bg-card-dark rounded-2xl shadow-inner-border dark:shadow-inner-border-dark text-base text-secondary dark:text-secondary-dark my-8"><h3 class="mdx-heading text-primary dark:text-primary-dark mt-0 mb-3 leading-tight text-2xl font-display leading-9 text-primary dark:text-primary-dark font-bold my-6">You will learn</h3><ul class="ms-6 my-3 list-disc"> <li class="leading-relaxed mb-1">What custom Hooks are, and how to write your own</li> <li class="leading-relaxed mb-1">How to reuse logic between components</li> <li class="leading-relaxed mb-1">How to name and structure your custom Hooks</li> <li class="leading-relaxed mb-1">When and why to extract custom Hooks</li> </ul></div> <h2 id="custom-hooks-sharing-logic-between-components" class="mdx-heading text-3xl font-display leading-10 text-primary dark:text-primary-dark font-bold my-6">Custom Hooks: Sharing logic between components <a href="#custom-hooks-sharing-logic-between-components" aria-label="Link for Custom Hooks: Sharing logic between components " title="Link for Custom Hooks: Sharing logic between components " class="mdx-header-anchor inline-block"><svg width="1em" height="1em" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="text-gray-70 ms-2 h-5 w-5"><g fill="currentColor" fill-rule="evenodd"><path d="M7.778 7.975a2.5 2.5 0 0 0 .347-3.837L6.017 2.03a2.498 2.498 0 0 0-3.542-.007 2.5 2.5 0 0 0 .006 3.543l1.153 1.15c.07-.29.154-.563.25-.773.036-.077.084-.16.14-.25L3.18 4.85a1.496 1.496 0 0 1 .002-2.12 1.496 1.496 0 0 1 2.12 0l2.124 2.123a1.496 1.496 0 0 1-.333 2.37c.16.246.42.504.685.752z"></path><path d="M5.657 4.557a2.5 2.5 0 0 0-.347 3.837l2.108 2.108a2.498 2.498 0 0 0 3.542.007 2.5 2.5 0 0 0-.006-3.543L9.802 5.815c-.07.29-.154.565-.25.774-.036.076-.084.16-.14.25l.842.84c.585.587.59 1.532 0 2.122-.587.585-1.532.59-2.12 0L6.008 7.68a1.496 1.496 0 0 1 .332-2.372c-.16-.245-.42-.503-.685-.75z"></path></g></svg></a></h2> <p class="whitespace-pre-wrap my-4">Imagine you’re developing an app that heavily relies on the network (as most apps do). You want to warn the user if their network connection has accidentally gone off while they were using your app. How would you go about it? It seems like you’ll need two things in your component:</p> <ol class="ms-6 my-3 list-decimal"> <li class="leading-relaxed mb-1">A piece of state that tracks whether the network is online.</li> <li class="leading-relaxed mb-1">An Effect that subscribes to the global <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/online_event" target="_blank" rel="nofollow noopener noreferrer" class="inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal"><code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">online</code></a> and <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/offline_event" target="_blank" rel="nofollow noopener noreferrer" class="inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal"><code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">offline</code></a> events, and updates that state.</li> </ol> <p class="whitespace-pre-wrap my-4">This will keep your component <a class="inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal" href="/learn/synchronizing-with-effects">synchronized</a> with the network status. You might start with something like this:</p> </div><!--$--><div class="sandpack sandpack--playground w-full my-8" dir="ltr"><div class="sp-wrapper"><div class="shadow-lg dark:shadow-lg-dark rounded-lg" style="contain:content"><div class="bg-wash dark:bg-card-dark flex justify-between items-center relative z-10 border-b border-border dark:border-border-dark rounded-t-lg text-lg"><div class="flex-1 grow min-w-0 px-4 lg:px-6"><div><div class="relative overflow-hidden"><div class="w-[fit-content] invisible"><div class=" sp-tabs" translate="no"><div aria-label="Select active file" class=" sp-tabs-scrollable-container" role="tablist"><button aria-selected="true" class=" sp-tab-button" data-active="true" role="tab" title="/src/App.js" type="button">App.js</button></div></div></div><button class="absolute top-0 start-[2px]" id="headlessui-listbox-button-:R16a2cq6:" aria-haspopup="true" aria-expanded="false" data-headlessui-state=""><span class="h-full py-2 px-1 mt-px -mb-px flex border-b text-link dark:text-link-dark border-link dark:border-link-dark items-center text-md leading-tight truncate" style="max-width:160px">App.js</span></button></div></div></div><div class="px-3 flex items-center justify-end text-start" translate="yes"><button class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1" title="Reset Sandbox" type="button"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative"><path d="M13.8982 5.20844C12.4626 4.88688 10.9686 4.93769 9.55821 5.35604L11.8524 3.06184C11.8989 3.0154 11.9357 2.96028 11.9608 2.89961C11.986 2.83894 11.9989 2.77391 11.9989 2.70824C11.9989 2.64256 11.986 2.57754 11.9608 2.51686C11.9357 2.45619 11.8989 2.40107 11.8524 2.35464L11.1456 1.64784C11.0992 1.60139 11.0441 1.56455 10.9834 1.53942C10.9227 1.51428 10.8577 1.50134 10.792 1.50134C10.7263 1.50134 10.6613 1.51428 10.6006 1.53942C10.54 1.56455 10.4848 1.60139 10.4384 1.64784L6.14571 5.94054C6.00654 6.07969 5.89615 6.2449 5.82083 6.42673C5.74551 6.60855 5.70675 6.80343 5.70675 7.00024C5.70675 7.19704 5.74551 7.39192 5.82083 7.57374C5.89615 7.75557 6.00654 7.92078 6.14571 8.05994L10.4387 12.3529C10.5325 12.4465 10.6595 12.4991 10.792 12.4991C10.9245 12.4991 11.0516 12.4465 11.1453 12.3529L11.8527 11.6455C11.9463 11.5518 11.9989 11.4247 11.9989 11.2922C11.9989 11.1598 11.9463 11.0327 11.8527 10.9389L8.77481 7.86104C9.99795 7.16236 11.415 6.8801 12.8125 7.05678C14.21 7.23347 15.5122 7.85953 16.523 8.84064C17.5338 9.82176 18.1983 11.1048 18.4165 12.4964C18.6347 13.888 18.3947 15.3129 17.7328 16.5562C17.0708 17.7996 16.0227 18.7942 14.7463 19.3902C13.47 19.9861 12.0345 20.1511 10.6563 19.8603C9.27798 19.5695 8.03152 18.8387 7.10469 17.778C6.17786 16.7172 5.62086 15.384 5.51761 13.9791C5.51156 13.8512 5.45689 13.7303 5.36477 13.6413C5.27265 13.5522 5.15001 13.5017 5.02191 13.5H4.02081C3.95297 13.4996 3.88574 13.5129 3.8232 13.5392C3.76065 13.5655 3.70408 13.6042 3.6569 13.6529C3.60972 13.7017 3.57291 13.7595 3.54869 13.8228C3.52448 13.8862 3.51336 13.9538 3.51601 14.0216C3.61349 15.5965 4.1473 17.1132 5.0577 18.4019C5.9681 19.6906 7.21917 20.7006 8.6709 21.3188C10.1226 21.937 11.7178 22.139 13.2778 21.9022C14.8378 21.6654 16.3011 20.9992 17.504 19.978C18.7069 18.9569 19.6019 17.6212 20.0889 16.1203C20.5759 14.6195 20.6356 13.0128 20.2614 11.4799C19.8872 9.94705 19.0938 8.54858 17.97 7.44098C16.8462 6.33339 15.4363 5.56037 13.8982 5.20844V5.20844Z" fill="currentColor"></path></svg> Reset</button><a href="https://codesandbox.io/api/v1/sandboxes/define?undefined&environment=create-react-app" rel="noreferrer noopener" target="_blank" title="Open in CodeSandbox" class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1 ms-2 md:ms-1"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative top-[1px]"><path d="M20.5001 2H15.5001C15.3675 2 15.2403 2.05268 15.1465 2.14645C15.0528 2.24021 15.0001 2.36739 15.0001 2.5V3.5C15.0001 3.63261 15.0528 3.75979 15.1465 3.85355C15.2403 3.94732 15.3675 4 15.5001 4H18.5901L7.6501 14.94C7.60323 14.9865 7.56604 15.0418 7.54065 15.1027C7.51527 15.1636 7.5022 15.229 7.5022 15.295C7.5022 15.361 7.51527 15.4264 7.54065 15.4873C7.56604 15.5482 7.60323 15.6035 7.6501 15.65L8.3501 16.35C8.39658 16.3969 8.45188 16.4341 8.51281 16.4594C8.57374 16.4848 8.63909 16.4979 8.7051 16.4979C8.7711 16.4979 8.83646 16.4848 8.89738 16.4594C8.95831 16.4341 9.01362 16.3969 9.0601 16.35L20.0001 5.41V8.5C20.0001 8.63261 20.0528 8.75979 20.1465 8.85355C20.2403 8.94732 20.3675 9 20.5001 9H21.5001C21.6327 9 21.7599 8.94732 21.8537 8.85355C21.9474 8.75979 22.0001 8.63261 22.0001 8.5V3.5C22.0001 3.10218 21.8421 2.72064 21.5608 2.43934C21.2795 2.15804 20.8979 2 20.5001 2V2Z" fill="currentColor"></path><path d="M21.5 13H20.5C20.3674 13 20.2402 13.0527 20.1464 13.1464C20.0527 13.2402 20 13.3674 20 13.5V20H4V4H10.5C10.6326 4 10.7598 3.94732 10.8536 3.85355C10.9473 3.75979 11 3.63261 11 3.5V2.5C11 2.36739 10.9473 2.24021 10.8536 2.14645C10.7598 2.05268 10.6326 2 10.5 2H3.5C3.10218 2 2.72064 2.15804 2.43934 2.43934C2.15804 2.72064 2 3.10218 2 3.5V20.5C2 20.8978 2.15804 21.2794 2.43934 21.5607C2.72064 21.842 3.10218 22 3.5 22H20.5C20.8978 22 21.2794 21.842 21.5607 21.5607C21.842 21.2794 22 20.8978 22 20.5V13.5C22 13.3674 21.9473 13.2402 21.8536 13.1464C21.7598 13.0527 21.6326 13 21.5 13Z" fill="currentColor"></path></svg><span class="hidden md:block">Fork</span></a></div></div><div class=" sp-layout"><div class=" sp-editor sp-stack"><div class=" sp-code-editor"><div aria-autocomplete="list" aria-label="Code Editor for App.js" aria-multiline="true" class="sp-pristine sp-javascript sp-cm" role="textbox" tabindex="0" translate="no"><pre class=" sp-pre-placeholder" style="margin-left:var(--sp-space-11)"><span class="sp-syntax-keyword">import</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">useState</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">useEffect</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">from</span> <span class="sp-syntax-string">'react'</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">export</span> <span class="sp-syntax-keyword">default</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">StatusBar</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">isOnline</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setIsOnline</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-static">true</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-definition">useEffect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">handleOnline</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-definition">setIsOnline</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-static">true</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">handleOffline</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-definition">setIsOnline</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-static">false</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-plain">window</span>.<span class="sp-syntax-property">addEventListener</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'online'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">handleOnline</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-plain">window</span>.<span class="sp-syntax-property">addEventListener</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'offline'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">handleOffline</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">window</span>.<span class="sp-syntax-property">removeEventListener</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'online'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">handleOnline</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-plain">window</span>.<span class="sp-syntax-property">removeEventListener</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'offline'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">handleOffline</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-punctuation">]</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">h1</span><span class="sp-syntax-punctuation">></span><span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">isOnline</span> ? <span class="sp-syntax-string">'✅ Online'</span> : <span class="sp-syntax-string">'❌ Disconnected'</span><span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation"></</span><span class="sp-syntax-tag">h1</span><span class="sp-syntax-punctuation">></span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> </pre></div></div></div><div class=" order-last xl:order-2 sp-stack"><div class="p-0 sm:p-2 md:p-4 lg:p-8 bg-card dark:bg-wash-dark h-full relative md:rounded-b-lg lg:rounded-b-none"><div style="position:relative"><iframe class="rounded-t-none bg-white md:shadow-md sm:rounded-lg w-full max-w-full transition-opacity absolute opacity-0 pointer-events-none duration-75" title="Sandbox Preview" style="height:15px;z-index:-1"></iframe></div></div></div><button translate="yes" class="sandpack-expand flex text-base justify-between dark:border-card-dark bg-wash dark:bg-card-dark items-center z-10 p-1 w-full order-2 xl:order-last border-b-1 relative top-0"><span class="flex p-2 focus:outline-none text-primary dark:text-primary-dark leading-[20px]"><svg class="rotate-0 inline me-1.5 text-xl" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg>Show more</span></button></div></div></div></div><!--/$--><div class="max-w-4xl ms-0 2xl:mx-auto"> <p class="whitespace-pre-wrap my-4">Try turning your network on and off, and notice how this <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">StatusBar</code> updates in response to your actions.</p> <p class="whitespace-pre-wrap my-4">Now imagine you <em>also</em> want to use the same logic in a different component. You want to implement a Save button that will become disabled and show “Reconnecting…” instead of “Save” while the network is off.</p> <p class="whitespace-pre-wrap my-4">To start, you can copy and paste the <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">isOnline</code> state and the Effect into <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">SaveButton</code>:</p> </div><!--$--><div class="sandpack sandpack--playground w-full my-8" dir="ltr"><div class="sp-wrapper"><div class="shadow-lg dark:shadow-lg-dark rounded-lg" style="contain:content"><div class="bg-wash dark:bg-card-dark flex justify-between items-center relative z-10 border-b border-border dark:border-border-dark rounded-t-lg text-lg"><div class="flex-1 grow min-w-0 px-4 lg:px-6"><div><div class="relative overflow-hidden"><div class="w-[fit-content] invisible"><div class=" sp-tabs" translate="no"><div aria-label="Select active file" class=" sp-tabs-scrollable-container" role="tablist"><button aria-selected="true" class=" sp-tab-button" data-active="true" role="tab" title="/src/App.js" type="button">App.js</button></div></div></div><button class="absolute top-0 start-[2px]" id="headlessui-listbox-button-:R16a4cq6:" aria-haspopup="true" aria-expanded="false" data-headlessui-state=""><span class="h-full py-2 px-1 mt-px -mb-px flex border-b text-link dark:text-link-dark border-link dark:border-link-dark items-center text-md leading-tight truncate" style="max-width:160px">App.js</span></button></div></div></div><div class="px-3 flex items-center justify-end text-start" translate="yes"><button class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1" title="Reset Sandbox" type="button"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative"><path d="M13.8982 5.20844C12.4626 4.88688 10.9686 4.93769 9.55821 5.35604L11.8524 3.06184C11.8989 3.0154 11.9357 2.96028 11.9608 2.89961C11.986 2.83894 11.9989 2.77391 11.9989 2.70824C11.9989 2.64256 11.986 2.57754 11.9608 2.51686C11.9357 2.45619 11.8989 2.40107 11.8524 2.35464L11.1456 1.64784C11.0992 1.60139 11.0441 1.56455 10.9834 1.53942C10.9227 1.51428 10.8577 1.50134 10.792 1.50134C10.7263 1.50134 10.6613 1.51428 10.6006 1.53942C10.54 1.56455 10.4848 1.60139 10.4384 1.64784L6.14571 5.94054C6.00654 6.07969 5.89615 6.2449 5.82083 6.42673C5.74551 6.60855 5.70675 6.80343 5.70675 7.00024C5.70675 7.19704 5.74551 7.39192 5.82083 7.57374C5.89615 7.75557 6.00654 7.92078 6.14571 8.05994L10.4387 12.3529C10.5325 12.4465 10.6595 12.4991 10.792 12.4991C10.9245 12.4991 11.0516 12.4465 11.1453 12.3529L11.8527 11.6455C11.9463 11.5518 11.9989 11.4247 11.9989 11.2922C11.9989 11.1598 11.9463 11.0327 11.8527 10.9389L8.77481 7.86104C9.99795 7.16236 11.415 6.8801 12.8125 7.05678C14.21 7.23347 15.5122 7.85953 16.523 8.84064C17.5338 9.82176 18.1983 11.1048 18.4165 12.4964C18.6347 13.888 18.3947 15.3129 17.7328 16.5562C17.0708 17.7996 16.0227 18.7942 14.7463 19.3902C13.47 19.9861 12.0345 20.1511 10.6563 19.8603C9.27798 19.5695 8.03152 18.8387 7.10469 17.778C6.17786 16.7172 5.62086 15.384 5.51761 13.9791C5.51156 13.8512 5.45689 13.7303 5.36477 13.6413C5.27265 13.5522 5.15001 13.5017 5.02191 13.5H4.02081C3.95297 13.4996 3.88574 13.5129 3.8232 13.5392C3.76065 13.5655 3.70408 13.6042 3.6569 13.6529C3.60972 13.7017 3.57291 13.7595 3.54869 13.8228C3.52448 13.8862 3.51336 13.9538 3.51601 14.0216C3.61349 15.5965 4.1473 17.1132 5.0577 18.4019C5.9681 19.6906 7.21917 20.7006 8.6709 21.3188C10.1226 21.937 11.7178 22.139 13.2778 21.9022C14.8378 21.6654 16.3011 20.9992 17.504 19.978C18.7069 18.9569 19.6019 17.6212 20.0889 16.1203C20.5759 14.6195 20.6356 13.0128 20.2614 11.4799C19.8872 9.94705 19.0938 8.54858 17.97 7.44098C16.8462 6.33339 15.4363 5.56037 13.8982 5.20844V5.20844Z" fill="currentColor"></path></svg> Reset</button><a href="https://codesandbox.io/api/v1/sandboxes/define?undefined&environment=create-react-app" rel="noreferrer noopener" target="_blank" title="Open in CodeSandbox" class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1 ms-2 md:ms-1"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative top-[1px]"><path d="M20.5001 2H15.5001C15.3675 2 15.2403 2.05268 15.1465 2.14645C15.0528 2.24021 15.0001 2.36739 15.0001 2.5V3.5C15.0001 3.63261 15.0528 3.75979 15.1465 3.85355C15.2403 3.94732 15.3675 4 15.5001 4H18.5901L7.6501 14.94C7.60323 14.9865 7.56604 15.0418 7.54065 15.1027C7.51527 15.1636 7.5022 15.229 7.5022 15.295C7.5022 15.361 7.51527 15.4264 7.54065 15.4873C7.56604 15.5482 7.60323 15.6035 7.6501 15.65L8.3501 16.35C8.39658 16.3969 8.45188 16.4341 8.51281 16.4594C8.57374 16.4848 8.63909 16.4979 8.7051 16.4979C8.7711 16.4979 8.83646 16.4848 8.89738 16.4594C8.95831 16.4341 9.01362 16.3969 9.0601 16.35L20.0001 5.41V8.5C20.0001 8.63261 20.0528 8.75979 20.1465 8.85355C20.2403 8.94732 20.3675 9 20.5001 9H21.5001C21.6327 9 21.7599 8.94732 21.8537 8.85355C21.9474 8.75979 22.0001 8.63261 22.0001 8.5V3.5C22.0001 3.10218 21.8421 2.72064 21.5608 2.43934C21.2795 2.15804 20.8979 2 20.5001 2V2Z" fill="currentColor"></path><path d="M21.5 13H20.5C20.3674 13 20.2402 13.0527 20.1464 13.1464C20.0527 13.2402 20 13.3674 20 13.5V20H4V4H10.5C10.6326 4 10.7598 3.94732 10.8536 3.85355C10.9473 3.75979 11 3.63261 11 3.5V2.5C11 2.36739 10.9473 2.24021 10.8536 2.14645C10.7598 2.05268 10.6326 2 10.5 2H3.5C3.10218 2 2.72064 2.15804 2.43934 2.43934C2.15804 2.72064 2 3.10218 2 3.5V20.5C2 20.8978 2.15804 21.2794 2.43934 21.5607C2.72064 21.842 3.10218 22 3.5 22H20.5C20.8978 22 21.2794 21.842 21.5607 21.5607C21.842 21.2794 22 20.8978 22 20.5V13.5C22 13.3674 21.9473 13.2402 21.8536 13.1464C21.7598 13.0527 21.6326 13 21.5 13Z" fill="currentColor"></path></svg><span class="hidden md:block">Fork</span></a></div></div><div class=" sp-layout"><div class=" sp-editor sp-stack"><div class=" sp-code-editor"><div aria-autocomplete="list" aria-label="Code Editor for App.js" aria-multiline="true" class="sp-pristine sp-javascript sp-cm" role="textbox" tabindex="0" translate="no"><pre class=" sp-pre-placeholder" style="margin-left:var(--sp-space-11)"><span class="sp-syntax-keyword">import</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">useState</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">useEffect</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">from</span> <span class="sp-syntax-string">'react'</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">export</span> <span class="sp-syntax-keyword">default</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">SaveButton</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">isOnline</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setIsOnline</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-static">true</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-definition">useEffect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">handleOnline</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-definition">setIsOnline</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-static">true</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">handleOffline</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-definition">setIsOnline</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-static">false</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-plain">window</span>.<span class="sp-syntax-property">addEventListener</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'online'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">handleOnline</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-plain">window</span>.<span class="sp-syntax-property">addEventListener</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'offline'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">handleOffline</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">window</span>.<span class="sp-syntax-property">removeEventListener</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'online'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">handleOnline</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-plain">window</span>.<span class="sp-syntax-property">removeEventListener</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'offline'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">handleOffline</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-punctuation">]</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">handleSaveClick</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">console</span>.<span class="sp-syntax-property">log</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'✅ Progress saved'</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">button</span> <span class="sp-syntax-property">disabled</span>=<span class="sp-syntax-punctuation">{</span>!<span class="sp-syntax-plain">isOnline</span><span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-property">onClick</span>=<span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">handleSaveClick</span><span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">isOnline</span> ? <span class="sp-syntax-string">'Save progress'</span> : <span class="sp-syntax-string">'Reconnecting...'</span><span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-punctuation"></</span><span class="sp-syntax-tag">button</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> </pre></div></div></div><div class=" order-last xl:order-2 sp-stack"><div class="p-0 sm:p-2 md:p-4 lg:p-8 bg-card dark:bg-wash-dark h-full relative md:rounded-b-lg lg:rounded-b-none"><div style="position:relative"><iframe class="rounded-t-none bg-white md:shadow-md sm:rounded-lg w-full max-w-full transition-opacity absolute opacity-0 pointer-events-none duration-75" title="Sandbox Preview" style="height:15px;z-index:-1"></iframe></div></div></div><button translate="yes" class="sandpack-expand flex text-base justify-between dark:border-card-dark bg-wash dark:bg-card-dark items-center z-10 p-1 w-full order-2 xl:order-last border-b-1 relative top-0"><span class="flex p-2 focus:outline-none text-primary dark:text-primary-dark leading-[20px]"><svg class="rotate-0 inline me-1.5 text-xl" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg>Show more</span></button></div></div></div></div><!--/$--><div class="max-w-4xl ms-0 2xl:mx-auto"> <p class="whitespace-pre-wrap my-4">Verify that, if you turn off the network, the button will change its appearance.</p> <p class="whitespace-pre-wrap my-4">These two components work fine, but the duplication in logic between them is unfortunate. It seems like even though they have different <em>visual appearance,</em> you want to reuse the logic between them.</p> <h3 id="extracting-your-own-custom-hook-from-a-component" class="mdx-heading text-2xl font-display leading-9 text-primary dark:text-primary-dark font-bold my-6">Extracting your own custom Hook from a component <a href="#extracting-your-own-custom-hook-from-a-component" aria-label="Link for Extracting your own custom Hook from a component " title="Link for Extracting your own custom Hook from a component " class="mdx-header-anchor inline-block"><svg width="1em" height="1em" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="text-gray-70 ms-2 h-5 w-5"><g fill="currentColor" fill-rule="evenodd"><path d="M7.778 7.975a2.5 2.5 0 0 0 .347-3.837L6.017 2.03a2.498 2.498 0 0 0-3.542-.007 2.5 2.5 0 0 0 .006 3.543l1.153 1.15c.07-.29.154-.563.25-.773.036-.077.084-.16.14-.25L3.18 4.85a1.496 1.496 0 0 1 .002-2.12 1.496 1.496 0 0 1 2.12 0l2.124 2.123a1.496 1.496 0 0 1-.333 2.37c.16.246.42.504.685.752z"></path><path d="M5.657 4.557a2.5 2.5 0 0 0-.347 3.837l2.108 2.108a2.498 2.498 0 0 0 3.542.007 2.5 2.5 0 0 0-.006-3.543L9.802 5.815c-.07.29-.154.565-.25.774-.036.076-.084.16-.14.25l.842.84c.585.587.59 1.532 0 2.122-.587.585-1.532.59-2.12 0L6.008 7.68a1.496 1.496 0 0 1 .332-2.372c-.16-.245-.42-.503-.685-.75z"></path></g></svg></a></h3> <p class="whitespace-pre-wrap my-4">Imagine for a moment that, similar to <a class="inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal" href="/reference/react/useState"><code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useState</code></a> and <a class="inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal" href="/reference/react/useEffect"><code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useEffect</code></a>, there was a built-in <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useOnlineStatus</code> Hook. Then both of these components could be simplified and you could remove the duplication between them:</p> <!--$--><div dir="ltr" class="sandpack sandpack--codeblock rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg my-8" style="contain:content"><div class="sp-wrapper"><div class="sp-stack"><div class="sp-code-editor"><pre class="sp-cm sp-pristine sp-javascript flex align-start"><code class="sp-pre-placeholder grow-[2]"><div class="cm-line "><span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">StatusBar</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">isOnline</span> = <span class="sp-syntax-definition">useOnlineStatus</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">h1</span><span class="sp-syntax-punctuation">></span><span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">isOnline</span> ? <span class="sp-syntax-string">'✅ Online'</span> : <span class="sp-syntax-string">'❌ Disconnected'</span><span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation"></</span><span class="sp-syntax-tag">h1</span><span class="sp-syntax-punctuation">></span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><span class="sp-syntax-punctuation">}</span><br/></div><div class="cm-line "><br/></div><div class="cm-line "><span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">SaveButton</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">isOnline</span> = <span class="sp-syntax-definition">useOnlineStatus</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">handleSaveClick</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-plain">console</span>.<span class="sp-syntax-property">log</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'✅ Progress saved'</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-punctuation">}</span><br/></div><div class="cm-line "><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span><br/></div><div class="cm-line "> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">button</span> <span class="sp-syntax-property">disabled</span>=<span class="sp-syntax-punctuation">{</span>!<span class="sp-syntax-plain">isOnline</span><span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-property">onClick</span>=<span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">handleSaveClick</span><span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">></span><br/></div><div class="cm-line "> <span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">isOnline</span> ? <span class="sp-syntax-string">'Save progress'</span> : <span class="sp-syntax-string">'Reconnecting...'</span><span class="sp-syntax-punctuation">}</span><br/></div><div class="cm-line "> <span class="sp-syntax-punctuation"></</span><span class="sp-syntax-tag">button</span><span class="sp-syntax-punctuation">></span><br/></div><div class="cm-line "> <span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><span class="sp-syntax-punctuation">}</span></div></code></pre></div></div></div></div><!--/$--> <p class="whitespace-pre-wrap my-4">Although there is no such built-in Hook, you can write it yourself. Declare a function called <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useOnlineStatus</code> and move all the duplicated code into it from the components you wrote earlier:</p> <!--$--><div dir="ltr" class="sandpack sandpack--codeblock rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg my-8" style="contain:content"><div class="sp-wrapper"><div class="sp-stack"><div class="sp-code-editor"><pre class="sp-cm sp-pristine sp-javascript flex align-start"><code class="sp-pre-placeholder grow-[2]"><div class="cm-line "><span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">useOnlineStatus</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">isOnline</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setIsOnline</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-static">true</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-definition">useEffect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">handleOnline</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-definition">setIsOnline</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-static">true</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">handleOffline</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-definition">setIsOnline</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-static">false</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-plain">window</span>.<span class="sp-syntax-property">addEventListener</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'online'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">handleOnline</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-plain">window</span>.<span class="sp-syntax-property">addEventListener</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'offline'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">handleOffline</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-plain">window</span>.<span class="sp-syntax-property">removeEventListener</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'online'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">handleOnline</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-plain">window</span>.<span class="sp-syntax-property">removeEventListener</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'offline'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">handleOffline</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-punctuation">]</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-plain">isOnline</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><span class="sp-syntax-punctuation">}</span></div></code></pre></div></div></div></div><!--/$--> <p class="whitespace-pre-wrap my-4">At the end of the function, return <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">isOnline</code>. This lets your components read that value:</p> </div><!--$--><div class="sandpack sandpack--playground w-full my-8" dir="ltr"><div class="sp-wrapper"><div class="shadow-lg dark:shadow-lg-dark rounded-lg" style="contain:content"><div class="bg-wash dark:bg-card-dark flex justify-between items-center relative z-10 border-b border-border dark:border-border-dark rounded-t-lg text-lg"><div class="flex-1 grow min-w-0 px-4 lg:px-6"><div><div class="relative overflow-hidden"><div class="w-[fit-content] invisible"><div class=" sp-tabs" translate="no"><div aria-label="Select active file" class=" sp-tabs-scrollable-container" role="tablist"><button aria-selected="true" class=" sp-tab-button" data-active="true" role="tab" title="/src/App.js" type="button">App.js</button><button aria-selected="false" class=" sp-tab-button" data-active="false" role="tab" title="/src/useOnlineStatus.js" type="button">useOnlineStatus.js</button></div></div></div><button class="absolute top-0 start-[2px]" id="headlessui-listbox-button-:R16a6cq6:" aria-haspopup="true" aria-expanded="false" data-headlessui-state=""><span class="h-full py-2 px-1 mt-px -mb-px flex border-b text-link dark:text-link-dark border-link dark:border-link-dark items-center text-md leading-tight truncate" style="max-width:160px">App.js<span class="ms-2"><svg class="rotate-0" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg></span></span></button></div></div></div><div class="px-3 flex items-center justify-end text-start" translate="yes"><button class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1" title="Reset Sandbox" type="button"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative"><path d="M13.8982 5.20844C12.4626 4.88688 10.9686 4.93769 9.55821 5.35604L11.8524 3.06184C11.8989 3.0154 11.9357 2.96028 11.9608 2.89961C11.986 2.83894 11.9989 2.77391 11.9989 2.70824C11.9989 2.64256 11.986 2.57754 11.9608 2.51686C11.9357 2.45619 11.8989 2.40107 11.8524 2.35464L11.1456 1.64784C11.0992 1.60139 11.0441 1.56455 10.9834 1.53942C10.9227 1.51428 10.8577 1.50134 10.792 1.50134C10.7263 1.50134 10.6613 1.51428 10.6006 1.53942C10.54 1.56455 10.4848 1.60139 10.4384 1.64784L6.14571 5.94054C6.00654 6.07969 5.89615 6.2449 5.82083 6.42673C5.74551 6.60855 5.70675 6.80343 5.70675 7.00024C5.70675 7.19704 5.74551 7.39192 5.82083 7.57374C5.89615 7.75557 6.00654 7.92078 6.14571 8.05994L10.4387 12.3529C10.5325 12.4465 10.6595 12.4991 10.792 12.4991C10.9245 12.4991 11.0516 12.4465 11.1453 12.3529L11.8527 11.6455C11.9463 11.5518 11.9989 11.4247 11.9989 11.2922C11.9989 11.1598 11.9463 11.0327 11.8527 10.9389L8.77481 7.86104C9.99795 7.16236 11.415 6.8801 12.8125 7.05678C14.21 7.23347 15.5122 7.85953 16.523 8.84064C17.5338 9.82176 18.1983 11.1048 18.4165 12.4964C18.6347 13.888 18.3947 15.3129 17.7328 16.5562C17.0708 17.7996 16.0227 18.7942 14.7463 19.3902C13.47 19.9861 12.0345 20.1511 10.6563 19.8603C9.27798 19.5695 8.03152 18.8387 7.10469 17.778C6.17786 16.7172 5.62086 15.384 5.51761 13.9791C5.51156 13.8512 5.45689 13.7303 5.36477 13.6413C5.27265 13.5522 5.15001 13.5017 5.02191 13.5H4.02081C3.95297 13.4996 3.88574 13.5129 3.8232 13.5392C3.76065 13.5655 3.70408 13.6042 3.6569 13.6529C3.60972 13.7017 3.57291 13.7595 3.54869 13.8228C3.52448 13.8862 3.51336 13.9538 3.51601 14.0216C3.61349 15.5965 4.1473 17.1132 5.0577 18.4019C5.9681 19.6906 7.21917 20.7006 8.6709 21.3188C10.1226 21.937 11.7178 22.139 13.2778 21.9022C14.8378 21.6654 16.3011 20.9992 17.504 19.978C18.7069 18.9569 19.6019 17.6212 20.0889 16.1203C20.5759 14.6195 20.6356 13.0128 20.2614 11.4799C19.8872 9.94705 19.0938 8.54858 17.97 7.44098C16.8462 6.33339 15.4363 5.56037 13.8982 5.20844V5.20844Z" fill="currentColor"></path></svg> Reset</button><a href="https://codesandbox.io/api/v1/sandboxes/define?undefined&environment=create-react-app" rel="noreferrer noopener" target="_blank" title="Open in CodeSandbox" class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1 ms-2 md:ms-1"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative top-[1px]"><path d="M20.5001 2H15.5001C15.3675 2 15.2403 2.05268 15.1465 2.14645C15.0528 2.24021 15.0001 2.36739 15.0001 2.5V3.5C15.0001 3.63261 15.0528 3.75979 15.1465 3.85355C15.2403 3.94732 15.3675 4 15.5001 4H18.5901L7.6501 14.94C7.60323 14.9865 7.56604 15.0418 7.54065 15.1027C7.51527 15.1636 7.5022 15.229 7.5022 15.295C7.5022 15.361 7.51527 15.4264 7.54065 15.4873C7.56604 15.5482 7.60323 15.6035 7.6501 15.65L8.3501 16.35C8.39658 16.3969 8.45188 16.4341 8.51281 16.4594C8.57374 16.4848 8.63909 16.4979 8.7051 16.4979C8.7711 16.4979 8.83646 16.4848 8.89738 16.4594C8.95831 16.4341 9.01362 16.3969 9.0601 16.35L20.0001 5.41V8.5C20.0001 8.63261 20.0528 8.75979 20.1465 8.85355C20.2403 8.94732 20.3675 9 20.5001 9H21.5001C21.6327 9 21.7599 8.94732 21.8537 8.85355C21.9474 8.75979 22.0001 8.63261 22.0001 8.5V3.5C22.0001 3.10218 21.8421 2.72064 21.5608 2.43934C21.2795 2.15804 20.8979 2 20.5001 2V2Z" fill="currentColor"></path><path d="M21.5 13H20.5C20.3674 13 20.2402 13.0527 20.1464 13.1464C20.0527 13.2402 20 13.3674 20 13.5V20H4V4H10.5C10.6326 4 10.7598 3.94732 10.8536 3.85355C10.9473 3.75979 11 3.63261 11 3.5V2.5C11 2.36739 10.9473 2.24021 10.8536 2.14645C10.7598 2.05268 10.6326 2 10.5 2H3.5C3.10218 2 2.72064 2.15804 2.43934 2.43934C2.15804 2.72064 2 3.10218 2 3.5V20.5C2 20.8978 2.15804 21.2794 2.43934 21.5607C2.72064 21.842 3.10218 22 3.5 22H20.5C20.8978 22 21.2794 21.842 21.5607 21.5607C21.842 21.2794 22 20.8978 22 20.5V13.5C22 13.3674 21.9473 13.2402 21.8536 13.1464C21.7598 13.0527 21.6326 13 21.5 13Z" fill="currentColor"></path></svg><span class="hidden md:block">Fork</span></a></div></div><div class=" sp-layout"><div class=" sp-editor sp-stack"><div class=" sp-code-editor"><div aria-autocomplete="list" aria-label="Code Editor for App.js" aria-multiline="true" class="sp-pristine sp-javascript sp-cm" role="textbox" tabindex="0" translate="no"><pre class=" sp-pre-placeholder" style="margin-left:var(--sp-space-11)"><span class="sp-syntax-keyword">import</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">useOnlineStatus</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">from</span> <span class="sp-syntax-string">'./useOnlineStatus.js'</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">StatusBar</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">isOnline</span> = <span class="sp-syntax-definition">useOnlineStatus</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">h1</span><span class="sp-syntax-punctuation">></span><span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">isOnline</span> ? <span class="sp-syntax-string">'✅ Online'</span> : <span class="sp-syntax-string">'❌ Disconnected'</span><span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation"></</span><span class="sp-syntax-tag">h1</span><span class="sp-syntax-punctuation">></span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">SaveButton</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">isOnline</span> = <span class="sp-syntax-definition">useOnlineStatus</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">handleSaveClick</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">console</span>.<span class="sp-syntax-property">log</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'✅ Progress saved'</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">button</span> <span class="sp-syntax-property">disabled</span>=<span class="sp-syntax-punctuation">{</span>!<span class="sp-syntax-plain">isOnline</span><span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-property">onClick</span>=<span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">handleSaveClick</span><span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">isOnline</span> ? <span class="sp-syntax-string">'Save progress'</span> : <span class="sp-syntax-string">'Reconnecting...'</span><span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-punctuation"></</span><span class="sp-syntax-tag">button</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">export</span> <span class="sp-syntax-keyword">default</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">App</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-definition">SaveButton</span> <span class="sp-syntax-punctuation">/></span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-definition">StatusBar</span> <span class="sp-syntax-punctuation">/></span> <span class="sp-syntax-punctuation"></</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> </pre></div></div></div><div class=" order-last xl:order-2 sp-stack"><div class="p-0 sm:p-2 md:p-4 lg:p-8 bg-card dark:bg-wash-dark h-full relative md:rounded-b-lg lg:rounded-b-none"><div style="position:relative"><iframe class="rounded-t-none bg-white md:shadow-md sm:rounded-lg w-full max-w-full transition-opacity absolute opacity-0 pointer-events-none duration-75" title="Sandbox Preview" style="height:15px;z-index:-1"></iframe></div></div></div><button translate="yes" class="sandpack-expand flex text-base justify-between dark:border-card-dark bg-wash dark:bg-card-dark items-center z-10 p-1 w-full order-2 xl:order-last border-b-1 relative top-0"><span class="flex p-2 focus:outline-none text-primary dark:text-primary-dark leading-[20px]"><svg class="rotate-0 inline me-1.5 text-xl" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg>Show more</span></button></div></div></div></div><!--/$--><div class="max-w-4xl ms-0 2xl:mx-auto"> <p class="whitespace-pre-wrap my-4">Verify that switching the network on and off updates both components.</p> <p class="whitespace-pre-wrap my-4">Now your components don’t have as much repetitive logic. <strong class="font-bold">More importantly, the code inside them describes <em>what they want to do</em> (use the online status!) rather than <em>how to do it</em> (by subscribing to the browser events).</strong></p> <p class="whitespace-pre-wrap my-4">When you extract logic into custom Hooks, you can hide the gnarly details of how you deal with some external system or a browser API. The code of your components expresses your intent, not the implementation.</p> <h3 id="hook-names-always-start-with-use" class="mdx-heading text-2xl font-display leading-9 text-primary dark:text-primary-dark font-bold my-6">Hook names always start with <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">use</code> <a href="#hook-names-always-start-with-use" aria-label="Link for this heading" title="Link for this heading" class="mdx-header-anchor inline-block"><svg width="1em" height="1em" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="text-gray-70 ms-2 h-5 w-5"><g fill="currentColor" fill-rule="evenodd"><path d="M7.778 7.975a2.5 2.5 0 0 0 .347-3.837L6.017 2.03a2.498 2.498 0 0 0-3.542-.007 2.5 2.5 0 0 0 .006 3.543l1.153 1.15c.07-.29.154-.563.25-.773.036-.077.084-.16.14-.25L3.18 4.85a1.496 1.496 0 0 1 .002-2.12 1.496 1.496 0 0 1 2.12 0l2.124 2.123a1.496 1.496 0 0 1-.333 2.37c.16.246.42.504.685.752z"></path><path d="M5.657 4.557a2.5 2.5 0 0 0-.347 3.837l2.108 2.108a2.498 2.498 0 0 0 3.542.007 2.5 2.5 0 0 0-.006-3.543L9.802 5.815c-.07.29-.154.565-.25.774-.036.076-.084.16-.14.25l.842.84c.585.587.59 1.532 0 2.122-.587.585-1.532.59-2.12 0L6.008 7.68a1.496 1.496 0 0 1 .332-2.372c-.16-.245-.42-.503-.685-.75z"></path></g></svg></a></h3> <p class="whitespace-pre-wrap my-4">React applications are built from components. Components are built from Hooks, whether built-in or custom. You’ll likely often use custom Hooks created by others, but occasionally you might write one yourself!</p> <p class="whitespace-pre-wrap my-4">You must follow these naming conventions:</p> <ol class="ms-6 my-3 list-decimal"> <li class="leading-relaxed mb-1"><strong class="font-bold">React component names must start with a capital letter,</strong> like <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">StatusBar</code> and <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">SaveButton</code>. React components also need to return something that React knows how to display, like a piece of JSX.</li> <li class="leading-relaxed mb-1"><strong class="font-bold">Hook names must start with <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">use</code> followed by a capital letter,</strong> like <a class="inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal" href="/reference/react/useState"><code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useState</code></a> (built-in) or <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useOnlineStatus</code> (custom, like earlier on the page). Hooks may return arbitrary values.</li> </ol> <p class="whitespace-pre-wrap my-4">This convention guarantees that you can always look at a component and know where its state, Effects, and other React features might “hide”. For example, if you see a <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">getColor()</code> function call inside your component, you can be sure that it can’t possibly contain React state inside because its name doesn’t start with <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">use</code>. However, a function call like <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useOnlineStatus()</code> will most likely contain calls to other Hooks inside!</p> <div class="expandable-callout pt-8 pb-4 px-5 sm:px-8 my-8 relative rounded-none shadow-inner-border -mx-5 sm:mx-auto sm:rounded-2xl bg-green-5 dark:bg-green-60 dark:bg-opacity-20 text-primary dark:text-primary-dark text-lg"><h3 class="text-2xl font-display font-bold text-green-60 dark:text-green-40"><svg class="inline me-2 mb-1 text-lg text-green-60 dark:text-green-40" width="2em" height="2em" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_40_48064)"><path d="M24 27C24 25.3431 25.3431 24 27 24H45C46.6569 24 48 25.3431 48 27C48 28.6569 46.6569 30 45 30H27C25.3431 30 24 28.6569 24 27Z" fill="currentColor"></path><path d="M24 39C24 37.3431 25.3431 36 27 36H39C40.6569 36 42 37.3431 42 39C42 40.6569 40.6569 42 39 42H27C25.3431 42 24 40.6569 24 39Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M12 18C12 13.0294 16.0294 9 21 9H51C55.9706 9 60 13.0294 60 18V54C60 58.9706 55.9706 63 51 63H21C16.0294 63 12 58.9706 12 54V18ZM21 15H51C52.6569 15 54 16.3431 54 18V54C54 55.6569 52.6569 57 51 57H21C19.3431 57 18 55.6569 18 54V18C18 16.3431 19.3431 15 21 15Z" fill="currentColor"></path></g><defs><clipPath id="clip0_40_48064"><rect width="72" height="72" fill="white"></rect></clipPath></defs></svg>Note</h3><div class="relative"><div class="py-2"><p class="whitespace-pre-wrap my-4">If your linter is <a class="inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal" href="/learn/editor-setup#linting">configured for React,</a> it will enforce this naming convention. Scroll up to the sandbox above and rename <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useOnlineStatus</code> to <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">getOnlineStatus</code>. Notice that the linter won’t allow you to call <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useState</code> or <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useEffect</code> inside of it anymore. Only Hooks and components can call other Hooks!</p></div></div></div> <details class="my-12 rounded-2xl shadow-inner-border dark:shadow-inner-border-dark relative dark:bg-opacity-20 dark:bg-purple-60 bg-purple-5"><summary class="list-none p-8" tabindex="-1"><h5 class="mb-4 uppercase font-bold flex items-center text-sm dark:text-purple-30 text-purple-50"><svg class="inline me-2 dark:text-purple-30 text-purple-40" width="1.5em" height="1.5em" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M34.7409 59.7228L32.9567 58.9094C27.2672 56.3157 20.7328 56.3157 15.0433 58.9094C12.6018 60.0224 9.39163 59.0275 8.44602 56.0621C7.45647 52.9589 5.99975 46.5898 6 35.9997C6.00029 23.5648 8.00803 18.3599 9.11099 16.4196C9.67795 15.4222 10.5255 14.8455 11.2254 14.5264L12.0179 14.1651C19.6351 10.6926 28.4011 10.6738 36 14.1733C43.5989 10.6738 52.3649 10.6926 59.9821 14.1651L60.7746 14.5264C61.4745 14.8455 62.3221 15.4222 62.889 16.4196C63.992 18.3599 65.9997 23.5648 66 35.9997C66.0002 46.5898 64.5435 52.9589 63.554 56.0621C62.6084 59.0275 59.3982 60.0224 56.9567 58.9094C51.2672 56.3157 44.7328 56.3157 39.0433 58.9094L37.2591 59.7228C37.1986 59.7508 37.1373 59.7767 37.0753 59.8004C36.4484 60.0411 35.7556 60.0653 35.1102 59.8648C34.9847 59.8258 34.8613 59.7784 34.7409 59.7228ZM14.5068 19.6246C20.3733 16.9501 27.0874 16.8775 33 19.4067V52.473C26.7613 50.32 19.9378 50.471 13.7811 52.9261C13.0005 49.9843 11.9998 44.547 12 35.9998C12.0002 25.5786 13.4879 21.1893 14.1179 19.8018L14.5068 19.6246ZM39 52.473C45.2387 50.32 52.0622 50.471 58.2189 52.9261C58.9995 49.9843 60.0002 44.547 60 35.9998C59.9998 25.5786 58.5121 21.1893 57.8821 19.8018L57.4932 19.6246C51.6267 16.9501 44.9126 16.8775 39 19.4067V52.473Z" fill="currentColor"></path></svg>Deep Dive</h5><div class="mb-4"><h4 id="should-all-functions-called-during-rendering-start-with-the-use-prefix" class="mdx-heading text-xl font-bold text-primary dark:text-primary-dark text-xl font-display font-bold leading-9 my-4">Should all functions called during rendering start with the use prefix? <a href="#should-all-functions-called-during-rendering-start-with-the-use-prefix" aria-label="Link for Should all functions called during rendering start with the use prefix? " title="Link for Should all functions called during rendering start with the use prefix? " class="mdx-header-anchor inline-block"><svg width="1em" height="1em" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="text-gray-70 ms-2 h-5 w-5"><g fill="currentColor" fill-rule="evenodd"><path d="M7.778 7.975a2.5 2.5 0 0 0 .347-3.837L6.017 2.03a2.498 2.498 0 0 0-3.542-.007 2.5 2.5 0 0 0 .006 3.543l1.153 1.15c.07-.29.154-.563.25-.773.036-.077.084-.16.14-.25L3.18 4.85a1.496 1.496 0 0 1 .002-2.12 1.496 1.496 0 0 1 2.12 0l2.124 2.123a1.496 1.496 0 0 1-.333 2.37c.16.246.42.504.685.752z"></path><path d="M5.657 4.557a2.5 2.5 0 0 0-.347 3.837l2.108 2.108a2.498 2.498 0 0 0 3.542.007 2.5 2.5 0 0 0-.006-3.543L9.802 5.815c-.07.29-.154.565-.25.774-.036.076-.084.16-.14.25l.842.84c.585.587.59 1.532 0 2.122-.587.585-1.532.59-2.12 0L6.008 7.68a1.496 1.496 0 0 1 .332-2.372c-.16-.245-.42-.503-.685-.75z"></path></g></svg></a></h4></div><button class="bg-purple-50 border-purple-50 hover:bg-purple-40 focus:bg-purple-50 active:bg-purple-50 text-base leading-tight font-bold rounded-full py-2 px-4 focus:outline focus:outline-offset-2 focus:outline-link dark:focus:outline-link-dark inline-flex items-center my-1 bg-link border-link text-white hover:bg-link focus:bg-link active:bg-link"><span class="me-1"><svg class="rotate-0" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg></span>Show Details</button></summary><div class="p-8 border-t dark:border-purple-60 border-purple-10 "><p class="whitespace-pre-wrap my-4">No. Functions that don’t <em>call</em> Hooks don’t need to <em>be</em> Hooks.</p><p class="whitespace-pre-wrap my-4">If your function doesn’t call any Hooks, avoid the <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">use</code> prefix. Instead, write it as a regular function <em>without</em> the <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">use</code> prefix. For example, <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useSorted</code> below doesn’t call Hooks, so call it <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">getSorted</code> instead:</p><!--$--><div dir="ltr" class="sandpack sandpack--codeblock rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg my-8" style="contain:content"><div class="sp-wrapper"><div class="sp-stack"><div class="sp-code-editor"><pre class="sp-cm sp-pristine sp-javascript flex align-start"><code class="sp-pre-placeholder grow-[2]"><div class="cm-line "><span class="sp-syntax-comment">// 🔴 Avoid: A Hook that doesn't use Hooks</span><br/></div><div class="cm-line "><span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">useSorted</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">items</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-plain">items</span>.<span class="sp-syntax-property">slice</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span>.<span class="sp-syntax-property">sort</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><span class="sp-syntax-punctuation">}</span><br/></div><div class="cm-line "><br/></div><div class="cm-line "><span class="sp-syntax-comment">// ✅ Good: A regular function that doesn't use Hooks</span><br/></div><div class="cm-line "><span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">getSorted</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">items</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-plain">items</span>.<span class="sp-syntax-property">slice</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span>.<span class="sp-syntax-property">sort</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><span class="sp-syntax-punctuation">}</span></div></code></pre></div></div></div></div><!--/$--><p class="whitespace-pre-wrap my-4">This ensures that your code can call this regular function anywhere, including conditions:</p><!--$--><div dir="ltr" class="sandpack sandpack--codeblock rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg my-8" style="contain:content"><div class="sp-wrapper"><div class="sp-stack"><div class="sp-code-editor"><pre class="sp-cm sp-pristine sp-javascript flex align-start"><code class="sp-pre-placeholder grow-[2]"><div class="cm-line "><span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">List</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">items</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-property">shouldSort</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">let</span> <span class="sp-syntax-plain">displayedItems</span> = <span class="sp-syntax-plain">items</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">if</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">shouldSort</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-comment">// ✅ It's ok to call getSorted() conditionally because it's not a Hook</span><br/></div><div class="cm-line "> <span class="sp-syntax-plain">displayedItems</span> = <span class="sp-syntax-definition">getSorted</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">items</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-punctuation">}</span><br/></div><div class="cm-line "> <span class="sp-syntax-comment">// ...</span><br/></div><div class="cm-line "><span class="sp-syntax-punctuation">}</span></div></code></pre></div></div></div></div><!--/$--><p class="whitespace-pre-wrap my-4">You should give <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">use</code> prefix to a function (and thus make it a Hook) if it uses at least one Hook inside of it:</p><!--$--><div dir="ltr" class="sandpack sandpack--codeblock rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg my-8" style="contain:content"><div class="sp-wrapper"><div class="sp-stack"><div class="sp-code-editor"><pre class="sp-cm sp-pristine sp-javascript flex align-start"><code class="sp-pre-placeholder grow-[2]"><div class="cm-line "><span class="sp-syntax-comment">// ✅ Good: A Hook that uses other Hooks</span><br/></div><div class="cm-line "><span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">useAuth</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-definition">useContext</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">Auth</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><span class="sp-syntax-punctuation">}</span></div></code></pre></div></div></div></div><!--/$--><p class="whitespace-pre-wrap my-4">Technically, this isn’t enforced by React. In principle, you could make a Hook that doesn’t call other Hooks. This is often confusing and limiting so it’s best to avoid that pattern. However, there may be rare cases where it is helpful. For example, maybe your function doesn’t use any Hooks right now, but you plan to add some Hook calls to it in the future. Then it makes sense to name it with the <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">use</code> prefix:</p><!--$--><div dir="ltr" class="sandpack sandpack--codeblock rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg my-8" style="contain:content"><div class="sp-wrapper"><div class="sp-stack"><div class="sp-code-editor"><pre class="sp-cm sp-pristine sp-javascript flex align-start"><code class="sp-pre-placeholder grow-[2]"><div class="cm-line "><span class="sp-syntax-comment">// ✅ Good: A Hook that will likely use some other Hooks later</span><br/></div><div class="cm-line "><span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">useAuth</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-comment">// TODO: Replace with this line when authentication is implemented:</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-comment">// return useContext(Auth);</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-plain">TEST_USER</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><span class="sp-syntax-punctuation">}</span></div></code></pre></div></div></div></div><!--/$--><p class="whitespace-pre-wrap my-4">Then components won’t be able to call it conditionally. This will become important when you actually add Hook calls inside. If you don’t plan to use Hooks inside it (now or later), don’t make it a Hook.</p></div></details> <h3 id="custom-hooks-let-you-share-stateful-logic-not-state-itself" class="mdx-heading text-2xl font-display leading-9 text-primary dark:text-primary-dark font-bold my-6">Custom Hooks let you share stateful logic, not state itself <a href="#custom-hooks-let-you-share-stateful-logic-not-state-itself" aria-label="Link for Custom Hooks let you share stateful logic, not state itself " title="Link for Custom Hooks let you share stateful logic, not state itself " class="mdx-header-anchor inline-block"><svg width="1em" height="1em" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="text-gray-70 ms-2 h-5 w-5"><g fill="currentColor" fill-rule="evenodd"><path d="M7.778 7.975a2.5 2.5 0 0 0 .347-3.837L6.017 2.03a2.498 2.498 0 0 0-3.542-.007 2.5 2.5 0 0 0 .006 3.543l1.153 1.15c.07-.29.154-.563.25-.773.036-.077.084-.16.14-.25L3.18 4.85a1.496 1.496 0 0 1 .002-2.12 1.496 1.496 0 0 1 2.12 0l2.124 2.123a1.496 1.496 0 0 1-.333 2.37c.16.246.42.504.685.752z"></path><path d="M5.657 4.557a2.5 2.5 0 0 0-.347 3.837l2.108 2.108a2.498 2.498 0 0 0 3.542.007 2.5 2.5 0 0 0-.006-3.543L9.802 5.815c-.07.29-.154.565-.25.774-.036.076-.084.16-.14.25l.842.84c.585.587.59 1.532 0 2.122-.587.585-1.532.59-2.12 0L6.008 7.68a1.496 1.496 0 0 1 .332-2.372c-.16-.245-.42-.503-.685-.75z"></path></g></svg></a></h3> <p class="whitespace-pre-wrap my-4">In the earlier example, when you turned the network on and off, both components updated together. However, it’s wrong to think that a single <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">isOnline</code> state variable is shared between them. Look at this code:</p> <!--$--><div dir="ltr" class="sandpack sandpack--codeblock rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg my-8" style="contain:content"><div class="sp-wrapper"><div class="sp-stack"><div class="sp-code-editor"><pre class="sp-cm sp-pristine sp-javascript flex align-start"><code class="sp-pre-placeholder grow-[2]"><div class="cm-line "><span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">StatusBar</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">isOnline</span> = <span class="sp-syntax-definition">useOnlineStatus</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-comment">// ...</span><br/></div><div class="cm-line "><span class="sp-syntax-punctuation">}</span><br/></div><div class="cm-line "><br/></div><div class="cm-line "><span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">SaveButton</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">isOnline</span> = <span class="sp-syntax-definition">useOnlineStatus</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-comment">// ...</span><br/></div><div class="cm-line "><span class="sp-syntax-punctuation">}</span></div></code></pre></div></div></div></div><!--/$--> <p class="whitespace-pre-wrap my-4">It works the same way as before you extracted the duplication:</p> <!--$--><div dir="ltr" class="sandpack sandpack--codeblock rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg my-8" style="contain:content"><div class="sp-wrapper"><div class="sp-stack"><div class="sp-code-editor"><pre class="sp-cm sp-pristine sp-javascript flex align-start"><code class="sp-pre-placeholder grow-[2]"><div class="cm-line "><span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">StatusBar</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">isOnline</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setIsOnline</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-static">true</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-definition">useEffect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-comment">// ...</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-punctuation">]</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-comment">// ...</span><br/></div><div class="cm-line "><span class="sp-syntax-punctuation">}</span><br/></div><div class="cm-line "><br/></div><div class="cm-line "><span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">SaveButton</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">isOnline</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setIsOnline</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-static">true</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-definition">useEffect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-comment">// ...</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-punctuation">]</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-comment">// ...</span><br/></div><div class="cm-line "><span class="sp-syntax-punctuation">}</span></div></code></pre></div></div></div></div><!--/$--> <p class="whitespace-pre-wrap my-4">These are two completely independent state variables and Effects! They happened to have the same value at the same time because you synchronized them with the same external value (whether the network is on).</p> <p class="whitespace-pre-wrap my-4">To better illustrate this, we’ll need a different example. Consider this <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">Form</code> component:</p> </div><!--$--><div class="sandpack sandpack--playground w-full my-8" dir="ltr"><div class="sp-wrapper"><div class="shadow-lg dark:shadow-lg-dark rounded-lg" style="contain:content"><div class="bg-wash dark:bg-card-dark flex justify-between items-center relative z-10 border-b border-border dark:border-border-dark rounded-t-lg text-lg"><div class="flex-1 grow min-w-0 px-4 lg:px-6"><div><div class="relative overflow-hidden"><div class="w-[fit-content] invisible"><div class=" sp-tabs" translate="no"><div aria-label="Select active file" class=" sp-tabs-scrollable-container" role="tablist"><button aria-selected="true" class=" sp-tab-button" data-active="true" role="tab" title="/src/App.js" type="button">App.js</button></div></div></div><button class="absolute top-0 start-[2px]" id="headlessui-listbox-button-:R16a8cq6:" aria-haspopup="true" aria-expanded="false" data-headlessui-state=""><span class="h-full py-2 px-1 mt-px -mb-px flex border-b text-link dark:text-link-dark border-link dark:border-link-dark items-center text-md leading-tight truncate" style="max-width:160px">App.js</span></button></div></div></div><div class="px-3 flex items-center justify-end text-start" translate="yes"><button class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1" title="Reset Sandbox" type="button"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative"><path d="M13.8982 5.20844C12.4626 4.88688 10.9686 4.93769 9.55821 5.35604L11.8524 3.06184C11.8989 3.0154 11.9357 2.96028 11.9608 2.89961C11.986 2.83894 11.9989 2.77391 11.9989 2.70824C11.9989 2.64256 11.986 2.57754 11.9608 2.51686C11.9357 2.45619 11.8989 2.40107 11.8524 2.35464L11.1456 1.64784C11.0992 1.60139 11.0441 1.56455 10.9834 1.53942C10.9227 1.51428 10.8577 1.50134 10.792 1.50134C10.7263 1.50134 10.6613 1.51428 10.6006 1.53942C10.54 1.56455 10.4848 1.60139 10.4384 1.64784L6.14571 5.94054C6.00654 6.07969 5.89615 6.2449 5.82083 6.42673C5.74551 6.60855 5.70675 6.80343 5.70675 7.00024C5.70675 7.19704 5.74551 7.39192 5.82083 7.57374C5.89615 7.75557 6.00654 7.92078 6.14571 8.05994L10.4387 12.3529C10.5325 12.4465 10.6595 12.4991 10.792 12.4991C10.9245 12.4991 11.0516 12.4465 11.1453 12.3529L11.8527 11.6455C11.9463 11.5518 11.9989 11.4247 11.9989 11.2922C11.9989 11.1598 11.9463 11.0327 11.8527 10.9389L8.77481 7.86104C9.99795 7.16236 11.415 6.8801 12.8125 7.05678C14.21 7.23347 15.5122 7.85953 16.523 8.84064C17.5338 9.82176 18.1983 11.1048 18.4165 12.4964C18.6347 13.888 18.3947 15.3129 17.7328 16.5562C17.0708 17.7996 16.0227 18.7942 14.7463 19.3902C13.47 19.9861 12.0345 20.1511 10.6563 19.8603C9.27798 19.5695 8.03152 18.8387 7.10469 17.778C6.17786 16.7172 5.62086 15.384 5.51761 13.9791C5.51156 13.8512 5.45689 13.7303 5.36477 13.6413C5.27265 13.5522 5.15001 13.5017 5.02191 13.5H4.02081C3.95297 13.4996 3.88574 13.5129 3.8232 13.5392C3.76065 13.5655 3.70408 13.6042 3.6569 13.6529C3.60972 13.7017 3.57291 13.7595 3.54869 13.8228C3.52448 13.8862 3.51336 13.9538 3.51601 14.0216C3.61349 15.5965 4.1473 17.1132 5.0577 18.4019C5.9681 19.6906 7.21917 20.7006 8.6709 21.3188C10.1226 21.937 11.7178 22.139 13.2778 21.9022C14.8378 21.6654 16.3011 20.9992 17.504 19.978C18.7069 18.9569 19.6019 17.6212 20.0889 16.1203C20.5759 14.6195 20.6356 13.0128 20.2614 11.4799C19.8872 9.94705 19.0938 8.54858 17.97 7.44098C16.8462 6.33339 15.4363 5.56037 13.8982 5.20844V5.20844Z" fill="currentColor"></path></svg> Reset</button><a href="https://codesandbox.io/api/v1/sandboxes/define?undefined&environment=create-react-app" rel="noreferrer noopener" target="_blank" title="Open in CodeSandbox" class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1 ms-2 md:ms-1"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative top-[1px]"><path d="M20.5001 2H15.5001C15.3675 2 15.2403 2.05268 15.1465 2.14645C15.0528 2.24021 15.0001 2.36739 15.0001 2.5V3.5C15.0001 3.63261 15.0528 3.75979 15.1465 3.85355C15.2403 3.94732 15.3675 4 15.5001 4H18.5901L7.6501 14.94C7.60323 14.9865 7.56604 15.0418 7.54065 15.1027C7.51527 15.1636 7.5022 15.229 7.5022 15.295C7.5022 15.361 7.51527 15.4264 7.54065 15.4873C7.56604 15.5482 7.60323 15.6035 7.6501 15.65L8.3501 16.35C8.39658 16.3969 8.45188 16.4341 8.51281 16.4594C8.57374 16.4848 8.63909 16.4979 8.7051 16.4979C8.7711 16.4979 8.83646 16.4848 8.89738 16.4594C8.95831 16.4341 9.01362 16.3969 9.0601 16.35L20.0001 5.41V8.5C20.0001 8.63261 20.0528 8.75979 20.1465 8.85355C20.2403 8.94732 20.3675 9 20.5001 9H21.5001C21.6327 9 21.7599 8.94732 21.8537 8.85355C21.9474 8.75979 22.0001 8.63261 22.0001 8.5V3.5C22.0001 3.10218 21.8421 2.72064 21.5608 2.43934C21.2795 2.15804 20.8979 2 20.5001 2V2Z" fill="currentColor"></path><path d="M21.5 13H20.5C20.3674 13 20.2402 13.0527 20.1464 13.1464C20.0527 13.2402 20 13.3674 20 13.5V20H4V4H10.5C10.6326 4 10.7598 3.94732 10.8536 3.85355C10.9473 3.75979 11 3.63261 11 3.5V2.5C11 2.36739 10.9473 2.24021 10.8536 2.14645C10.7598 2.05268 10.6326 2 10.5 2H3.5C3.10218 2 2.72064 2.15804 2.43934 2.43934C2.15804 2.72064 2 3.10218 2 3.5V20.5C2 20.8978 2.15804 21.2794 2.43934 21.5607C2.72064 21.842 3.10218 22 3.5 22H20.5C20.8978 22 21.2794 21.842 21.5607 21.5607C21.842 21.2794 22 20.8978 22 20.5V13.5C22 13.3674 21.9473 13.2402 21.8536 13.1464C21.7598 13.0527 21.6326 13 21.5 13Z" fill="currentColor"></path></svg><span class="hidden md:block">Fork</span></a></div></div><div class=" sp-layout"><div class=" sp-editor sp-stack"><div class=" sp-code-editor"><div aria-autocomplete="list" aria-label="Code Editor for App.js" aria-multiline="true" class="sp-pristine sp-javascript sp-cm" role="textbox" tabindex="0" translate="no"><pre class=" sp-pre-placeholder" style="margin-left:var(--sp-space-11)"><span class="sp-syntax-keyword">import</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">useState</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">from</span> <span class="sp-syntax-string">'react'</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">export</span> <span class="sp-syntax-keyword">default</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">Form</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">firstName</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setFirstName</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'Mary'</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">lastName</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setLastName</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'Poppins'</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">handleFirstNameChange</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">e</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-definition">setFirstName</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">e</span>.<span class="sp-syntax-property">target</span>.<span class="sp-syntax-property">value</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">handleLastNameChange</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">e</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-definition">setLastName</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">e</span>.<span class="sp-syntax-property">target</span>.<span class="sp-syntax-property">value</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">label</span><span class="sp-syntax-punctuation">></span> First name: <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">input</span> <span class="sp-syntax-property">value</span>=<span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">firstName</span><span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-property">onChange</span>=<span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">handleFirstNameChange</span><span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-punctuation">/></span> <span class="sp-syntax-punctuation"></</span><span class="sp-syntax-tag">label</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">label</span><span class="sp-syntax-punctuation">></span> Last name: <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">input</span> <span class="sp-syntax-property">value</span>=<span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">lastName</span><span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-property">onChange</span>=<span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">handleLastNameChange</span><span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-punctuation">/></span> <span class="sp-syntax-punctuation"></</span><span class="sp-syntax-tag">label</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">p</span><span class="sp-syntax-punctuation">></span><span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">b</span><span class="sp-syntax-punctuation">></span>Good morning, <span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">firstName</span><span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">lastName</span><span class="sp-syntax-punctuation">}</span>.<span class="sp-syntax-punctuation"></</span><span class="sp-syntax-tag">b</span><span class="sp-syntax-punctuation">></span><span class="sp-syntax-punctuation"></</span><span class="sp-syntax-tag">p</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation"></</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> </pre></div></div></div><div class=" order-last xl:order-2 sp-stack"><div class="p-0 sm:p-2 md:p-4 lg:p-8 bg-card dark:bg-wash-dark h-full relative md:rounded-b-lg lg:rounded-b-none"><div style="position:relative"><iframe class="rounded-t-none bg-white md:shadow-md sm:rounded-lg w-full max-w-full transition-opacity absolute opacity-0 pointer-events-none duration-75" title="Sandbox Preview" style="height:15px;z-index:-1"></iframe></div></div></div><button translate="yes" class="sandpack-expand flex text-base justify-between dark:border-card-dark bg-wash dark:bg-card-dark items-center z-10 p-1 w-full order-2 xl:order-last border-b-1 relative top-0"><span class="flex p-2 focus:outline-none text-primary dark:text-primary-dark leading-[20px]"><svg class="rotate-0 inline me-1.5 text-xl" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg>Show more</span></button></div></div></div></div><!--/$--><div class="max-w-4xl ms-0 2xl:mx-auto"> <p class="whitespace-pre-wrap my-4">There’s some repetitive logic for each form field:</p> <ol class="ms-6 my-3 list-decimal"> <li class="leading-relaxed mb-1">There’s a piece of state (<code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">firstName</code> and <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">lastName</code>).</li> <li class="leading-relaxed mb-1">There’s a change handler (<code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">handleFirstNameChange</code> and <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">handleLastNameChange</code>).</li> <li class="leading-relaxed mb-1">There’s a piece of JSX that specifies the <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">value</code> and <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">onChange</code> attributes for that input.</li> </ol> <p class="whitespace-pre-wrap my-4">You can extract the repetitive logic into this <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useFormInput</code> custom Hook:</p> </div><!--$--><div class="sandpack sandpack--playground w-full my-8" dir="ltr"><div class="sp-wrapper"><div class="shadow-lg dark:shadow-lg-dark rounded-lg" style="contain:content"><div class="bg-wash dark:bg-card-dark flex justify-between items-center relative z-10 border-b border-border dark:border-border-dark rounded-t-lg text-lg"><div class="flex-1 grow min-w-0 px-4 lg:px-6"><div><div class="relative overflow-hidden"><div class="w-[fit-content] invisible"><div class=" sp-tabs" translate="no"><div aria-label="Select active file" class=" sp-tabs-scrollable-container" role="tablist"><button aria-selected="false" class=" sp-tab-button" data-active="false" role="tab" title="/src/App.js" type="button">App.js</button><button aria-selected="true" class=" sp-tab-button" data-active="true" role="tab" title="/src/useFormInput.js" type="button">useFormInput.js</button></div></div></div><button class="absolute top-0 start-[2px]" id="headlessui-listbox-button-:R16aacq6:" aria-haspopup="true" aria-expanded="false" data-headlessui-state=""><span class="h-full py-2 px-1 mt-px -mb-px flex border-b text-link dark:text-link-dark border-link dark:border-link-dark items-center text-md leading-tight truncate" style="max-width:160px">useFormInput.js<span class="ms-2"><svg class="rotate-0" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg></span></span></button></div></div></div><div class="px-3 flex items-center justify-end text-start" translate="yes"><button class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1" title="Reset Sandbox" type="button"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative"><path d="M13.8982 5.20844C12.4626 4.88688 10.9686 4.93769 9.55821 5.35604L11.8524 3.06184C11.8989 3.0154 11.9357 2.96028 11.9608 2.89961C11.986 2.83894 11.9989 2.77391 11.9989 2.70824C11.9989 2.64256 11.986 2.57754 11.9608 2.51686C11.9357 2.45619 11.8989 2.40107 11.8524 2.35464L11.1456 1.64784C11.0992 1.60139 11.0441 1.56455 10.9834 1.53942C10.9227 1.51428 10.8577 1.50134 10.792 1.50134C10.7263 1.50134 10.6613 1.51428 10.6006 1.53942C10.54 1.56455 10.4848 1.60139 10.4384 1.64784L6.14571 5.94054C6.00654 6.07969 5.89615 6.2449 5.82083 6.42673C5.74551 6.60855 5.70675 6.80343 5.70675 7.00024C5.70675 7.19704 5.74551 7.39192 5.82083 7.57374C5.89615 7.75557 6.00654 7.92078 6.14571 8.05994L10.4387 12.3529C10.5325 12.4465 10.6595 12.4991 10.792 12.4991C10.9245 12.4991 11.0516 12.4465 11.1453 12.3529L11.8527 11.6455C11.9463 11.5518 11.9989 11.4247 11.9989 11.2922C11.9989 11.1598 11.9463 11.0327 11.8527 10.9389L8.77481 7.86104C9.99795 7.16236 11.415 6.8801 12.8125 7.05678C14.21 7.23347 15.5122 7.85953 16.523 8.84064C17.5338 9.82176 18.1983 11.1048 18.4165 12.4964C18.6347 13.888 18.3947 15.3129 17.7328 16.5562C17.0708 17.7996 16.0227 18.7942 14.7463 19.3902C13.47 19.9861 12.0345 20.1511 10.6563 19.8603C9.27798 19.5695 8.03152 18.8387 7.10469 17.778C6.17786 16.7172 5.62086 15.384 5.51761 13.9791C5.51156 13.8512 5.45689 13.7303 5.36477 13.6413C5.27265 13.5522 5.15001 13.5017 5.02191 13.5H4.02081C3.95297 13.4996 3.88574 13.5129 3.8232 13.5392C3.76065 13.5655 3.70408 13.6042 3.6569 13.6529C3.60972 13.7017 3.57291 13.7595 3.54869 13.8228C3.52448 13.8862 3.51336 13.9538 3.51601 14.0216C3.61349 15.5965 4.1473 17.1132 5.0577 18.4019C5.9681 19.6906 7.21917 20.7006 8.6709 21.3188C10.1226 21.937 11.7178 22.139 13.2778 21.9022C14.8378 21.6654 16.3011 20.9992 17.504 19.978C18.7069 18.9569 19.6019 17.6212 20.0889 16.1203C20.5759 14.6195 20.6356 13.0128 20.2614 11.4799C19.8872 9.94705 19.0938 8.54858 17.97 7.44098C16.8462 6.33339 15.4363 5.56037 13.8982 5.20844V5.20844Z" fill="currentColor"></path></svg> Reset</button><a href="https://codesandbox.io/api/v1/sandboxes/define?undefined&environment=create-react-app" rel="noreferrer noopener" target="_blank" title="Open in CodeSandbox" class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1 ms-2 md:ms-1"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative top-[1px]"><path d="M20.5001 2H15.5001C15.3675 2 15.2403 2.05268 15.1465 2.14645C15.0528 2.24021 15.0001 2.36739 15.0001 2.5V3.5C15.0001 3.63261 15.0528 3.75979 15.1465 3.85355C15.2403 3.94732 15.3675 4 15.5001 4H18.5901L7.6501 14.94C7.60323 14.9865 7.56604 15.0418 7.54065 15.1027C7.51527 15.1636 7.5022 15.229 7.5022 15.295C7.5022 15.361 7.51527 15.4264 7.54065 15.4873C7.56604 15.5482 7.60323 15.6035 7.6501 15.65L8.3501 16.35C8.39658 16.3969 8.45188 16.4341 8.51281 16.4594C8.57374 16.4848 8.63909 16.4979 8.7051 16.4979C8.7711 16.4979 8.83646 16.4848 8.89738 16.4594C8.95831 16.4341 9.01362 16.3969 9.0601 16.35L20.0001 5.41V8.5C20.0001 8.63261 20.0528 8.75979 20.1465 8.85355C20.2403 8.94732 20.3675 9 20.5001 9H21.5001C21.6327 9 21.7599 8.94732 21.8537 8.85355C21.9474 8.75979 22.0001 8.63261 22.0001 8.5V3.5C22.0001 3.10218 21.8421 2.72064 21.5608 2.43934C21.2795 2.15804 20.8979 2 20.5001 2V2Z" fill="currentColor"></path><path d="M21.5 13H20.5C20.3674 13 20.2402 13.0527 20.1464 13.1464C20.0527 13.2402 20 13.3674 20 13.5V20H4V4H10.5C10.6326 4 10.7598 3.94732 10.8536 3.85355C10.9473 3.75979 11 3.63261 11 3.5V2.5C11 2.36739 10.9473 2.24021 10.8536 2.14645C10.7598 2.05268 10.6326 2 10.5 2H3.5C3.10218 2 2.72064 2.15804 2.43934 2.43934C2.15804 2.72064 2 3.10218 2 3.5V20.5C2 20.8978 2.15804 21.2794 2.43934 21.5607C2.72064 21.842 3.10218 22 3.5 22H20.5C20.8978 22 21.2794 21.842 21.5607 21.5607C21.842 21.2794 22 20.8978 22 20.5V13.5C22 13.3674 21.9473 13.2402 21.8536 13.1464C21.7598 13.0527 21.6326 13 21.5 13Z" fill="currentColor"></path></svg><span class="hidden md:block">Fork</span></a></div></div><div class=" sp-layout"><div class=" sp-editor sp-stack"><div class=" sp-code-editor"><div aria-autocomplete="list" aria-label="Code Editor for useFormInput.js" aria-multiline="true" class="sp-pristine sp-javascript sp-cm" role="textbox" tabindex="0" translate="no"><pre class=" sp-pre-placeholder" style="margin-left:var(--sp-space-11)"><span class="sp-syntax-keyword">import</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">useState</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">from</span> <span class="sp-syntax-string">'react'</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">export</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">useFormInput</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">initialValue</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">value</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setValue</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">initialValue</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">handleChange</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">e</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-definition">setValue</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">e</span>.<span class="sp-syntax-property">target</span>.<span class="sp-syntax-property">value</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">inputProps</span> = <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">value</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-plain">value</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-property">onChange</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-plain">handleChange</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-plain">inputProps</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> </pre></div></div></div><div class=" order-last xl:order-2 sp-stack"><div class="p-0 sm:p-2 md:p-4 lg:p-8 bg-card dark:bg-wash-dark h-full relative md:rounded-b-lg lg:rounded-b-none"><div style="position:relative"><iframe class="rounded-t-none bg-white md:shadow-md sm:rounded-lg w-full max-w-full transition-opacity absolute opacity-0 pointer-events-none duration-75" title="Sandbox Preview" style="height:15px;z-index:-1"></iframe></div></div></div><button translate="yes" class="sandpack-expand flex text-base justify-between dark:border-card-dark bg-wash dark:bg-card-dark items-center z-10 p-1 w-full order-2 xl:order-last border-b-1 relative top-0"><span class="flex p-2 focus:outline-none text-primary dark:text-primary-dark leading-[20px]"><svg class="rotate-0 inline me-1.5 text-xl" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg>Show more</span></button></div></div></div></div><!--/$--><div class="max-w-4xl ms-0 2xl:mx-auto"> <p class="whitespace-pre-wrap my-4">Notice that it only declares <em>one</em> state variable called <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">value</code>.</p> <p class="whitespace-pre-wrap my-4">However, the <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">Form</code> component calls <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useFormInput</code> <em>two times:</em></p> <!--$--><div dir="ltr" class="sandpack sandpack--codeblock rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg my-8" style="contain:content"><div class="sp-wrapper"><div class="sp-stack"><div class="sp-code-editor"><pre class="sp-cm sp-pristine sp-javascript flex align-start"><code class="sp-pre-placeholder grow-[2]"><div class="cm-line "><span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">Form</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">firstNameProps</span> = <span class="sp-syntax-definition">useFormInput</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'Mary'</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">lastNameProps</span> = <span class="sp-syntax-definition">useFormInput</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'Poppins'</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-comment">// ...</span></div></code></pre></div></div></div></div><!--/$--> <p class="whitespace-pre-wrap my-4">This is why it works like declaring two separate state variables!</p> <p class="whitespace-pre-wrap my-4"><strong class="font-bold">Custom Hooks let you share <em>stateful logic</em> but not <em>state itself.</em> Each call to a Hook is completely independent from every other call to the same Hook.</strong> This is why the two sandboxes above are completely equivalent. If you’d like, scroll back up and compare them. The behavior before and after extracting a custom Hook is identical.</p> <p class="whitespace-pre-wrap my-4">When you need to share the state itself between multiple components, <a class="inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal" href="/learn/sharing-state-between-components">lift it up and pass it down</a> instead.</p> <h2 id="passing-reactive-values-between-hooks" class="mdx-heading text-3xl font-display leading-10 text-primary dark:text-primary-dark font-bold my-6">Passing reactive values between Hooks <a href="#passing-reactive-values-between-hooks" aria-label="Link for Passing reactive values between Hooks " title="Link for Passing reactive values between Hooks " class="mdx-header-anchor inline-block"><svg width="1em" height="1em" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="text-gray-70 ms-2 h-5 w-5"><g fill="currentColor" fill-rule="evenodd"><path d="M7.778 7.975a2.5 2.5 0 0 0 .347-3.837L6.017 2.03a2.498 2.498 0 0 0-3.542-.007 2.5 2.5 0 0 0 .006 3.543l1.153 1.15c.07-.29.154-.563.25-.773.036-.077.084-.16.14-.25L3.18 4.85a1.496 1.496 0 0 1 .002-2.12 1.496 1.496 0 0 1 2.12 0l2.124 2.123a1.496 1.496 0 0 1-.333 2.37c.16.246.42.504.685.752z"></path><path d="M5.657 4.557a2.5 2.5 0 0 0-.347 3.837l2.108 2.108a2.498 2.498 0 0 0 3.542.007 2.5 2.5 0 0 0-.006-3.543L9.802 5.815c-.07.29-.154.565-.25.774-.036.076-.084.16-.14.25l.842.84c.585.587.59 1.532 0 2.122-.587.585-1.532.59-2.12 0L6.008 7.68a1.496 1.496 0 0 1 .332-2.372c-.16-.245-.42-.503-.685-.75z"></path></g></svg></a></h2> <p class="whitespace-pre-wrap my-4">The code inside your custom Hooks will re-run during every re-render of your component. This is why, like components, custom Hooks <a class="inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal" href="/learn/keeping-components-pure">need to be pure.</a> Think of custom Hooks’ code as part of your component’s body!</p> <p class="whitespace-pre-wrap my-4">Because custom Hooks re-render together with your component, they always receive the latest props and state. To see what this means, consider this chat room example. Change the server URL or the chat room:</p> </div><!--$--><div class="sandpack sandpack--playground w-full my-8" dir="ltr"><div class="sp-wrapper"><div class="shadow-lg dark:shadow-lg-dark rounded-lg" style="contain:content"><div class="bg-wash dark:bg-card-dark flex justify-between items-center relative z-10 border-b border-border dark:border-border-dark rounded-t-lg text-lg"><div class="flex-1 grow min-w-0 px-4 lg:px-6"><div><div class="relative overflow-hidden"><div class="w-[fit-content] invisible"><div class=" sp-tabs" translate="no"><div aria-label="Select active file" class=" sp-tabs-scrollable-container" role="tablist"><button aria-selected="false" class=" sp-tab-button" data-active="false" role="tab" title="/src/App.js" type="button">App.js</button><button aria-selected="true" class=" sp-tab-button" data-active="true" role="tab" title="/src/ChatRoom.js" type="button">ChatRoom.js</button><button aria-selected="false" class=" sp-tab-button" data-active="false" role="tab" title="/src/chat.js" type="button">chat.js</button><button aria-selected="false" class=" sp-tab-button" data-active="false" role="tab" title="/src/notifications.js" type="button">notifications.js</button></div></div></div><button class="absolute top-0 start-[2px]" id="headlessui-listbox-button-:R16accq6:" aria-haspopup="true" aria-expanded="false" data-headlessui-state=""><span class="h-full py-2 px-1 mt-px -mb-px flex border-b text-link dark:text-link-dark border-link dark:border-link-dark items-center text-md leading-tight truncate" style="max-width:160px">ChatRoom.js<span class="ms-2"><svg class="rotate-0" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg></span></span></button></div></div></div><div class="px-3 flex items-center justify-end text-start" translate="yes"><button class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1" title="Reset Sandbox" type="button"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative"><path d="M13.8982 5.20844C12.4626 4.88688 10.9686 4.93769 9.55821 5.35604L11.8524 3.06184C11.8989 3.0154 11.9357 2.96028 11.9608 2.89961C11.986 2.83894 11.9989 2.77391 11.9989 2.70824C11.9989 2.64256 11.986 2.57754 11.9608 2.51686C11.9357 2.45619 11.8989 2.40107 11.8524 2.35464L11.1456 1.64784C11.0992 1.60139 11.0441 1.56455 10.9834 1.53942C10.9227 1.51428 10.8577 1.50134 10.792 1.50134C10.7263 1.50134 10.6613 1.51428 10.6006 1.53942C10.54 1.56455 10.4848 1.60139 10.4384 1.64784L6.14571 5.94054C6.00654 6.07969 5.89615 6.2449 5.82083 6.42673C5.74551 6.60855 5.70675 6.80343 5.70675 7.00024C5.70675 7.19704 5.74551 7.39192 5.82083 7.57374C5.89615 7.75557 6.00654 7.92078 6.14571 8.05994L10.4387 12.3529C10.5325 12.4465 10.6595 12.4991 10.792 12.4991C10.9245 12.4991 11.0516 12.4465 11.1453 12.3529L11.8527 11.6455C11.9463 11.5518 11.9989 11.4247 11.9989 11.2922C11.9989 11.1598 11.9463 11.0327 11.8527 10.9389L8.77481 7.86104C9.99795 7.16236 11.415 6.8801 12.8125 7.05678C14.21 7.23347 15.5122 7.85953 16.523 8.84064C17.5338 9.82176 18.1983 11.1048 18.4165 12.4964C18.6347 13.888 18.3947 15.3129 17.7328 16.5562C17.0708 17.7996 16.0227 18.7942 14.7463 19.3902C13.47 19.9861 12.0345 20.1511 10.6563 19.8603C9.27798 19.5695 8.03152 18.8387 7.10469 17.778C6.17786 16.7172 5.62086 15.384 5.51761 13.9791C5.51156 13.8512 5.45689 13.7303 5.36477 13.6413C5.27265 13.5522 5.15001 13.5017 5.02191 13.5H4.02081C3.95297 13.4996 3.88574 13.5129 3.8232 13.5392C3.76065 13.5655 3.70408 13.6042 3.6569 13.6529C3.60972 13.7017 3.57291 13.7595 3.54869 13.8228C3.52448 13.8862 3.51336 13.9538 3.51601 14.0216C3.61349 15.5965 4.1473 17.1132 5.0577 18.4019C5.9681 19.6906 7.21917 20.7006 8.6709 21.3188C10.1226 21.937 11.7178 22.139 13.2778 21.9022C14.8378 21.6654 16.3011 20.9992 17.504 19.978C18.7069 18.9569 19.6019 17.6212 20.0889 16.1203C20.5759 14.6195 20.6356 13.0128 20.2614 11.4799C19.8872 9.94705 19.0938 8.54858 17.97 7.44098C16.8462 6.33339 15.4363 5.56037 13.8982 5.20844V5.20844Z" fill="currentColor"></path></svg> Reset</button><a href="https://codesandbox.io/api/v1/sandboxes/define?undefined&environment=create-react-app" rel="noreferrer noopener" target="_blank" title="Open in CodeSandbox" class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1 ms-2 md:ms-1"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative top-[1px]"><path d="M20.5001 2H15.5001C15.3675 2 15.2403 2.05268 15.1465 2.14645C15.0528 2.24021 15.0001 2.36739 15.0001 2.5V3.5C15.0001 3.63261 15.0528 3.75979 15.1465 3.85355C15.2403 3.94732 15.3675 4 15.5001 4H18.5901L7.6501 14.94C7.60323 14.9865 7.56604 15.0418 7.54065 15.1027C7.51527 15.1636 7.5022 15.229 7.5022 15.295C7.5022 15.361 7.51527 15.4264 7.54065 15.4873C7.56604 15.5482 7.60323 15.6035 7.6501 15.65L8.3501 16.35C8.39658 16.3969 8.45188 16.4341 8.51281 16.4594C8.57374 16.4848 8.63909 16.4979 8.7051 16.4979C8.7711 16.4979 8.83646 16.4848 8.89738 16.4594C8.95831 16.4341 9.01362 16.3969 9.0601 16.35L20.0001 5.41V8.5C20.0001 8.63261 20.0528 8.75979 20.1465 8.85355C20.2403 8.94732 20.3675 9 20.5001 9H21.5001C21.6327 9 21.7599 8.94732 21.8537 8.85355C21.9474 8.75979 22.0001 8.63261 22.0001 8.5V3.5C22.0001 3.10218 21.8421 2.72064 21.5608 2.43934C21.2795 2.15804 20.8979 2 20.5001 2V2Z" fill="currentColor"></path><path d="M21.5 13H20.5C20.3674 13 20.2402 13.0527 20.1464 13.1464C20.0527 13.2402 20 13.3674 20 13.5V20H4V4H10.5C10.6326 4 10.7598 3.94732 10.8536 3.85355C10.9473 3.75979 11 3.63261 11 3.5V2.5C11 2.36739 10.9473 2.24021 10.8536 2.14645C10.7598 2.05268 10.6326 2 10.5 2H3.5C3.10218 2 2.72064 2.15804 2.43934 2.43934C2.15804 2.72064 2 3.10218 2 3.5V20.5C2 20.8978 2.15804 21.2794 2.43934 21.5607C2.72064 21.842 3.10218 22 3.5 22H20.5C20.8978 22 21.2794 21.842 21.5607 21.5607C21.842 21.2794 22 20.8978 22 20.5V13.5C22 13.3674 21.9473 13.2402 21.8536 13.1464C21.7598 13.0527 21.6326 13 21.5 13Z" fill="currentColor"></path></svg><span class="hidden md:block">Fork</span></a></div></div><div class=" sp-layout"><div class=" sp-editor sp-stack"><div class=" sp-code-editor"><div aria-autocomplete="list" aria-label="Code Editor for ChatRoom.js" aria-multiline="true" class="sp-pristine sp-javascript sp-cm" role="textbox" tabindex="0" translate="no"><pre class=" sp-pre-placeholder" style="margin-left:var(--sp-space-11)"><span class="sp-syntax-keyword">import</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">useState</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">useEffect</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">from</span> <span class="sp-syntax-string">'react'</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">import</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">createConnection</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">from</span> <span class="sp-syntax-string">'./chat.js'</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">import</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">showNotification</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">from</span> <span class="sp-syntax-string">'./notifications.js'</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">export</span> <span class="sp-syntax-keyword">default</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">ChatRoom</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">roomId</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setServerUrl</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'https://localhost:1234'</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-definition">useEffect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">options</span> = <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">serverUrl</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-property">roomId</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-plain">roomId</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">connection</span> = <span class="sp-syntax-definition">createConnection</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">options</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-plain">connection</span>.<span class="sp-syntax-property">on</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'message'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">msg</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-definition">showNotification</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'New message: '</span> + <span class="sp-syntax-plain">msg</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-plain">connection</span>.<span class="sp-syntax-property">connect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-plain">connection</span>.<span class="sp-syntax-property">disconnect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">roomId</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">]</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">label</span><span class="sp-syntax-punctuation">></span> Server URL: <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">input</span> <span class="sp-syntax-property">value</span>=<span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-property">onChange</span>=<span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">e</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-definition">setServerUrl</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">e</span>.<span class="sp-syntax-property">target</span>.<span class="sp-syntax-property">value</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-punctuation">/></span> <span class="sp-syntax-punctuation"></</span><span class="sp-syntax-tag">label</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">h1</span><span class="sp-syntax-punctuation">></span>Welcome to the <span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">roomId</span><span class="sp-syntax-punctuation">}</span> room!<span class="sp-syntax-punctuation"></</span><span class="sp-syntax-tag">h1</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation"></</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> </pre></div></div></div><div class=" order-last xl:order-2 sp-stack"><div class="p-0 sm:p-2 md:p-4 lg:p-8 bg-card dark:bg-wash-dark h-full relative md:rounded-b-lg lg:rounded-b-none"><div style="position:relative"><iframe class="rounded-t-none bg-white md:shadow-md sm:rounded-lg w-full max-w-full transition-opacity absolute opacity-0 pointer-events-none duration-75" title="Sandbox Preview" style="height:15px;z-index:-1"></iframe></div></div></div><button translate="yes" class="sandpack-expand flex text-base justify-between dark:border-card-dark bg-wash dark:bg-card-dark items-center z-10 p-1 w-full order-2 xl:order-last border-b-1 relative top-0"><span class="flex p-2 focus:outline-none text-primary dark:text-primary-dark leading-[20px]"><svg class="rotate-0 inline me-1.5 text-xl" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg>Show more</span></button></div></div></div></div><!--/$--><div class="max-w-4xl ms-0 2xl:mx-auto"> <p class="whitespace-pre-wrap my-4">When you change <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">serverUrl</code> or <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">roomId</code>, the Effect <a class="inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal" href="/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values">“reacts” to your changes</a> and re-synchronizes. You can tell by the console messages that the chat re-connects every time that you change your Effect’s dependencies.</p> <p class="whitespace-pre-wrap my-4">Now move the Effect’s code into a custom Hook:</p> <!--$--><div dir="ltr" class="sandpack sandpack--codeblock rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg my-8" style="contain:content"><div class="sp-wrapper"><div class="sp-stack"><div class="sp-code-editor"><pre class="sp-cm sp-pristine sp-javascript flex align-start"><code class="sp-pre-placeholder grow-[2]"><div class="cm-line "><span class="sp-syntax-keyword">export</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">useChatRoom</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">serverUrl</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-property">roomId</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-definition">useEffect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">options</span> = <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-property">serverUrl</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">,</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-property">roomId</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-plain">roomId</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">connection</span> = <span class="sp-syntax-definition">createConnection</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">options</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-plain">connection</span>.<span class="sp-syntax-property">connect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-plain">connection</span>.<span class="sp-syntax-property">on</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'message'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">msg</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-definition">showNotification</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'New message: '</span> + <span class="sp-syntax-plain">msg</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-plain">connection</span>.<span class="sp-syntax-property">disconnect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">roomId</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">]</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><span class="sp-syntax-punctuation">}</span></div></code></pre></div></div></div></div><!--/$--> <p class="whitespace-pre-wrap my-4">This lets your <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">ChatRoom</code> component call your custom Hook without worrying about how it works inside:</p> <!--$--><div dir="ltr" class="sandpack sandpack--codeblock rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg my-8" style="contain:content"><div class="sp-wrapper"><div class="sp-stack"><div class="sp-code-editor"><pre class="sp-cm sp-pristine sp-javascript flex align-start"><code class="sp-pre-placeholder grow-[2]"><div class="cm-line "><span class="sp-syntax-keyword">export</span> <span class="sp-syntax-keyword">default</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">ChatRoom</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">roomId</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setServerUrl</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'https://localhost:1234'</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-definition">useChatRoom</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-property">roomId</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-plain">roomId</span><span class="sp-syntax-punctuation">,</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-property">serverUrl</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-plain">serverUrl</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span><br/></div><div class="cm-line "> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-punctuation">></span><br/></div><div class="cm-line "> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">label</span><span class="sp-syntax-punctuation">></span><br/></div><div class="cm-line "> Server URL:<br/></div><div class="cm-line "> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">input</span> <span class="sp-syntax-property">value</span>=<span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-property">onChange</span>=<span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">e</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-definition">setServerUrl</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">e</span>.<span class="sp-syntax-property">target</span>.<span class="sp-syntax-property">value</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-punctuation">/></span><br/></div><div class="cm-line "> <span class="sp-syntax-punctuation"></</span><span class="sp-syntax-tag">label</span><span class="sp-syntax-punctuation">></span><br/></div><div class="cm-line "> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">h1</span><span class="sp-syntax-punctuation">></span>Welcome to the <span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">roomId</span><span class="sp-syntax-punctuation">}</span> room!<span class="sp-syntax-punctuation"></</span><span class="sp-syntax-tag">h1</span><span class="sp-syntax-punctuation">></span><br/></div><div class="cm-line "> <span class="sp-syntax-punctuation"></</span><span class="sp-syntax-punctuation">></span><br/></div><div class="cm-line "> <span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><span class="sp-syntax-punctuation">}</span></div></code></pre></div></div></div></div><!--/$--> <p class="whitespace-pre-wrap my-4">This looks much simpler! (But it does the same thing.)</p> <p class="whitespace-pre-wrap my-4">Notice that the logic <em>still responds</em> to prop and state changes. Try editing the server URL or the selected room:</p> </div><!--$--><div class="sandpack sandpack--playground w-full my-8" dir="ltr"><div class="sp-wrapper"><div class="shadow-lg dark:shadow-lg-dark rounded-lg" style="contain:content"><div class="bg-wash dark:bg-card-dark flex justify-between items-center relative z-10 border-b border-border dark:border-border-dark rounded-t-lg text-lg"><div class="flex-1 grow min-w-0 px-4 lg:px-6"><div><div class="relative overflow-hidden"><div class="w-[fit-content] invisible"><div class=" sp-tabs" translate="no"><div aria-label="Select active file" class=" sp-tabs-scrollable-container" role="tablist"><button aria-selected="false" class=" sp-tab-button" data-active="false" role="tab" title="/src/App.js" type="button">App.js</button><button aria-selected="true" class=" sp-tab-button" data-active="true" role="tab" title="/src/ChatRoom.js" type="button">ChatRoom.js</button><button aria-selected="false" class=" sp-tab-button" data-active="false" role="tab" title="/src/useChatRoom.js" type="button">useChatRoom.js</button><button aria-selected="false" class=" sp-tab-button" data-active="false" role="tab" title="/src/chat.js" type="button">chat.js</button><button aria-selected="false" class=" sp-tab-button" data-active="false" role="tab" title="/src/notifications.js" type="button">notifications.js</button></div></div></div><button class="absolute top-0 start-[2px]" id="headlessui-listbox-button-:R16aecq6:" aria-haspopup="true" aria-expanded="false" data-headlessui-state=""><span class="h-full py-2 px-1 mt-px -mb-px flex border-b text-link dark:text-link-dark border-link dark:border-link-dark items-center text-md leading-tight truncate" style="max-width:160px">ChatRoom.js<span class="ms-2"><svg class="rotate-0" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg></span></span></button></div></div></div><div class="px-3 flex items-center justify-end text-start" translate="yes"><button class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1" title="Reset Sandbox" type="button"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative"><path d="M13.8982 5.20844C12.4626 4.88688 10.9686 4.93769 9.55821 5.35604L11.8524 3.06184C11.8989 3.0154 11.9357 2.96028 11.9608 2.89961C11.986 2.83894 11.9989 2.77391 11.9989 2.70824C11.9989 2.64256 11.986 2.57754 11.9608 2.51686C11.9357 2.45619 11.8989 2.40107 11.8524 2.35464L11.1456 1.64784C11.0992 1.60139 11.0441 1.56455 10.9834 1.53942C10.9227 1.51428 10.8577 1.50134 10.792 1.50134C10.7263 1.50134 10.6613 1.51428 10.6006 1.53942C10.54 1.56455 10.4848 1.60139 10.4384 1.64784L6.14571 5.94054C6.00654 6.07969 5.89615 6.2449 5.82083 6.42673C5.74551 6.60855 5.70675 6.80343 5.70675 7.00024C5.70675 7.19704 5.74551 7.39192 5.82083 7.57374C5.89615 7.75557 6.00654 7.92078 6.14571 8.05994L10.4387 12.3529C10.5325 12.4465 10.6595 12.4991 10.792 12.4991C10.9245 12.4991 11.0516 12.4465 11.1453 12.3529L11.8527 11.6455C11.9463 11.5518 11.9989 11.4247 11.9989 11.2922C11.9989 11.1598 11.9463 11.0327 11.8527 10.9389L8.77481 7.86104C9.99795 7.16236 11.415 6.8801 12.8125 7.05678C14.21 7.23347 15.5122 7.85953 16.523 8.84064C17.5338 9.82176 18.1983 11.1048 18.4165 12.4964C18.6347 13.888 18.3947 15.3129 17.7328 16.5562C17.0708 17.7996 16.0227 18.7942 14.7463 19.3902C13.47 19.9861 12.0345 20.1511 10.6563 19.8603C9.27798 19.5695 8.03152 18.8387 7.10469 17.778C6.17786 16.7172 5.62086 15.384 5.51761 13.9791C5.51156 13.8512 5.45689 13.7303 5.36477 13.6413C5.27265 13.5522 5.15001 13.5017 5.02191 13.5H4.02081C3.95297 13.4996 3.88574 13.5129 3.8232 13.5392C3.76065 13.5655 3.70408 13.6042 3.6569 13.6529C3.60972 13.7017 3.57291 13.7595 3.54869 13.8228C3.52448 13.8862 3.51336 13.9538 3.51601 14.0216C3.61349 15.5965 4.1473 17.1132 5.0577 18.4019C5.9681 19.6906 7.21917 20.7006 8.6709 21.3188C10.1226 21.937 11.7178 22.139 13.2778 21.9022C14.8378 21.6654 16.3011 20.9992 17.504 19.978C18.7069 18.9569 19.6019 17.6212 20.0889 16.1203C20.5759 14.6195 20.6356 13.0128 20.2614 11.4799C19.8872 9.94705 19.0938 8.54858 17.97 7.44098C16.8462 6.33339 15.4363 5.56037 13.8982 5.20844V5.20844Z" fill="currentColor"></path></svg> Reset</button><a href="https://codesandbox.io/api/v1/sandboxes/define?undefined&environment=create-react-app" rel="noreferrer noopener" target="_blank" title="Open in CodeSandbox" class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1 ms-2 md:ms-1"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative top-[1px]"><path d="M20.5001 2H15.5001C15.3675 2 15.2403 2.05268 15.1465 2.14645C15.0528 2.24021 15.0001 2.36739 15.0001 2.5V3.5C15.0001 3.63261 15.0528 3.75979 15.1465 3.85355C15.2403 3.94732 15.3675 4 15.5001 4H18.5901L7.6501 14.94C7.60323 14.9865 7.56604 15.0418 7.54065 15.1027C7.51527 15.1636 7.5022 15.229 7.5022 15.295C7.5022 15.361 7.51527 15.4264 7.54065 15.4873C7.56604 15.5482 7.60323 15.6035 7.6501 15.65L8.3501 16.35C8.39658 16.3969 8.45188 16.4341 8.51281 16.4594C8.57374 16.4848 8.63909 16.4979 8.7051 16.4979C8.7711 16.4979 8.83646 16.4848 8.89738 16.4594C8.95831 16.4341 9.01362 16.3969 9.0601 16.35L20.0001 5.41V8.5C20.0001 8.63261 20.0528 8.75979 20.1465 8.85355C20.2403 8.94732 20.3675 9 20.5001 9H21.5001C21.6327 9 21.7599 8.94732 21.8537 8.85355C21.9474 8.75979 22.0001 8.63261 22.0001 8.5V3.5C22.0001 3.10218 21.8421 2.72064 21.5608 2.43934C21.2795 2.15804 20.8979 2 20.5001 2V2Z" fill="currentColor"></path><path d="M21.5 13H20.5C20.3674 13 20.2402 13.0527 20.1464 13.1464C20.0527 13.2402 20 13.3674 20 13.5V20H4V4H10.5C10.6326 4 10.7598 3.94732 10.8536 3.85355C10.9473 3.75979 11 3.63261 11 3.5V2.5C11 2.36739 10.9473 2.24021 10.8536 2.14645C10.7598 2.05268 10.6326 2 10.5 2H3.5C3.10218 2 2.72064 2.15804 2.43934 2.43934C2.15804 2.72064 2 3.10218 2 3.5V20.5C2 20.8978 2.15804 21.2794 2.43934 21.5607C2.72064 21.842 3.10218 22 3.5 22H20.5C20.8978 22 21.2794 21.842 21.5607 21.5607C21.842 21.2794 22 20.8978 22 20.5V13.5C22 13.3674 21.9473 13.2402 21.8536 13.1464C21.7598 13.0527 21.6326 13 21.5 13Z" fill="currentColor"></path></svg><span class="hidden md:block">Fork</span></a></div></div><div class=" sp-layout"><div class=" sp-editor sp-stack"><div class=" sp-code-editor"><div aria-autocomplete="list" aria-label="Code Editor for ChatRoom.js" aria-multiline="true" class="sp-pristine sp-javascript sp-cm" role="textbox" tabindex="0" translate="no"><pre class=" sp-pre-placeholder" style="margin-left:var(--sp-space-11)"><span class="sp-syntax-keyword">import</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">useState</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">from</span> <span class="sp-syntax-string">'react'</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">import</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">useChatRoom</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">from</span> <span class="sp-syntax-string">'./useChatRoom.js'</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">export</span> <span class="sp-syntax-keyword">default</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">ChatRoom</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">roomId</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setServerUrl</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'https://localhost:1234'</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-definition">useChatRoom</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">roomId</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-plain">roomId</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-property">serverUrl</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-plain">serverUrl</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">label</span><span class="sp-syntax-punctuation">></span> Server URL: <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">input</span> <span class="sp-syntax-property">value</span>=<span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-property">onChange</span>=<span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">e</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-definition">setServerUrl</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">e</span>.<span class="sp-syntax-property">target</span>.<span class="sp-syntax-property">value</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-punctuation">/></span> <span class="sp-syntax-punctuation"></</span><span class="sp-syntax-tag">label</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">h1</span><span class="sp-syntax-punctuation">></span>Welcome to the <span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">roomId</span><span class="sp-syntax-punctuation">}</span> room!<span class="sp-syntax-punctuation"></</span><span class="sp-syntax-tag">h1</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation"></</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> </pre></div></div></div><div class=" order-last xl:order-2 sp-stack"><div class="p-0 sm:p-2 md:p-4 lg:p-8 bg-card dark:bg-wash-dark h-full relative md:rounded-b-lg lg:rounded-b-none"><div style="position:relative"><iframe class="rounded-t-none bg-white md:shadow-md sm:rounded-lg w-full max-w-full transition-opacity absolute opacity-0 pointer-events-none duration-75" title="Sandbox Preview" style="height:15px;z-index:-1"></iframe></div></div></div><button translate="yes" class="sandpack-expand flex text-base justify-between dark:border-card-dark bg-wash dark:bg-card-dark items-center z-10 p-1 w-full order-2 xl:order-last border-b-1 relative top-0"><span class="flex p-2 focus:outline-none text-primary dark:text-primary-dark leading-[20px]"><svg class="rotate-0 inline me-1.5 text-xl" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg>Show more</span></button></div></div></div></div><!--/$--><div class="max-w-4xl ms-0 2xl:mx-auto"> <p class="whitespace-pre-wrap my-4">Notice how you’re taking the return value of one Hook:</p> <!--$--><div dir="ltr" class="sandpack sandpack--codeblock rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg my-8" style="contain:content"><div class="sp-wrapper"><div class="sp-stack"><div class="sp-code-editor"><pre class="sp-cm sp-pristine sp-javascript flex align-start"><code class="sp-pre-placeholder grow-[2]"><div class="cm-line "><span class="sp-syntax-keyword">export</span> <span class="sp-syntax-keyword">default</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">ChatRoom</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">roomId</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setServerUrl</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'https://localhost:1234'</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><br/></div><div class="cm-line "> <span class="sp-syntax-definition">useChatRoom</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-property">roomId</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-plain">roomId</span><span class="sp-syntax-punctuation">,</span><br/></div><div class="cm-line "> <span class="sp-syntax-property">serverUrl</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-plain">serverUrl</span><br/></div><div class="cm-line "> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-comment">// ...</span></div></code></pre></div></div></div></div><!--/$--> <p class="whitespace-pre-wrap my-4">and pass it as an input to another Hook:</p> <!--$--><div dir="ltr" class="sandpack sandpack--codeblock rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg my-8" style="contain:content"><div class="sp-wrapper"><div class="sp-stack"><div class="sp-code-editor"><pre class="sp-cm sp-pristine sp-javascript flex align-start"><code class="sp-pre-placeholder grow-[2]"><div class="cm-line "><span class="sp-syntax-keyword">export</span> <span class="sp-syntax-keyword">default</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">ChatRoom</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">roomId</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setServerUrl</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'https://localhost:1234'</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><br/></div><div class="cm-line "> <span class="sp-syntax-definition">useChatRoom</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-property">roomId</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-plain">roomId</span><span class="sp-syntax-punctuation">,</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-property">serverUrl</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-plain">serverUrl</span><br/></div><div class="cm-line "> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-comment">// ...</span></div></code></pre></div></div></div></div><!--/$--> <p class="whitespace-pre-wrap my-4">Every time your <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">ChatRoom</code> component re-renders, it passes the latest <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">roomId</code> and <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">serverUrl</code> to your Hook. This is why your Effect re-connects to the chat whenever their values are different after a re-render. (If you ever worked with audio or video processing software, chaining Hooks like this might remind you of chaining visual or audio effects. It’s as if the output of <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useState</code> “feeds into” the input of the <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useChatRoom</code>.)</p> <h3 id="passing-event-handlers-to-custom-hooks" class="mdx-heading text-2xl font-display leading-9 text-primary dark:text-primary-dark font-bold my-6">Passing event handlers to custom Hooks <a href="#passing-event-handlers-to-custom-hooks" aria-label="Link for Passing event handlers to custom Hooks " title="Link for Passing event handlers to custom Hooks " class="mdx-header-anchor inline-block"><svg width="1em" height="1em" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="text-gray-70 ms-2 h-5 w-5"><g fill="currentColor" fill-rule="evenodd"><path d="M7.778 7.975a2.5 2.5 0 0 0 .347-3.837L6.017 2.03a2.498 2.498 0 0 0-3.542-.007 2.5 2.5 0 0 0 .006 3.543l1.153 1.15c.07-.29.154-.563.25-.773.036-.077.084-.16.14-.25L3.18 4.85a1.496 1.496 0 0 1 .002-2.12 1.496 1.496 0 0 1 2.12 0l2.124 2.123a1.496 1.496 0 0 1-.333 2.37c.16.246.42.504.685.752z"></path><path d="M5.657 4.557a2.5 2.5 0 0 0-.347 3.837l2.108 2.108a2.498 2.498 0 0 0 3.542.007 2.5 2.5 0 0 0-.006-3.543L9.802 5.815c-.07.29-.154.565-.25.774-.036.076-.084.16-.14.25l.842.84c.585.587.59 1.532 0 2.122-.587.585-1.532.59-2.12 0L6.008 7.68a1.496 1.496 0 0 1 .332-2.372c-.16-.245-.42-.503-.685-.75z"></path></g></svg></a></h3> <div class="expandable-callout pt-8 pb-4 px-5 sm:px-8 my-8 relative rounded-none shadow-inner-border -mx-5 sm:mx-auto sm:rounded-2xl bg-yellow-5 dark:bg-yellow-60 dark:bg-opacity-20"><h3 class="text-2xl font-display font-bold text-yellow-50 dark:text-yellow-40"><svg class="inline me-2 mb-1 text-lg text-yellow-50 dark:text-yellow-40" width="2em" height="2em" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_40_48064)"><path d="M24 27C24 25.3431 25.3431 24 27 24H45C46.6569 24 48 25.3431 48 27C48 28.6569 46.6569 30 45 30H27C25.3431 30 24 28.6569 24 27Z" fill="currentColor"></path><path d="M24 39C24 37.3431 25.3431 36 27 36H39C40.6569 36 42 37.3431 42 39C42 40.6569 40.6569 42 39 42H27C25.3431 42 24 40.6569 24 39Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M12 18C12 13.0294 16.0294 9 21 9H51C55.9706 9 60 13.0294 60 18V54C60 58.9706 55.9706 63 51 63H21C16.0294 63 12 58.9706 12 54V18ZM21 15H51C52.6569 15 54 16.3431 54 18V54C54 55.6569 52.6569 57 51 57H21C19.3431 57 18 55.6569 18 54V18C18 16.3431 19.3431 15 21 15Z" fill="currentColor"></path></g><defs><clipPath id="clip0_40_48064"><rect width="72" height="72" fill="white"></rect></clipPath></defs></svg>Under Construction</h3><div class="relative"><div class="py-2"><p class="whitespace-pre-wrap my-4">This section describes an <strong class="font-bold">experimental API that has not yet been released</strong> in a stable version of React.</p></div></div></div> <p class="whitespace-pre-wrap my-4">As you start using <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useChatRoom</code> in more components, you might want to let components customize its behavior. For example, currently, the logic for what to do when a message arrives is hardcoded inside the Hook:</p> <!--$--><div dir="ltr" class="sandpack sandpack--codeblock rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg my-8" style="contain:content"><div class="sp-wrapper"><div class="sp-stack"><div class="sp-code-editor"><pre class="sp-cm sp-pristine sp-javascript flex align-start"><code class="sp-pre-placeholder grow-[2]"><div class="cm-line "><span class="sp-syntax-keyword">export</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">useChatRoom</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">serverUrl</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-property">roomId</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-definition">useEffect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">options</span> = <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-property">serverUrl</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">,</span><br/></div><div class="cm-line "> <span class="sp-syntax-property">roomId</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-plain">roomId</span><br/></div><div class="cm-line "> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">connection</span> = <span class="sp-syntax-definition">createConnection</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">options</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-plain">connection</span>.<span class="sp-syntax-property">connect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-plain">connection</span>.<span class="sp-syntax-property">on</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'message'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">msg</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-definition">showNotification</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'New message: '</span> + <span class="sp-syntax-plain">msg</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-plain">connection</span>.<span class="sp-syntax-property">disconnect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">roomId</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">]</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><span class="sp-syntax-punctuation">}</span></div></code></pre></div></div></div></div><!--/$--> <p class="whitespace-pre-wrap my-4">Let’s say you want to move this logic back to your component:</p> <!--$--><div dir="ltr" class="sandpack sandpack--codeblock rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg my-8" style="contain:content"><div class="sp-wrapper"><div class="sp-stack"><div class="sp-code-editor"><pre class="sp-cm sp-pristine sp-javascript flex align-start"><code class="sp-pre-placeholder grow-[2]"><div class="cm-line "><span class="sp-syntax-keyword">export</span> <span class="sp-syntax-keyword">default</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">ChatRoom</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">roomId</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setServerUrl</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'https://localhost:1234'</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><br/></div><div class="cm-line "> <span class="sp-syntax-definition">useChatRoom</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-property">roomId</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-plain">roomId</span><span class="sp-syntax-punctuation">,</span><br/></div><div class="cm-line "> <span class="sp-syntax-property">serverUrl</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">,</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-property">onReceiveMessage</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">msg</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-definition">showNotification</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'New message: '</span> + <span class="sp-syntax-plain">msg</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><br/></div><div class="cm-line "> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-comment">// ...</span></div></code></pre></div></div></div></div><!--/$--> <p class="whitespace-pre-wrap my-4">To make this work, change your custom Hook to take <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">onReceiveMessage</code> as one of its named options:</p> <!--$--><div dir="ltr" class="sandpack sandpack--codeblock rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg my-8" style="contain:content"><div class="sp-wrapper"><div class="sp-stack"><div class="sp-code-editor"><pre class="sp-cm sp-pristine sp-javascript flex align-start"><code class="sp-pre-placeholder grow-[2]"><div class="cm-line bg-github-highlight dark:bg-opacity-10"><span class="sp-syntax-keyword">export</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">useChatRoom</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">serverUrl</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-property">roomId</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-property">onReceiveMessage</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-definition">useEffect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">options</span> = <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-property">serverUrl</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">,</span><br/></div><div class="cm-line "> <span class="sp-syntax-property">roomId</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-plain">roomId</span><br/></div><div class="cm-line "> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">connection</span> = <span class="sp-syntax-definition">createConnection</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">options</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-plain">connection</span>.<span class="sp-syntax-property">connect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-plain">connection</span>.<span class="sp-syntax-property">on</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'message'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">msg</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-definition">onReceiveMessage</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">msg</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-plain">connection</span>.<span class="sp-syntax-property">disconnect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">roomId</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">onReceiveMessage</span><span class="sp-syntax-punctuation">]</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-comment">// ✅ All dependencies declared</span><br/></div><div class="cm-line "><span class="sp-syntax-punctuation">}</span></div></code></pre></div></div></div></div><!--/$--> <p class="whitespace-pre-wrap my-4">This will work, but there’s one more improvement you can do when your custom Hook accepts event handlers.</p> <p class="whitespace-pre-wrap my-4">Adding a dependency on <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">onReceiveMessage</code> is not ideal because it will cause the chat to re-connect every time the component re-renders. <a class="inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal" href="/learn/removing-effect-dependencies#wrapping-an-event-handler-from-the-props">Wrap this event handler into an Effect Event to remove it from the dependencies:</a></p> <!--$--><div dir="ltr" class="sandpack sandpack--codeblock rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg my-8" style="contain:content"><div class="sp-wrapper"><div class="sp-stack"><div class="sp-code-editor"><pre class="sp-cm sp-pristine sp-javascript flex align-start"><code class="sp-pre-placeholder grow-[2]"><div class="cm-line bg-github-highlight dark:bg-opacity-10"><span class="sp-syntax-keyword">import</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">useEffect</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">useEffectEvent</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">from</span> <span class="sp-syntax-string">'react'</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><span class="sp-syntax-comment">// ...</span><br/></div><div class="cm-line "><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"><span class="sp-syntax-keyword">export</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">useChatRoom</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">serverUrl</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-property">roomId</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-property">onReceiveMessage</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">onMessage</span> = <span class="sp-syntax-definition">useEffectEvent</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">onReceiveMessage</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><br/></div><div class="cm-line "> <span class="sp-syntax-definition">useEffect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">options</span> = <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-property">serverUrl</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">,</span><br/></div><div class="cm-line "> <span class="sp-syntax-property">roomId</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-plain">roomId</span><br/></div><div class="cm-line "> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">connection</span> = <span class="sp-syntax-definition">createConnection</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">options</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-plain">connection</span>.<span class="sp-syntax-property">connect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-plain">connection</span>.<span class="sp-syntax-property">on</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'message'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">msg</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-definition">onMessage</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">msg</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-plain">connection</span>.<span class="sp-syntax-property">disconnect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">roomId</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">]</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-comment">// ✅ All dependencies declared</span><br/></div><div class="cm-line "><span class="sp-syntax-punctuation">}</span></div></code></pre></div></div></div></div><!--/$--> <p class="whitespace-pre-wrap my-4">Now the chat won’t re-connect every time that the <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">ChatRoom</code> component re-renders. Here is a fully working demo of passing an event handler to a custom Hook that you can play with:</p> </div><!--$--><div class="sandpack sandpack--playground w-full my-8" dir="ltr"><div class="sp-wrapper"><div class="shadow-lg dark:shadow-lg-dark rounded-lg" style="contain:content"><div class="bg-wash dark:bg-card-dark flex justify-between items-center relative z-10 border-b border-border dark:border-border-dark rounded-t-lg text-lg"><div class="flex-1 grow min-w-0 px-4 lg:px-6"><div><div class="relative overflow-hidden"><div class="w-[fit-content] invisible"><div class=" sp-tabs" translate="no"><div aria-label="Select active file" class=" sp-tabs-scrollable-container" role="tablist"><button aria-selected="false" class=" sp-tab-button" data-active="false" role="tab" title="/src/App.js" type="button">App.js</button><button aria-selected="true" class=" sp-tab-button" data-active="true" role="tab" title="/src/ChatRoom.js" type="button">ChatRoom.js</button><button aria-selected="false" class=" sp-tab-button" data-active="false" role="tab" title="/src/useChatRoom.js" type="button">useChatRoom.js</button><button aria-selected="false" class=" sp-tab-button" data-active="false" role="tab" title="/src/chat.js" type="button">chat.js</button><button aria-selected="false" class=" sp-tab-button" data-active="false" role="tab" title="/src/notifications.js" type="button">notifications.js</button></div></div></div><button class="absolute top-0 start-[2px]" id="headlessui-listbox-button-:R16agcq6:" aria-haspopup="true" aria-expanded="false" data-headlessui-state=""><span class="h-full py-2 px-1 mt-px -mb-px flex border-b text-link dark:text-link-dark border-link dark:border-link-dark items-center text-md leading-tight truncate" style="max-width:160px">ChatRoom.js<span class="ms-2"><svg class="rotate-0" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg></span></span></button></div></div></div><div class="px-3 flex items-center justify-end text-start" translate="yes"><button class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1" title="Reset Sandbox" type="button"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative"><path d="M13.8982 5.20844C12.4626 4.88688 10.9686 4.93769 9.55821 5.35604L11.8524 3.06184C11.8989 3.0154 11.9357 2.96028 11.9608 2.89961C11.986 2.83894 11.9989 2.77391 11.9989 2.70824C11.9989 2.64256 11.986 2.57754 11.9608 2.51686C11.9357 2.45619 11.8989 2.40107 11.8524 2.35464L11.1456 1.64784C11.0992 1.60139 11.0441 1.56455 10.9834 1.53942C10.9227 1.51428 10.8577 1.50134 10.792 1.50134C10.7263 1.50134 10.6613 1.51428 10.6006 1.53942C10.54 1.56455 10.4848 1.60139 10.4384 1.64784L6.14571 5.94054C6.00654 6.07969 5.89615 6.2449 5.82083 6.42673C5.74551 6.60855 5.70675 6.80343 5.70675 7.00024C5.70675 7.19704 5.74551 7.39192 5.82083 7.57374C5.89615 7.75557 6.00654 7.92078 6.14571 8.05994L10.4387 12.3529C10.5325 12.4465 10.6595 12.4991 10.792 12.4991C10.9245 12.4991 11.0516 12.4465 11.1453 12.3529L11.8527 11.6455C11.9463 11.5518 11.9989 11.4247 11.9989 11.2922C11.9989 11.1598 11.9463 11.0327 11.8527 10.9389L8.77481 7.86104C9.99795 7.16236 11.415 6.8801 12.8125 7.05678C14.21 7.23347 15.5122 7.85953 16.523 8.84064C17.5338 9.82176 18.1983 11.1048 18.4165 12.4964C18.6347 13.888 18.3947 15.3129 17.7328 16.5562C17.0708 17.7996 16.0227 18.7942 14.7463 19.3902C13.47 19.9861 12.0345 20.1511 10.6563 19.8603C9.27798 19.5695 8.03152 18.8387 7.10469 17.778C6.17786 16.7172 5.62086 15.384 5.51761 13.9791C5.51156 13.8512 5.45689 13.7303 5.36477 13.6413C5.27265 13.5522 5.15001 13.5017 5.02191 13.5H4.02081C3.95297 13.4996 3.88574 13.5129 3.8232 13.5392C3.76065 13.5655 3.70408 13.6042 3.6569 13.6529C3.60972 13.7017 3.57291 13.7595 3.54869 13.8228C3.52448 13.8862 3.51336 13.9538 3.51601 14.0216C3.61349 15.5965 4.1473 17.1132 5.0577 18.4019C5.9681 19.6906 7.21917 20.7006 8.6709 21.3188C10.1226 21.937 11.7178 22.139 13.2778 21.9022C14.8378 21.6654 16.3011 20.9992 17.504 19.978C18.7069 18.9569 19.6019 17.6212 20.0889 16.1203C20.5759 14.6195 20.6356 13.0128 20.2614 11.4799C19.8872 9.94705 19.0938 8.54858 17.97 7.44098C16.8462 6.33339 15.4363 5.56037 13.8982 5.20844V5.20844Z" fill="currentColor"></path></svg> Reset</button><a href="https://codesandbox.io/api/v1/sandboxes/define?undefined&environment=create-react-app" rel="noreferrer noopener" target="_blank" title="Open in CodeSandbox" class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1 ms-2 md:ms-1"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative top-[1px]"><path d="M20.5001 2H15.5001C15.3675 2 15.2403 2.05268 15.1465 2.14645C15.0528 2.24021 15.0001 2.36739 15.0001 2.5V3.5C15.0001 3.63261 15.0528 3.75979 15.1465 3.85355C15.2403 3.94732 15.3675 4 15.5001 4H18.5901L7.6501 14.94C7.60323 14.9865 7.56604 15.0418 7.54065 15.1027C7.51527 15.1636 7.5022 15.229 7.5022 15.295C7.5022 15.361 7.51527 15.4264 7.54065 15.4873C7.56604 15.5482 7.60323 15.6035 7.6501 15.65L8.3501 16.35C8.39658 16.3969 8.45188 16.4341 8.51281 16.4594C8.57374 16.4848 8.63909 16.4979 8.7051 16.4979C8.7711 16.4979 8.83646 16.4848 8.89738 16.4594C8.95831 16.4341 9.01362 16.3969 9.0601 16.35L20.0001 5.41V8.5C20.0001 8.63261 20.0528 8.75979 20.1465 8.85355C20.2403 8.94732 20.3675 9 20.5001 9H21.5001C21.6327 9 21.7599 8.94732 21.8537 8.85355C21.9474 8.75979 22.0001 8.63261 22.0001 8.5V3.5C22.0001 3.10218 21.8421 2.72064 21.5608 2.43934C21.2795 2.15804 20.8979 2 20.5001 2V2Z" fill="currentColor"></path><path d="M21.5 13H20.5C20.3674 13 20.2402 13.0527 20.1464 13.1464C20.0527 13.2402 20 13.3674 20 13.5V20H4V4H10.5C10.6326 4 10.7598 3.94732 10.8536 3.85355C10.9473 3.75979 11 3.63261 11 3.5V2.5C11 2.36739 10.9473 2.24021 10.8536 2.14645C10.7598 2.05268 10.6326 2 10.5 2H3.5C3.10218 2 2.72064 2.15804 2.43934 2.43934C2.15804 2.72064 2 3.10218 2 3.5V20.5C2 20.8978 2.15804 21.2794 2.43934 21.5607C2.72064 21.842 3.10218 22 3.5 22H20.5C20.8978 22 21.2794 21.842 21.5607 21.5607C21.842 21.2794 22 20.8978 22 20.5V13.5C22 13.3674 21.9473 13.2402 21.8536 13.1464C21.7598 13.0527 21.6326 13 21.5 13Z" fill="currentColor"></path></svg><span class="hidden md:block">Fork</span></a></div></div><div class=" sp-layout"><div class=" sp-editor sp-stack"><div class=" sp-code-editor"><div aria-autocomplete="list" aria-label="Code Editor for ChatRoom.js" aria-multiline="true" class="sp-pristine sp-javascript sp-cm" role="textbox" tabindex="0" translate="no"><pre class=" sp-pre-placeholder" style="margin-left:var(--sp-space-11)"><span class="sp-syntax-keyword">import</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">useState</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">from</span> <span class="sp-syntax-string">'react'</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">import</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">useChatRoom</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">from</span> <span class="sp-syntax-string">'./useChatRoom.js'</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">import</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">showNotification</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">from</span> <span class="sp-syntax-string">'./notifications.js'</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">export</span> <span class="sp-syntax-keyword">default</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">ChatRoom</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">roomId</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setServerUrl</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'https://localhost:1234'</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-definition">useChatRoom</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">roomId</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-plain">roomId</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-property">serverUrl</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-property">onReceiveMessage</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">msg</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-definition">showNotification</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'New message: '</span> + <span class="sp-syntax-plain">msg</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">label</span><span class="sp-syntax-punctuation">></span> Server URL: <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">input</span> <span class="sp-syntax-property">value</span>=<span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-property">onChange</span>=<span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">e</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-definition">setServerUrl</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">e</span>.<span class="sp-syntax-property">target</span>.<span class="sp-syntax-property">value</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-punctuation">/></span> <span class="sp-syntax-punctuation"></</span><span class="sp-syntax-tag">label</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">h1</span><span class="sp-syntax-punctuation">></span>Welcome to the <span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">roomId</span><span class="sp-syntax-punctuation">}</span> room!<span class="sp-syntax-punctuation"></</span><span class="sp-syntax-tag">h1</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation"></</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> </pre></div></div></div><div class=" order-last xl:order-2 sp-stack"><div class="p-0 sm:p-2 md:p-4 lg:p-8 bg-card dark:bg-wash-dark h-full relative md:rounded-b-lg lg:rounded-b-none"><div style="position:relative"><iframe class="rounded-t-none bg-white md:shadow-md sm:rounded-lg w-full max-w-full transition-opacity absolute opacity-0 pointer-events-none duration-75" title="Sandbox Preview" style="height:15px;z-index:-1"></iframe></div></div></div><button translate="yes" class="sandpack-expand flex text-base justify-between dark:border-card-dark bg-wash dark:bg-card-dark items-center z-10 p-1 w-full order-2 xl:order-last border-b-1 relative top-0"><span class="flex p-2 focus:outline-none text-primary dark:text-primary-dark leading-[20px]"><svg class="rotate-0 inline me-1.5 text-xl" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg>Show more</span></button></div></div></div></div><!--/$--><div class="max-w-4xl ms-0 2xl:mx-auto"> <p class="whitespace-pre-wrap my-4">Notice how you no longer need to know <em>how</em> <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useChatRoom</code> works in order to use it. You could add it to any other component, pass any other options, and it would work the same way. That’s the power of custom Hooks.</p> <h2 id="when-to-use-custom-hooks" class="mdx-heading text-3xl font-display leading-10 text-primary dark:text-primary-dark font-bold my-6">When to use custom Hooks <a href="#when-to-use-custom-hooks" aria-label="Link for When to use custom Hooks " title="Link for When to use custom Hooks " class="mdx-header-anchor inline-block"><svg width="1em" height="1em" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="text-gray-70 ms-2 h-5 w-5"><g fill="currentColor" fill-rule="evenodd"><path d="M7.778 7.975a2.5 2.5 0 0 0 .347-3.837L6.017 2.03a2.498 2.498 0 0 0-3.542-.007 2.5 2.5 0 0 0 .006 3.543l1.153 1.15c.07-.29.154-.563.25-.773.036-.077.084-.16.14-.25L3.18 4.85a1.496 1.496 0 0 1 .002-2.12 1.496 1.496 0 0 1 2.12 0l2.124 2.123a1.496 1.496 0 0 1-.333 2.37c.16.246.42.504.685.752z"></path><path d="M5.657 4.557a2.5 2.5 0 0 0-.347 3.837l2.108 2.108a2.498 2.498 0 0 0 3.542.007 2.5 2.5 0 0 0-.006-3.543L9.802 5.815c-.07.29-.154.565-.25.774-.036.076-.084.16-.14.25l.842.84c.585.587.59 1.532 0 2.122-.587.585-1.532.59-2.12 0L6.008 7.68a1.496 1.496 0 0 1 .332-2.372c-.16-.245-.42-.503-.685-.75z"></path></g></svg></a></h2> <p class="whitespace-pre-wrap my-4">You don’t need to extract a custom Hook for every little duplicated bit of code. Some duplication is fine. For example, extracting a <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useFormInput</code> Hook to wrap a single <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useState</code> call like earlier is probably unnecessary.</p> <p class="whitespace-pre-wrap my-4">However, whenever you write an Effect, consider whether it would be clearer to also wrap it in a custom Hook. <a class="inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal" href="/learn/you-might-not-need-an-effect">You shouldn’t need Effects very often,</a> so if you’re writing one, it means that you need to “step outside React” to synchronize with some external system or to do something that React doesn’t have a built-in API for. Wrapping it into a custom Hook lets you precisely communicate your intent and how the data flows through it.</p> <p class="whitespace-pre-wrap my-4">For example, consider a <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">ShippingForm</code> component that displays two dropdowns: one shows the list of cities, and another shows the list of areas in the selected city. You might start with some code that looks like this:</p> <!--$--><div dir="ltr" class="sandpack sandpack--codeblock rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg my-8" style="contain:content"><div class="sp-wrapper"><div class="sp-stack"><div class="sp-code-editor"><pre class="sp-cm sp-pristine sp-javascript flex align-start"><code class="sp-pre-placeholder grow-[2]"><div class="cm-line "><span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">ShippingForm</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">country</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">cities</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setCities</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-keyword">null</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-comment">// This Effect fetches cities for a country</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-definition">useEffect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">let</span> <span class="sp-syntax-plain">ignore</span> = <span class="sp-syntax-static">false</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-definition">fetch</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">`/api/cities?country=</span><span class="sp-syntax-punctuation">${</span><span class="sp-syntax-plain">country</span><span class="sp-syntax-punctuation">}</span><span class="sp-syntax-string">`</span><span class="sp-syntax-punctuation">)</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> .<span class="sp-syntax-property">then</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">response</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-plain">response</span>.<span class="sp-syntax-property">json</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">)</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> .<span class="sp-syntax-property">then</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">json</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">if</span> <span class="sp-syntax-punctuation">(</span>!<span class="sp-syntax-plain">ignore</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-definition">setCities</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">json</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-plain">ignore</span> = <span class="sp-syntax-static">true</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">country</span><span class="sp-syntax-punctuation">]</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">city</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setCity</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-keyword">null</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">areas</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setAreas</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-keyword">null</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-comment">// This Effect fetches areas for the selected city</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-definition">useEffect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">if</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">city</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">let</span> <span class="sp-syntax-plain">ignore</span> = <span class="sp-syntax-static">false</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-definition">fetch</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">`/api/areas?city=</span><span class="sp-syntax-punctuation">${</span><span class="sp-syntax-plain">city</span><span class="sp-syntax-punctuation">}</span><span class="sp-syntax-string">`</span><span class="sp-syntax-punctuation">)</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> .<span class="sp-syntax-property">then</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">response</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-plain">response</span>.<span class="sp-syntax-property">json</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">)</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> .<span class="sp-syntax-property">then</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">json</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">if</span> <span class="sp-syntax-punctuation">(</span>!<span class="sp-syntax-plain">ignore</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-definition">setAreas</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">json</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-plain">ignore</span> = <span class="sp-syntax-static">true</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">city</span><span class="sp-syntax-punctuation">]</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><br/></div><div class="cm-line "> <span class="sp-syntax-comment">// ...</span></div></code></pre></div></div></div></div><!--/$--> <p class="whitespace-pre-wrap my-4">Although this code is quite repetitive, <a class="inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal" href="/learn/removing-effect-dependencies#is-your-effect-doing-several-unrelated-things">it’s correct to keep these Effects separate from each other.</a> They synchronize two different things, so you shouldn’t merge them into one Effect. Instead, you can simplify the <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">ShippingForm</code> component above by extracting the common logic between them into your own <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useData</code> Hook:</p> <!--$--><div dir="ltr" class="sandpack sandpack--codeblock rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg my-8" style="contain:content"><div class="sp-wrapper"><div class="sp-stack"><div class="sp-code-editor"><pre class="sp-cm sp-pristine sp-javascript flex align-start"><code class="sp-pre-placeholder grow-[2]"><div class="cm-line "><span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">useData</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">url</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">data</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setData</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-keyword">null</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-definition">useEffect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">if</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">url</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">let</span> <span class="sp-syntax-plain">ignore</span> = <span class="sp-syntax-static">false</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-definition">fetch</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">url</span><span class="sp-syntax-punctuation">)</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> .<span class="sp-syntax-property">then</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">response</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-plain">response</span>.<span class="sp-syntax-property">json</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">)</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> .<span class="sp-syntax-property">then</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">json</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">if</span> <span class="sp-syntax-punctuation">(</span>!<span class="sp-syntax-plain">ignore</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-definition">setData</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">json</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-plain">ignore</span> = <span class="sp-syntax-static">true</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">url</span><span class="sp-syntax-punctuation">]</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-plain">data</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><span class="sp-syntax-punctuation">}</span></div></code></pre></div></div></div></div><!--/$--> <p class="whitespace-pre-wrap my-4">Now you can replace both Effects in the <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">ShippingForm</code> components with calls to <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useData</code>:</p> <!--$--><div dir="ltr" class="sandpack sandpack--codeblock rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg my-8" style="contain:content"><div class="sp-wrapper"><div class="sp-stack"><div class="sp-code-editor"><pre class="sp-cm sp-pristine sp-javascript flex align-start"><code class="sp-pre-placeholder grow-[2]"><div class="cm-line "><span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">ShippingForm</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">country</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">cities</span> = <span class="sp-syntax-definition">useData</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">`/api/cities?country=</span><span class="sp-syntax-punctuation">${</span><span class="sp-syntax-plain">country</span><span class="sp-syntax-punctuation">}</span><span class="sp-syntax-string">`</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">city</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setCity</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-keyword">null</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">areas</span> = <span class="sp-syntax-definition">useData</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">city</span> ? <span class="sp-syntax-string">`/api/areas?city=</span><span class="sp-syntax-punctuation">${</span><span class="sp-syntax-plain">city</span><span class="sp-syntax-punctuation">}</span><span class="sp-syntax-string">`</span> : <span class="sp-syntax-keyword">null</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-comment">// ...</span></div></code></pre></div></div></div></div><!--/$--> <p class="whitespace-pre-wrap my-4">Extracting a custom Hook makes the data flow explicit. You feed the <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">url</code> in and you get the <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">data</code> out. By “hiding” your Effect inside <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useData</code>, you also prevent someone working on the <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">ShippingForm</code> component from adding <a class="inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal" href="/learn/removing-effect-dependencies">unnecessary dependencies</a> to it. With time, most of your app’s Effects will be in custom Hooks.</p> <details class="my-12 rounded-2xl shadow-inner-border dark:shadow-inner-border-dark relative dark:bg-opacity-20 dark:bg-purple-60 bg-purple-5"><summary class="list-none p-8" tabindex="-1"><h5 class="mb-4 uppercase font-bold flex items-center text-sm dark:text-purple-30 text-purple-50"><svg class="inline me-2 dark:text-purple-30 text-purple-40" width="1.5em" height="1.5em" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M34.7409 59.7228L32.9567 58.9094C27.2672 56.3157 20.7328 56.3157 15.0433 58.9094C12.6018 60.0224 9.39163 59.0275 8.44602 56.0621C7.45647 52.9589 5.99975 46.5898 6 35.9997C6.00029 23.5648 8.00803 18.3599 9.11099 16.4196C9.67795 15.4222 10.5255 14.8455 11.2254 14.5264L12.0179 14.1651C19.6351 10.6926 28.4011 10.6738 36 14.1733C43.5989 10.6738 52.3649 10.6926 59.9821 14.1651L60.7746 14.5264C61.4745 14.8455 62.3221 15.4222 62.889 16.4196C63.992 18.3599 65.9997 23.5648 66 35.9997C66.0002 46.5898 64.5435 52.9589 63.554 56.0621C62.6084 59.0275 59.3982 60.0224 56.9567 58.9094C51.2672 56.3157 44.7328 56.3157 39.0433 58.9094L37.2591 59.7228C37.1986 59.7508 37.1373 59.7767 37.0753 59.8004C36.4484 60.0411 35.7556 60.0653 35.1102 59.8648C34.9847 59.8258 34.8613 59.7784 34.7409 59.7228ZM14.5068 19.6246C20.3733 16.9501 27.0874 16.8775 33 19.4067V52.473C26.7613 50.32 19.9378 50.471 13.7811 52.9261C13.0005 49.9843 11.9998 44.547 12 35.9998C12.0002 25.5786 13.4879 21.1893 14.1179 19.8018L14.5068 19.6246ZM39 52.473C45.2387 50.32 52.0622 50.471 58.2189 52.9261C58.9995 49.9843 60.0002 44.547 60 35.9998C59.9998 25.5786 58.5121 21.1893 57.8821 19.8018L57.4932 19.6246C51.6267 16.9501 44.9126 16.8775 39 19.4067V52.473Z" fill="currentColor"></path></svg>Deep Dive</h5><div class="mb-4"><h4 id="keep-your-custom-hooks-focused-on-concrete-high-level-use-cases" class="mdx-heading text-xl font-bold text-primary dark:text-primary-dark text-xl font-display font-bold leading-9 my-4">Keep your custom Hooks focused on concrete high-level use cases <a href="#keep-your-custom-hooks-focused-on-concrete-high-level-use-cases" aria-label="Link for Keep your custom Hooks focused on concrete high-level use cases " title="Link for Keep your custom Hooks focused on concrete high-level use cases " class="mdx-header-anchor inline-block"><svg width="1em" height="1em" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="text-gray-70 ms-2 h-5 w-5"><g fill="currentColor" fill-rule="evenodd"><path d="M7.778 7.975a2.5 2.5 0 0 0 .347-3.837L6.017 2.03a2.498 2.498 0 0 0-3.542-.007 2.5 2.5 0 0 0 .006 3.543l1.153 1.15c.07-.29.154-.563.25-.773.036-.077.084-.16.14-.25L3.18 4.85a1.496 1.496 0 0 1 .002-2.12 1.496 1.496 0 0 1 2.12 0l2.124 2.123a1.496 1.496 0 0 1-.333 2.37c.16.246.42.504.685.752z"></path><path d="M5.657 4.557a2.5 2.5 0 0 0-.347 3.837l2.108 2.108a2.498 2.498 0 0 0 3.542.007 2.5 2.5 0 0 0-.006-3.543L9.802 5.815c-.07.29-.154.565-.25.774-.036.076-.084.16-.14.25l.842.84c.585.587.59 1.532 0 2.122-.587.585-1.532.59-2.12 0L6.008 7.68a1.496 1.496 0 0 1 .332-2.372c-.16-.245-.42-.503-.685-.75z"></path></g></svg></a></h4></div><button class="bg-purple-50 border-purple-50 hover:bg-purple-40 focus:bg-purple-50 active:bg-purple-50 text-base leading-tight font-bold rounded-full py-2 px-4 focus:outline focus:outline-offset-2 focus:outline-link dark:focus:outline-link-dark inline-flex items-center my-1 bg-link border-link text-white hover:bg-link focus:bg-link active:bg-link"><span class="me-1"><svg class="rotate-0" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg></span>Show Details</button></summary><div class="p-8 border-t dark:border-purple-60 border-purple-10 "><p class="whitespace-pre-wrap my-4">Start by choosing your custom Hook’s name. If you struggle to pick a clear name, it might mean that your Effect is too coupled to the rest of your component’s logic, and is not yet ready to be extracted.</p><p class="whitespace-pre-wrap my-4">Ideally, your custom Hook’s name should be clear enough that even a person who doesn’t write code often could have a good guess about what your custom Hook does, what it takes, and what it returns:</p><ul class="ms-6 my-3 list-disc"> <li class="leading-relaxed mb-1">✅ <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useData(url)</code></li> <li class="leading-relaxed mb-1">✅ <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useImpressionLog(eventName, extraData)</code></li> <li class="leading-relaxed mb-1">✅ <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useChatRoom(options)</code></li> </ul><p class="whitespace-pre-wrap my-4">When you synchronize with an external system, your custom Hook name may be more technical and use jargon specific to that system. It’s good as long as it would be clear to a person familiar with that system:</p><ul class="ms-6 my-3 list-disc"> <li class="leading-relaxed mb-1">✅ <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useMediaQuery(query)</code></li> <li class="leading-relaxed mb-1">✅ <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useSocket(url)</code></li> <li class="leading-relaxed mb-1">✅ <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useIntersectionObserver(ref, options)</code></li> </ul><p class="whitespace-pre-wrap my-4"><strong class="font-bold">Keep custom Hooks focused on concrete high-level use cases.</strong> Avoid creating and using custom “lifecycle” Hooks that act as alternatives and convenience wrappers for the <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useEffect</code> API itself:</p><ul class="ms-6 my-3 list-disc"> <li class="leading-relaxed mb-1">🔴 <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useMount(fn)</code></li> <li class="leading-relaxed mb-1">🔴 <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useEffectOnce(fn)</code></li> <li class="leading-relaxed mb-1">🔴 <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useUpdateEffect(fn)</code></li> </ul><p class="whitespace-pre-wrap my-4">For example, this <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useMount</code> Hook tries to ensure some code only runs “on mount”:</p><!--$--><div dir="ltr" class="sandpack sandpack--codeblock rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg my-8" style="contain:content"><div class="sp-wrapper"><div class="sp-stack"><div class="sp-code-editor"><pre class="sp-cm sp-pristine sp-javascript flex align-start"><code class="sp-pre-placeholder grow-[2]"><div class="cm-line "><span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">ChatRoom</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">roomId</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setServerUrl</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'https://localhost:1234'</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-comment">// 🔴 Avoid: using custom "lifecycle" Hooks</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-definition">useMount</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">connection</span> = <span class="sp-syntax-definition">createConnection</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">roomId</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-property">serverUrl</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-plain">connection</span>.<span class="sp-syntax-property">connect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><br/></div><div class="cm-line "> <span class="sp-syntax-definition">post</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'/analytics/event'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">eventName</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-string">'visit_chat'</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-comment">// ...</span><br/></div><div class="cm-line "><span class="sp-syntax-punctuation">}</span><br/></div><div class="cm-line "><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"><span class="sp-syntax-comment">// 🔴 Avoid: creating custom "lifecycle" Hooks</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"><span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">useMount</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">fn</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-definition">useEffect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-definition">fn</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-punctuation">]</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-comment">// 🔴 React Hook useEffect has a missing dependency: 'fn'</span><br/></div><div class="cm-line "><span class="sp-syntax-punctuation">}</span></div></code></pre></div></div></div></div><!--/$--><p class="whitespace-pre-wrap my-4"><strong class="font-bold">Custom “lifecycle” Hooks like <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useMount</code> don’t fit well into the React paradigm.</strong> For example, this code example has a mistake (it doesn’t “react” to <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">roomId</code> or <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">serverUrl</code> changes), but the linter won’t warn you about it because the linter only checks direct <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useEffect</code> calls. It won’t know about your Hook.</p><p class="whitespace-pre-wrap my-4">If you’re writing an Effect, start by using the React API directly:</p><!--$--><div dir="ltr" class="sandpack sandpack--codeblock rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg my-8" style="contain:content"><div class="sp-wrapper"><div class="sp-stack"><div class="sp-code-editor"><pre class="sp-cm sp-pristine sp-javascript flex align-start"><code class="sp-pre-placeholder grow-[2]"><div class="cm-line "><span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">ChatRoom</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">roomId</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setServerUrl</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'https://localhost:1234'</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><br/></div><div class="cm-line "> <span class="sp-syntax-comment">// ✅ Good: two raw Effects separated by purpose</span><br/></div><div class="cm-line "><br/></div><div class="cm-line "> <span class="sp-syntax-definition">useEffect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">connection</span> = <span class="sp-syntax-definition">createConnection</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">serverUrl</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-property">roomId</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-plain">connection</span>.<span class="sp-syntax-property">connect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-plain">connection</span>.<span class="sp-syntax-property">disconnect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">roomId</span><span class="sp-syntax-punctuation">]</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><br/></div><div class="cm-line "> <span class="sp-syntax-definition">useEffect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-definition">post</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'/analytics/event'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">eventName</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-string">'visit_chat'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-property">roomId</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">roomId</span><span class="sp-syntax-punctuation">]</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><br/></div><div class="cm-line "> <span class="sp-syntax-comment">// ...</span><br/></div><div class="cm-line "><span class="sp-syntax-punctuation">}</span></div></code></pre></div></div></div></div><!--/$--><p class="whitespace-pre-wrap my-4">Then, you can (but don’t have to) extract custom Hooks for different high-level use cases:</p><!--$--><div dir="ltr" class="sandpack sandpack--codeblock rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg my-8" style="contain:content"><div class="sp-wrapper"><div class="sp-stack"><div class="sp-code-editor"><pre class="sp-cm sp-pristine sp-javascript flex align-start"><code class="sp-pre-placeholder grow-[2]"><div class="cm-line "><span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">ChatRoom</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">roomId</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">serverUrl</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setServerUrl</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'https://localhost:1234'</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><br/></div><div class="cm-line "> <span class="sp-syntax-comment">// ✅ Great: custom Hooks named after their purpose</span><br/></div><div class="cm-line "> <span class="sp-syntax-definition">useChatRoom</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">serverUrl</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-property">roomId</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-definition">useImpressionLog</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'visit_chat'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">roomId</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-comment">// ...</span><br/></div><div class="cm-line "><span class="sp-syntax-punctuation">}</span></div></code></pre></div></div></div></div><!--/$--><p class="whitespace-pre-wrap my-4"><strong class="font-bold">A good custom Hook makes the calling code more declarative by constraining what it does.</strong> For example, <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useChatRoom(options)</code> can only connect to the chat room, while <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useImpressionLog(eventName, extraData)</code> can only send an impression log to the analytics. If your custom Hook API doesn’t constrain the use cases and is very abstract, in the long run it’s likely to introduce more problems than it solves.</p></div></details> <h3 id="custom-hooks-help-you-migrate-to-better-patterns" class="mdx-heading text-2xl font-display leading-9 text-primary dark:text-primary-dark font-bold my-6">Custom Hooks help you migrate to better patterns <a href="#custom-hooks-help-you-migrate-to-better-patterns" aria-label="Link for Custom Hooks help you migrate to better patterns " title="Link for Custom Hooks help you migrate to better patterns " class="mdx-header-anchor inline-block"><svg width="1em" height="1em" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="text-gray-70 ms-2 h-5 w-5"><g fill="currentColor" fill-rule="evenodd"><path d="M7.778 7.975a2.5 2.5 0 0 0 .347-3.837L6.017 2.03a2.498 2.498 0 0 0-3.542-.007 2.5 2.5 0 0 0 .006 3.543l1.153 1.15c.07-.29.154-.563.25-.773.036-.077.084-.16.14-.25L3.18 4.85a1.496 1.496 0 0 1 .002-2.12 1.496 1.496 0 0 1 2.12 0l2.124 2.123a1.496 1.496 0 0 1-.333 2.37c.16.246.42.504.685.752z"></path><path d="M5.657 4.557a2.5 2.5 0 0 0-.347 3.837l2.108 2.108a2.498 2.498 0 0 0 3.542.007 2.5 2.5 0 0 0-.006-3.543L9.802 5.815c-.07.29-.154.565-.25.774-.036.076-.084.16-.14.25l.842.84c.585.587.59 1.532 0 2.122-.587.585-1.532.59-2.12 0L6.008 7.68a1.496 1.496 0 0 1 .332-2.372c-.16-.245-.42-.503-.685-.75z"></path></g></svg></a></h3> <p class="whitespace-pre-wrap my-4">Effects are an <a class="inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal" href="/learn/escape-hatches">“escape hatch”</a>: you use them when you need to “step outside React” and when there is no better built-in solution for your use case. With time, the React team’s goal is to reduce the number of the Effects in your app to the minimum by providing more specific solutions to more specific problems. Wrapping your Effects in custom Hooks makes it easier to upgrade your code when these solutions become available.</p> <p class="whitespace-pre-wrap my-4">Let’s return to this example:</p> </div><!--$--><div class="sandpack sandpack--playground w-full my-8" dir="ltr"><div class="sp-wrapper"><div class="shadow-lg dark:shadow-lg-dark rounded-lg" style="contain:content"><div class="bg-wash dark:bg-card-dark flex justify-between items-center relative z-10 border-b border-border dark:border-border-dark rounded-t-lg text-lg"><div class="flex-1 grow min-w-0 px-4 lg:px-6"><div><div class="relative overflow-hidden"><div class="w-[fit-content] invisible"><div class=" sp-tabs" translate="no"><div aria-label="Select active file" class=" sp-tabs-scrollable-container" role="tablist"><button aria-selected="false" class=" sp-tab-button" data-active="false" role="tab" title="/src/App.js" type="button">App.js</button><button aria-selected="true" class=" sp-tab-button" data-active="true" role="tab" title="/src/useOnlineStatus.js" type="button">useOnlineStatus.js</button></div></div></div><button class="absolute top-0 start-[2px]" id="headlessui-listbox-button-:R16aicq6:" aria-haspopup="true" aria-expanded="false" data-headlessui-state=""><span class="h-full py-2 px-1 mt-px -mb-px flex border-b text-link dark:text-link-dark border-link dark:border-link-dark items-center text-md leading-tight truncate" style="max-width:160px">useOnlineStatus.js<span class="ms-2"><svg class="rotate-0" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg></span></span></button></div></div></div><div class="px-3 flex items-center justify-end text-start" translate="yes"><button class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1" title="Reset Sandbox" type="button"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative"><path d="M13.8982 5.20844C12.4626 4.88688 10.9686 4.93769 9.55821 5.35604L11.8524 3.06184C11.8989 3.0154 11.9357 2.96028 11.9608 2.89961C11.986 2.83894 11.9989 2.77391 11.9989 2.70824C11.9989 2.64256 11.986 2.57754 11.9608 2.51686C11.9357 2.45619 11.8989 2.40107 11.8524 2.35464L11.1456 1.64784C11.0992 1.60139 11.0441 1.56455 10.9834 1.53942C10.9227 1.51428 10.8577 1.50134 10.792 1.50134C10.7263 1.50134 10.6613 1.51428 10.6006 1.53942C10.54 1.56455 10.4848 1.60139 10.4384 1.64784L6.14571 5.94054C6.00654 6.07969 5.89615 6.2449 5.82083 6.42673C5.74551 6.60855 5.70675 6.80343 5.70675 7.00024C5.70675 7.19704 5.74551 7.39192 5.82083 7.57374C5.89615 7.75557 6.00654 7.92078 6.14571 8.05994L10.4387 12.3529C10.5325 12.4465 10.6595 12.4991 10.792 12.4991C10.9245 12.4991 11.0516 12.4465 11.1453 12.3529L11.8527 11.6455C11.9463 11.5518 11.9989 11.4247 11.9989 11.2922C11.9989 11.1598 11.9463 11.0327 11.8527 10.9389L8.77481 7.86104C9.99795 7.16236 11.415 6.8801 12.8125 7.05678C14.21 7.23347 15.5122 7.85953 16.523 8.84064C17.5338 9.82176 18.1983 11.1048 18.4165 12.4964C18.6347 13.888 18.3947 15.3129 17.7328 16.5562C17.0708 17.7996 16.0227 18.7942 14.7463 19.3902C13.47 19.9861 12.0345 20.1511 10.6563 19.8603C9.27798 19.5695 8.03152 18.8387 7.10469 17.778C6.17786 16.7172 5.62086 15.384 5.51761 13.9791C5.51156 13.8512 5.45689 13.7303 5.36477 13.6413C5.27265 13.5522 5.15001 13.5017 5.02191 13.5H4.02081C3.95297 13.4996 3.88574 13.5129 3.8232 13.5392C3.76065 13.5655 3.70408 13.6042 3.6569 13.6529C3.60972 13.7017 3.57291 13.7595 3.54869 13.8228C3.52448 13.8862 3.51336 13.9538 3.51601 14.0216C3.61349 15.5965 4.1473 17.1132 5.0577 18.4019C5.9681 19.6906 7.21917 20.7006 8.6709 21.3188C10.1226 21.937 11.7178 22.139 13.2778 21.9022C14.8378 21.6654 16.3011 20.9992 17.504 19.978C18.7069 18.9569 19.6019 17.6212 20.0889 16.1203C20.5759 14.6195 20.6356 13.0128 20.2614 11.4799C19.8872 9.94705 19.0938 8.54858 17.97 7.44098C16.8462 6.33339 15.4363 5.56037 13.8982 5.20844V5.20844Z" fill="currentColor"></path></svg> Reset</button><a href="https://codesandbox.io/api/v1/sandboxes/define?undefined&environment=create-react-app" rel="noreferrer noopener" target="_blank" title="Open in CodeSandbox" class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1 ms-2 md:ms-1"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative top-[1px]"><path d="M20.5001 2H15.5001C15.3675 2 15.2403 2.05268 15.1465 2.14645C15.0528 2.24021 15.0001 2.36739 15.0001 2.5V3.5C15.0001 3.63261 15.0528 3.75979 15.1465 3.85355C15.2403 3.94732 15.3675 4 15.5001 4H18.5901L7.6501 14.94C7.60323 14.9865 7.56604 15.0418 7.54065 15.1027C7.51527 15.1636 7.5022 15.229 7.5022 15.295C7.5022 15.361 7.51527 15.4264 7.54065 15.4873C7.56604 15.5482 7.60323 15.6035 7.6501 15.65L8.3501 16.35C8.39658 16.3969 8.45188 16.4341 8.51281 16.4594C8.57374 16.4848 8.63909 16.4979 8.7051 16.4979C8.7711 16.4979 8.83646 16.4848 8.89738 16.4594C8.95831 16.4341 9.01362 16.3969 9.0601 16.35L20.0001 5.41V8.5C20.0001 8.63261 20.0528 8.75979 20.1465 8.85355C20.2403 8.94732 20.3675 9 20.5001 9H21.5001C21.6327 9 21.7599 8.94732 21.8537 8.85355C21.9474 8.75979 22.0001 8.63261 22.0001 8.5V3.5C22.0001 3.10218 21.8421 2.72064 21.5608 2.43934C21.2795 2.15804 20.8979 2 20.5001 2V2Z" fill="currentColor"></path><path d="M21.5 13H20.5C20.3674 13 20.2402 13.0527 20.1464 13.1464C20.0527 13.2402 20 13.3674 20 13.5V20H4V4H10.5C10.6326 4 10.7598 3.94732 10.8536 3.85355C10.9473 3.75979 11 3.63261 11 3.5V2.5C11 2.36739 10.9473 2.24021 10.8536 2.14645C10.7598 2.05268 10.6326 2 10.5 2H3.5C3.10218 2 2.72064 2.15804 2.43934 2.43934C2.15804 2.72064 2 3.10218 2 3.5V20.5C2 20.8978 2.15804 21.2794 2.43934 21.5607C2.72064 21.842 3.10218 22 3.5 22H20.5C20.8978 22 21.2794 21.842 21.5607 21.5607C21.842 21.2794 22 20.8978 22 20.5V13.5C22 13.3674 21.9473 13.2402 21.8536 13.1464C21.7598 13.0527 21.6326 13 21.5 13Z" fill="currentColor"></path></svg><span class="hidden md:block">Fork</span></a></div></div><div class=" sp-layout"><div class=" sp-editor sp-stack"><div class=" sp-code-editor"><div aria-autocomplete="list" aria-label="Code Editor for useOnlineStatus.js" aria-multiline="true" class="sp-pristine sp-javascript sp-cm" role="textbox" tabindex="0" translate="no"><pre class=" sp-pre-placeholder" style="margin-left:var(--sp-space-11)"><span class="sp-syntax-keyword">import</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">useState</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">useEffect</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">from</span> <span class="sp-syntax-string">'react'</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">export</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">useOnlineStatus</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">isOnline</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setIsOnline</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-static">true</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-definition">useEffect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">handleOnline</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-definition">setIsOnline</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-static">true</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">handleOffline</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-definition">setIsOnline</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-static">false</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-plain">window</span>.<span class="sp-syntax-property">addEventListener</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'online'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">handleOnline</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-plain">window</span>.<span class="sp-syntax-property">addEventListener</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'offline'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">handleOffline</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">window</span>.<span class="sp-syntax-property">removeEventListener</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'online'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">handleOnline</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-plain">window</span>.<span class="sp-syntax-property">removeEventListener</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'offline'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">handleOffline</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-punctuation">]</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-plain">isOnline</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> </pre></div></div></div><div class=" order-last xl:order-2 sp-stack"><div class="p-0 sm:p-2 md:p-4 lg:p-8 bg-card dark:bg-wash-dark h-full relative md:rounded-b-lg lg:rounded-b-none"><div style="position:relative"><iframe class="rounded-t-none bg-white md:shadow-md sm:rounded-lg w-full max-w-full transition-opacity absolute opacity-0 pointer-events-none duration-75" title="Sandbox Preview" style="height:15px;z-index:-1"></iframe></div></div></div><button translate="yes" class="sandpack-expand flex text-base justify-between dark:border-card-dark bg-wash dark:bg-card-dark items-center z-10 p-1 w-full order-2 xl:order-last border-b-1 relative top-0"><span class="flex p-2 focus:outline-none text-primary dark:text-primary-dark leading-[20px]"><svg class="rotate-0 inline me-1.5 text-xl" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg>Show more</span></button></div></div></div></div><!--/$--><div class="max-w-4xl ms-0 2xl:mx-auto"> <p class="whitespace-pre-wrap my-4">In the above example, <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useOnlineStatus</code> is implemented with a pair of <a class="inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal" href="/reference/react/useState"><code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useState</code></a> and <a class="inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal" href="/reference/react/useEffect"><code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useEffect</code>.</a> However, this isn’t the best possible solution. There is a number of edge cases it doesn’t consider. For example, it assumes that when the component mounts, <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">isOnline</code> is already <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">true</code>, but this may be wrong if the network already went offline. You can use the browser <a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine" target="_blank" rel="nofollow noopener noreferrer" class="inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal"><code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">navigator.onLine</code></a> API to check for that, but using it directly would not work on the server for generating the initial HTML. In short, this code could be improved.</p> <p class="whitespace-pre-wrap my-4">Luckily, React 18 includes a dedicated API called <a class="inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal" href="/reference/react/useSyncExternalStore"><code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useSyncExternalStore</code></a> which takes care of all of these problems for you. Here is how your <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useOnlineStatus</code> Hook, rewritten to take advantage of this new API:</p> </div><!--$--><div class="sandpack sandpack--playground w-full my-8" dir="ltr"><div class="sp-wrapper"><div class="shadow-lg dark:shadow-lg-dark rounded-lg" style="contain:content"><div class="bg-wash dark:bg-card-dark flex justify-between items-center relative z-10 border-b border-border dark:border-border-dark rounded-t-lg text-lg"><div class="flex-1 grow min-w-0 px-4 lg:px-6"><div><div class="relative overflow-hidden"><div class="w-[fit-content] invisible"><div class=" sp-tabs" translate="no"><div aria-label="Select active file" class=" sp-tabs-scrollable-container" role="tablist"><button aria-selected="false" class=" sp-tab-button" data-active="false" role="tab" title="/src/App.js" type="button">App.js</button><button aria-selected="true" class=" sp-tab-button" data-active="true" role="tab" title="/src/useOnlineStatus.js" type="button">useOnlineStatus.js</button></div></div></div><button class="absolute top-0 start-[2px]" id="headlessui-listbox-button-:R16akcq6:" aria-haspopup="true" aria-expanded="false" data-headlessui-state=""><span class="h-full py-2 px-1 mt-px -mb-px flex border-b text-link dark:text-link-dark border-link dark:border-link-dark items-center text-md leading-tight truncate" style="max-width:160px">useOnlineStatus.js<span class="ms-2"><svg class="rotate-0" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg></span></span></button></div></div></div><div class="px-3 flex items-center justify-end text-start" translate="yes"><button class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1" title="Reset Sandbox" type="button"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative"><path d="M13.8982 5.20844C12.4626 4.88688 10.9686 4.93769 9.55821 5.35604L11.8524 3.06184C11.8989 3.0154 11.9357 2.96028 11.9608 2.89961C11.986 2.83894 11.9989 2.77391 11.9989 2.70824C11.9989 2.64256 11.986 2.57754 11.9608 2.51686C11.9357 2.45619 11.8989 2.40107 11.8524 2.35464L11.1456 1.64784C11.0992 1.60139 11.0441 1.56455 10.9834 1.53942C10.9227 1.51428 10.8577 1.50134 10.792 1.50134C10.7263 1.50134 10.6613 1.51428 10.6006 1.53942C10.54 1.56455 10.4848 1.60139 10.4384 1.64784L6.14571 5.94054C6.00654 6.07969 5.89615 6.2449 5.82083 6.42673C5.74551 6.60855 5.70675 6.80343 5.70675 7.00024C5.70675 7.19704 5.74551 7.39192 5.82083 7.57374C5.89615 7.75557 6.00654 7.92078 6.14571 8.05994L10.4387 12.3529C10.5325 12.4465 10.6595 12.4991 10.792 12.4991C10.9245 12.4991 11.0516 12.4465 11.1453 12.3529L11.8527 11.6455C11.9463 11.5518 11.9989 11.4247 11.9989 11.2922C11.9989 11.1598 11.9463 11.0327 11.8527 10.9389L8.77481 7.86104C9.99795 7.16236 11.415 6.8801 12.8125 7.05678C14.21 7.23347 15.5122 7.85953 16.523 8.84064C17.5338 9.82176 18.1983 11.1048 18.4165 12.4964C18.6347 13.888 18.3947 15.3129 17.7328 16.5562C17.0708 17.7996 16.0227 18.7942 14.7463 19.3902C13.47 19.9861 12.0345 20.1511 10.6563 19.8603C9.27798 19.5695 8.03152 18.8387 7.10469 17.778C6.17786 16.7172 5.62086 15.384 5.51761 13.9791C5.51156 13.8512 5.45689 13.7303 5.36477 13.6413C5.27265 13.5522 5.15001 13.5017 5.02191 13.5H4.02081C3.95297 13.4996 3.88574 13.5129 3.8232 13.5392C3.76065 13.5655 3.70408 13.6042 3.6569 13.6529C3.60972 13.7017 3.57291 13.7595 3.54869 13.8228C3.52448 13.8862 3.51336 13.9538 3.51601 14.0216C3.61349 15.5965 4.1473 17.1132 5.0577 18.4019C5.9681 19.6906 7.21917 20.7006 8.6709 21.3188C10.1226 21.937 11.7178 22.139 13.2778 21.9022C14.8378 21.6654 16.3011 20.9992 17.504 19.978C18.7069 18.9569 19.6019 17.6212 20.0889 16.1203C20.5759 14.6195 20.6356 13.0128 20.2614 11.4799C19.8872 9.94705 19.0938 8.54858 17.97 7.44098C16.8462 6.33339 15.4363 5.56037 13.8982 5.20844V5.20844Z" fill="currentColor"></path></svg> Reset</button><a href="https://codesandbox.io/api/v1/sandboxes/define?undefined&environment=create-react-app" rel="noreferrer noopener" target="_blank" title="Open in CodeSandbox" class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1 ms-2 md:ms-1"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative top-[1px]"><path d="M20.5001 2H15.5001C15.3675 2 15.2403 2.05268 15.1465 2.14645C15.0528 2.24021 15.0001 2.36739 15.0001 2.5V3.5C15.0001 3.63261 15.0528 3.75979 15.1465 3.85355C15.2403 3.94732 15.3675 4 15.5001 4H18.5901L7.6501 14.94C7.60323 14.9865 7.56604 15.0418 7.54065 15.1027C7.51527 15.1636 7.5022 15.229 7.5022 15.295C7.5022 15.361 7.51527 15.4264 7.54065 15.4873C7.56604 15.5482 7.60323 15.6035 7.6501 15.65L8.3501 16.35C8.39658 16.3969 8.45188 16.4341 8.51281 16.4594C8.57374 16.4848 8.63909 16.4979 8.7051 16.4979C8.7711 16.4979 8.83646 16.4848 8.89738 16.4594C8.95831 16.4341 9.01362 16.3969 9.0601 16.35L20.0001 5.41V8.5C20.0001 8.63261 20.0528 8.75979 20.1465 8.85355C20.2403 8.94732 20.3675 9 20.5001 9H21.5001C21.6327 9 21.7599 8.94732 21.8537 8.85355C21.9474 8.75979 22.0001 8.63261 22.0001 8.5V3.5C22.0001 3.10218 21.8421 2.72064 21.5608 2.43934C21.2795 2.15804 20.8979 2 20.5001 2V2Z" fill="currentColor"></path><path d="M21.5 13H20.5C20.3674 13 20.2402 13.0527 20.1464 13.1464C20.0527 13.2402 20 13.3674 20 13.5V20H4V4H10.5C10.6326 4 10.7598 3.94732 10.8536 3.85355C10.9473 3.75979 11 3.63261 11 3.5V2.5C11 2.36739 10.9473 2.24021 10.8536 2.14645C10.7598 2.05268 10.6326 2 10.5 2H3.5C3.10218 2 2.72064 2.15804 2.43934 2.43934C2.15804 2.72064 2 3.10218 2 3.5V20.5C2 20.8978 2.15804 21.2794 2.43934 21.5607C2.72064 21.842 3.10218 22 3.5 22H20.5C20.8978 22 21.2794 21.842 21.5607 21.5607C21.842 21.2794 22 20.8978 22 20.5V13.5C22 13.3674 21.9473 13.2402 21.8536 13.1464C21.7598 13.0527 21.6326 13 21.5 13Z" fill="currentColor"></path></svg><span class="hidden md:block">Fork</span></a></div></div><div class=" sp-layout"><div class=" sp-editor sp-stack"><div class=" sp-code-editor"><div aria-autocomplete="list" aria-label="Code Editor for useOnlineStatus.js" aria-multiline="true" class="sp-pristine sp-javascript sp-cm" role="textbox" tabindex="0" translate="no"><pre class=" sp-pre-placeholder" style="margin-left:var(--sp-space-11)"><span class="sp-syntax-keyword">import</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">useSyncExternalStore</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">from</span> <span class="sp-syntax-string">'react'</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">subscribe</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">callback</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">window</span>.<span class="sp-syntax-property">addEventListener</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'online'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">callback</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-plain">window</span>.<span class="sp-syntax-property">addEventListener</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'offline'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">callback</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">window</span>.<span class="sp-syntax-property">removeEventListener</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'online'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">callback</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-plain">window</span>.<span class="sp-syntax-property">removeEventListener</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">'offline'</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">callback</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">export</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">useOnlineStatus</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-definition">useSyncExternalStore</span><span class="sp-syntax-punctuation">(</span> <span class="sp-syntax-plain">subscribe</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-plain">navigator</span>.<span class="sp-syntax-property">onLine</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-comment">// How to get the value on the client</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-static">true</span> <span class="sp-syntax-comment">// How to get the value on the server</span> <span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> </pre></div></div></div><div class=" order-last xl:order-2 sp-stack"><div class="p-0 sm:p-2 md:p-4 lg:p-8 bg-card dark:bg-wash-dark h-full relative md:rounded-b-lg lg:rounded-b-none"><div style="position:relative"><iframe class="rounded-t-none bg-white md:shadow-md sm:rounded-lg w-full max-w-full transition-opacity absolute opacity-0 pointer-events-none duration-75" title="Sandbox Preview" style="height:15px;z-index:-1"></iframe></div></div></div><button translate="yes" class="sandpack-expand flex text-base justify-between dark:border-card-dark bg-wash dark:bg-card-dark items-center z-10 p-1 w-full order-2 xl:order-last border-b-1 relative top-0"><span class="flex p-2 focus:outline-none text-primary dark:text-primary-dark leading-[20px]"><svg class="rotate-0 inline me-1.5 text-xl" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg>Show more</span></button></div></div></div></div><!--/$--><div class="max-w-4xl ms-0 2xl:mx-auto"> <p class="whitespace-pre-wrap my-4">Notice how <strong class="font-bold">you didn’t need to change any of the components</strong> to make this migration:</p> <!--$--><div dir="ltr" class="sandpack sandpack--codeblock rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg my-8" style="contain:content"><div class="sp-wrapper"><div class="sp-stack"><div class="sp-code-editor"><pre class="sp-cm sp-pristine sp-javascript flex align-start"><code class="sp-pre-placeholder grow-[2]"><div class="cm-line "><span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">StatusBar</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">isOnline</span> = <span class="sp-syntax-definition">useOnlineStatus</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-comment">// ...</span><br/></div><div class="cm-line "><span class="sp-syntax-punctuation">}</span><br/></div><div class="cm-line "><br/></div><div class="cm-line "><span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">SaveButton</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">isOnline</span> = <span class="sp-syntax-definition">useOnlineStatus</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-comment">// ...</span><br/></div><div class="cm-line "><span class="sp-syntax-punctuation">}</span></div></code></pre></div></div></div></div><!--/$--> <p class="whitespace-pre-wrap my-4">This is another reason for why wrapping Effects in custom Hooks is often beneficial:</p> <ol class="ms-6 my-3 list-decimal"> <li class="leading-relaxed mb-1">You make the data flow to and from your Effects very explicit.</li> <li class="leading-relaxed mb-1">You let your components focus on the intent rather than on the exact implementation of your Effects.</li> <li class="leading-relaxed mb-1">When React adds new features, you can remove those Effects without changing any of your components.</li> </ol> <p class="whitespace-pre-wrap my-4">Similar to a <a href="https://uxdesign.cc/everything-you-need-to-know-about-design-systems-54b109851969" target="_blank" rel="nofollow noopener noreferrer" class="inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal">design system,</a> you might find it helpful to start extracting common idioms from your app’s components into custom Hooks. This will keep your components’ code focused on the intent, and let you avoid writing raw Effects very often. Many excellent custom Hooks are maintained by the React community.</p> <details class="my-12 rounded-2xl shadow-inner-border dark:shadow-inner-border-dark relative dark:bg-opacity-20 dark:bg-purple-60 bg-purple-5"><summary class="list-none p-8" tabindex="-1"><h5 class="mb-4 uppercase font-bold flex items-center text-sm dark:text-purple-30 text-purple-50"><svg class="inline me-2 dark:text-purple-30 text-purple-40" width="1.5em" height="1.5em" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M34.7409 59.7228L32.9567 58.9094C27.2672 56.3157 20.7328 56.3157 15.0433 58.9094C12.6018 60.0224 9.39163 59.0275 8.44602 56.0621C7.45647 52.9589 5.99975 46.5898 6 35.9997C6.00029 23.5648 8.00803 18.3599 9.11099 16.4196C9.67795 15.4222 10.5255 14.8455 11.2254 14.5264L12.0179 14.1651C19.6351 10.6926 28.4011 10.6738 36 14.1733C43.5989 10.6738 52.3649 10.6926 59.9821 14.1651L60.7746 14.5264C61.4745 14.8455 62.3221 15.4222 62.889 16.4196C63.992 18.3599 65.9997 23.5648 66 35.9997C66.0002 46.5898 64.5435 52.9589 63.554 56.0621C62.6084 59.0275 59.3982 60.0224 56.9567 58.9094C51.2672 56.3157 44.7328 56.3157 39.0433 58.9094L37.2591 59.7228C37.1986 59.7508 37.1373 59.7767 37.0753 59.8004C36.4484 60.0411 35.7556 60.0653 35.1102 59.8648C34.9847 59.8258 34.8613 59.7784 34.7409 59.7228ZM14.5068 19.6246C20.3733 16.9501 27.0874 16.8775 33 19.4067V52.473C26.7613 50.32 19.9378 50.471 13.7811 52.9261C13.0005 49.9843 11.9998 44.547 12 35.9998C12.0002 25.5786 13.4879 21.1893 14.1179 19.8018L14.5068 19.6246ZM39 52.473C45.2387 50.32 52.0622 50.471 58.2189 52.9261C58.9995 49.9843 60.0002 44.547 60 35.9998C59.9998 25.5786 58.5121 21.1893 57.8821 19.8018L57.4932 19.6246C51.6267 16.9501 44.9126 16.8775 39 19.4067V52.473Z" fill="currentColor"></path></svg>Deep Dive</h5><div class="mb-4"><h4 id="will-react-provide-any-built-in-solution-for-data-fetching" class="mdx-heading text-xl font-bold text-primary dark:text-primary-dark text-xl font-display font-bold leading-9 my-4">Will React provide any built-in solution for data fetching? <a href="#will-react-provide-any-built-in-solution-for-data-fetching" aria-label="Link for Will React provide any built-in solution for data fetching? " title="Link for Will React provide any built-in solution for data fetching? " class="mdx-header-anchor inline-block"><svg width="1em" height="1em" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="text-gray-70 ms-2 h-5 w-5"><g fill="currentColor" fill-rule="evenodd"><path d="M7.778 7.975a2.5 2.5 0 0 0 .347-3.837L6.017 2.03a2.498 2.498 0 0 0-3.542-.007 2.5 2.5 0 0 0 .006 3.543l1.153 1.15c.07-.29.154-.563.25-.773.036-.077.084-.16.14-.25L3.18 4.85a1.496 1.496 0 0 1 .002-2.12 1.496 1.496 0 0 1 2.12 0l2.124 2.123a1.496 1.496 0 0 1-.333 2.37c.16.246.42.504.685.752z"></path><path d="M5.657 4.557a2.5 2.5 0 0 0-.347 3.837l2.108 2.108a2.498 2.498 0 0 0 3.542.007 2.5 2.5 0 0 0-.006-3.543L9.802 5.815c-.07.29-.154.565-.25.774-.036.076-.084.16-.14.25l.842.84c.585.587.59 1.532 0 2.122-.587.585-1.532.59-2.12 0L6.008 7.68a1.496 1.496 0 0 1 .332-2.372c-.16-.245-.42-.503-.685-.75z"></path></g></svg></a></h4></div><button class="bg-purple-50 border-purple-50 hover:bg-purple-40 focus:bg-purple-50 active:bg-purple-50 text-base leading-tight font-bold rounded-full py-2 px-4 focus:outline focus:outline-offset-2 focus:outline-link dark:focus:outline-link-dark inline-flex items-center my-1 bg-link border-link text-white hover:bg-link focus:bg-link active:bg-link"><span class="me-1"><svg class="rotate-0" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg></span>Show Details</button></summary><div class="p-8 border-t dark:border-purple-60 border-purple-10 "><p class="whitespace-pre-wrap my-4">We’re still working out the details, but we expect that in the future, you’ll write data fetching like this:</p><!--$--><div dir="ltr" class="sandpack sandpack--codeblock rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg my-8" style="contain:content"><div class="sp-wrapper"><div class="sp-stack"><div class="sp-code-editor"><pre class="sp-cm sp-pristine sp-javascript flex align-start"><code class="sp-pre-placeholder grow-[2]"><div class="cm-line bg-github-highlight dark:bg-opacity-10"><span class="sp-syntax-keyword">import</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">use</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">from</span> <span class="sp-syntax-string">'react'</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-comment">// Not available yet!</span><br/></div><div class="cm-line "><br/></div><div class="cm-line "><span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">ShippingForm</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">country</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">cities</span> = <span class="sp-syntax-definition">use</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-definition">fetch</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">`/api/cities?country=</span><span class="sp-syntax-punctuation">${</span><span class="sp-syntax-plain">country</span><span class="sp-syntax-punctuation">}</span><span class="sp-syntax-string">`</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">city</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setCity</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-keyword">null</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line bg-github-highlight dark:bg-opacity-10"> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">areas</span> = <span class="sp-syntax-plain">city</span> ? <span class="sp-syntax-definition">use</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-definition">fetch</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-string">`/api/areas?city=</span><span class="sp-syntax-punctuation">${</span><span class="sp-syntax-plain">city</span><span class="sp-syntax-punctuation">}</span><span class="sp-syntax-string">`</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">)</span> : <span class="sp-syntax-keyword">null</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-comment">// ...</span></div></code></pre></div></div></div></div><!--/$--><p class="whitespace-pre-wrap my-4">If you use custom Hooks like <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useData</code> above in your app, it will require fewer changes to migrate to the eventually recommended approach than if you write raw Effects in every component manually. However, the old approach will still work fine, so if you feel happy writing raw Effects, you can continue to do that.</p></div></details> <h3 id="there-is-more-than-one-way-to-do-it" class="mdx-heading text-2xl font-display leading-9 text-primary dark:text-primary-dark font-bold my-6">There is more than one way to do it <a href="#there-is-more-than-one-way-to-do-it" aria-label="Link for There is more than one way to do it " title="Link for There is more than one way to do it " class="mdx-header-anchor inline-block"><svg width="1em" height="1em" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="text-gray-70 ms-2 h-5 w-5"><g fill="currentColor" fill-rule="evenodd"><path d="M7.778 7.975a2.5 2.5 0 0 0 .347-3.837L6.017 2.03a2.498 2.498 0 0 0-3.542-.007 2.5 2.5 0 0 0 .006 3.543l1.153 1.15c.07-.29.154-.563.25-.773.036-.077.084-.16.14-.25L3.18 4.85a1.496 1.496 0 0 1 .002-2.12 1.496 1.496 0 0 1 2.12 0l2.124 2.123a1.496 1.496 0 0 1-.333 2.37c.16.246.42.504.685.752z"></path><path d="M5.657 4.557a2.5 2.5 0 0 0-.347 3.837l2.108 2.108a2.498 2.498 0 0 0 3.542.007 2.5 2.5 0 0 0-.006-3.543L9.802 5.815c-.07.29-.154.565-.25.774-.036.076-.084.16-.14.25l.842.84c.585.587.59 1.532 0 2.122-.587.585-1.532.59-2.12 0L6.008 7.68a1.496 1.496 0 0 1 .332-2.372c-.16-.245-.42-.503-.685-.75z"></path></g></svg></a></h3> <p class="whitespace-pre-wrap my-4">Let’s say you want to implement a fade-in animation <em>from scratch</em> using the browser <a href="https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame" target="_blank" rel="nofollow noopener noreferrer" class="inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal"><code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">requestAnimationFrame</code></a> API. You might start with an Effect that sets up an animation loop. During each frame of the animation, you could change the opacity of the DOM node you <a class="inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal" href="/learn/manipulating-the-dom-with-refs">hold in a ref</a> until it reaches <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">1</code>. Your code might start like this:</p> </div><!--$--><div class="sandpack sandpack--playground w-full my-8" dir="ltr"><div class="sp-wrapper"><div class="shadow-lg dark:shadow-lg-dark rounded-lg" style="contain:content"><div class="bg-wash dark:bg-card-dark flex justify-between items-center relative z-10 border-b border-border dark:border-border-dark rounded-t-lg text-lg"><div class="flex-1 grow min-w-0 px-4 lg:px-6"><div><div class="relative overflow-hidden"><div class="w-[fit-content] invisible"><div class=" sp-tabs" translate="no"><div aria-label="Select active file" class=" sp-tabs-scrollable-container" role="tablist"><button aria-selected="true" class=" sp-tab-button" data-active="true" role="tab" title="/src/App.js" type="button">App.js</button></div></div></div><button class="absolute top-0 start-[2px]" id="headlessui-listbox-button-:R16amcq6:" aria-haspopup="true" aria-expanded="false" data-headlessui-state=""><span class="h-full py-2 px-1 mt-px -mb-px flex border-b text-link dark:text-link-dark border-link dark:border-link-dark items-center text-md leading-tight truncate" style="max-width:160px">App.js</span></button></div></div></div><div class="px-3 flex items-center justify-end text-start" translate="yes"><button class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1" title="Reset Sandbox" type="button"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative"><path d="M13.8982 5.20844C12.4626 4.88688 10.9686 4.93769 9.55821 5.35604L11.8524 3.06184C11.8989 3.0154 11.9357 2.96028 11.9608 2.89961C11.986 2.83894 11.9989 2.77391 11.9989 2.70824C11.9989 2.64256 11.986 2.57754 11.9608 2.51686C11.9357 2.45619 11.8989 2.40107 11.8524 2.35464L11.1456 1.64784C11.0992 1.60139 11.0441 1.56455 10.9834 1.53942C10.9227 1.51428 10.8577 1.50134 10.792 1.50134C10.7263 1.50134 10.6613 1.51428 10.6006 1.53942C10.54 1.56455 10.4848 1.60139 10.4384 1.64784L6.14571 5.94054C6.00654 6.07969 5.89615 6.2449 5.82083 6.42673C5.74551 6.60855 5.70675 6.80343 5.70675 7.00024C5.70675 7.19704 5.74551 7.39192 5.82083 7.57374C5.89615 7.75557 6.00654 7.92078 6.14571 8.05994L10.4387 12.3529C10.5325 12.4465 10.6595 12.4991 10.792 12.4991C10.9245 12.4991 11.0516 12.4465 11.1453 12.3529L11.8527 11.6455C11.9463 11.5518 11.9989 11.4247 11.9989 11.2922C11.9989 11.1598 11.9463 11.0327 11.8527 10.9389L8.77481 7.86104C9.99795 7.16236 11.415 6.8801 12.8125 7.05678C14.21 7.23347 15.5122 7.85953 16.523 8.84064C17.5338 9.82176 18.1983 11.1048 18.4165 12.4964C18.6347 13.888 18.3947 15.3129 17.7328 16.5562C17.0708 17.7996 16.0227 18.7942 14.7463 19.3902C13.47 19.9861 12.0345 20.1511 10.6563 19.8603C9.27798 19.5695 8.03152 18.8387 7.10469 17.778C6.17786 16.7172 5.62086 15.384 5.51761 13.9791C5.51156 13.8512 5.45689 13.7303 5.36477 13.6413C5.27265 13.5522 5.15001 13.5017 5.02191 13.5H4.02081C3.95297 13.4996 3.88574 13.5129 3.8232 13.5392C3.76065 13.5655 3.70408 13.6042 3.6569 13.6529C3.60972 13.7017 3.57291 13.7595 3.54869 13.8228C3.52448 13.8862 3.51336 13.9538 3.51601 14.0216C3.61349 15.5965 4.1473 17.1132 5.0577 18.4019C5.9681 19.6906 7.21917 20.7006 8.6709 21.3188C10.1226 21.937 11.7178 22.139 13.2778 21.9022C14.8378 21.6654 16.3011 20.9992 17.504 19.978C18.7069 18.9569 19.6019 17.6212 20.0889 16.1203C20.5759 14.6195 20.6356 13.0128 20.2614 11.4799C19.8872 9.94705 19.0938 8.54858 17.97 7.44098C16.8462 6.33339 15.4363 5.56037 13.8982 5.20844V5.20844Z" fill="currentColor"></path></svg> Reset</button><a href="https://codesandbox.io/api/v1/sandboxes/define?undefined&environment=create-react-app" rel="noreferrer noopener" target="_blank" title="Open in CodeSandbox" class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1 ms-2 md:ms-1"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative top-[1px]"><path d="M20.5001 2H15.5001C15.3675 2 15.2403 2.05268 15.1465 2.14645C15.0528 2.24021 15.0001 2.36739 15.0001 2.5V3.5C15.0001 3.63261 15.0528 3.75979 15.1465 3.85355C15.2403 3.94732 15.3675 4 15.5001 4H18.5901L7.6501 14.94C7.60323 14.9865 7.56604 15.0418 7.54065 15.1027C7.51527 15.1636 7.5022 15.229 7.5022 15.295C7.5022 15.361 7.51527 15.4264 7.54065 15.4873C7.56604 15.5482 7.60323 15.6035 7.6501 15.65L8.3501 16.35C8.39658 16.3969 8.45188 16.4341 8.51281 16.4594C8.57374 16.4848 8.63909 16.4979 8.7051 16.4979C8.7711 16.4979 8.83646 16.4848 8.89738 16.4594C8.95831 16.4341 9.01362 16.3969 9.0601 16.35L20.0001 5.41V8.5C20.0001 8.63261 20.0528 8.75979 20.1465 8.85355C20.2403 8.94732 20.3675 9 20.5001 9H21.5001C21.6327 9 21.7599 8.94732 21.8537 8.85355C21.9474 8.75979 22.0001 8.63261 22.0001 8.5V3.5C22.0001 3.10218 21.8421 2.72064 21.5608 2.43934C21.2795 2.15804 20.8979 2 20.5001 2V2Z" fill="currentColor"></path><path d="M21.5 13H20.5C20.3674 13 20.2402 13.0527 20.1464 13.1464C20.0527 13.2402 20 13.3674 20 13.5V20H4V4H10.5C10.6326 4 10.7598 3.94732 10.8536 3.85355C10.9473 3.75979 11 3.63261 11 3.5V2.5C11 2.36739 10.9473 2.24021 10.8536 2.14645C10.7598 2.05268 10.6326 2 10.5 2H3.5C3.10218 2 2.72064 2.15804 2.43934 2.43934C2.15804 2.72064 2 3.10218 2 3.5V20.5C2 20.8978 2.15804 21.2794 2.43934 21.5607C2.72064 21.842 3.10218 22 3.5 22H20.5C20.8978 22 21.2794 21.842 21.5607 21.5607C21.842 21.2794 22 20.8978 22 20.5V13.5C22 13.3674 21.9473 13.2402 21.8536 13.1464C21.7598 13.0527 21.6326 13 21.5 13Z" fill="currentColor"></path></svg><span class="hidden md:block">Fork</span></a></div></div><div class=" sp-layout"><div class=" sp-editor sp-stack"><div class=" sp-code-editor"><div aria-autocomplete="list" aria-label="Code Editor for App.js" aria-multiline="true" class="sp-pristine sp-javascript sp-cm" role="textbox" tabindex="0" translate="no"><pre class=" sp-pre-placeholder" style="margin-left:var(--sp-space-11)"><span class="sp-syntax-keyword">import</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">useState</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">useEffect</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">useRef</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">from</span> <span class="sp-syntax-string">'react'</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">Welcome</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">ref</span> = <span class="sp-syntax-definition">useRef</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-keyword">null</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-definition">useEffect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">duration</span> = <span class="sp-syntax-static">1000</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">node</span> = <span class="sp-syntax-plain">ref</span>.<span class="sp-syntax-property">current</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">let</span> <span class="sp-syntax-plain">startTime</span> = <span class="sp-syntax-plain">performance</span>.<span class="sp-syntax-property">now</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">let</span> <span class="sp-syntax-plain">frameId</span> = <span class="sp-syntax-keyword">null</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">onFrame</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">now</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">timePassed</span> = <span class="sp-syntax-plain">now</span> - <span class="sp-syntax-plain">startTime</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">progress</span> = <span class="sp-syntax-plain">Math</span>.<span class="sp-syntax-property">min</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">timePassed</span> / <span class="sp-syntax-plain">duration</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-static">1</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-definition">onProgress</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">progress</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">if</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">progress</span> < <span class="sp-syntax-static">1</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-comment">// We still have more frames to paint</span> <span class="sp-syntax-plain">frameId</span> = <span class="sp-syntax-definition">requestAnimationFrame</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">onFrame</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">onProgress</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">progress</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">node</span>.<span class="sp-syntax-property">style</span>.<span class="sp-syntax-property">opacity</span> = <span class="sp-syntax-plain">progress</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">start</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-definition">onProgress</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-static">0</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-plain">startTime</span> = <span class="sp-syntax-plain">performance</span>.<span class="sp-syntax-property">now</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-plain">frameId</span> = <span class="sp-syntax-definition">requestAnimationFrame</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">onFrame</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">stop</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-definition">cancelAnimationFrame</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">frameId</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-plain">startTime</span> = <span class="sp-syntax-keyword">null</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-plain">frameId</span> = <span class="sp-syntax-keyword">null</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-definition">start</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-definition">stop</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-punctuation">]</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">h1</span> <span class="sp-syntax-property">className</span>=<span class="sp-syntax-string">"welcome"</span> <span class="sp-syntax-property">ref</span>=<span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">ref</span><span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">></span> Welcome <span class="sp-syntax-punctuation"></</span><span class="sp-syntax-tag">h1</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">export</span> <span class="sp-syntax-keyword">default</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">App</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">show</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setShow</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-static">false</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">button</span> <span class="sp-syntax-property">onClick</span>=<span class="sp-syntax-punctuation">{</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-definition">setShow</span><span class="sp-syntax-punctuation">(</span>!<span class="sp-syntax-plain">show</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">show</span> ? <span class="sp-syntax-string">'Remove'</span> : <span class="sp-syntax-string">'Show'</span><span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-punctuation"></</span><span class="sp-syntax-tag">button</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">hr</span> <span class="sp-syntax-punctuation">/></span> <span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">show</span> && <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-definition">Welcome</span> <span class="sp-syntax-punctuation">/></span><span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-punctuation"></</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> </pre></div></div></div><div class=" order-last xl:order-2 sp-stack"><div class="p-0 sm:p-2 md:p-4 lg:p-8 bg-card dark:bg-wash-dark h-full relative md:rounded-b-lg lg:rounded-b-none"><div style="position:relative"><iframe class="rounded-t-none bg-white md:shadow-md sm:rounded-lg w-full max-w-full transition-opacity absolute opacity-0 pointer-events-none duration-75" title="Sandbox Preview" style="height:15px;z-index:-1"></iframe></div></div></div><button translate="yes" class="sandpack-expand flex text-base justify-between dark:border-card-dark bg-wash dark:bg-card-dark items-center z-10 p-1 w-full order-2 xl:order-last border-b-1 relative top-0"><span class="flex p-2 focus:outline-none text-primary dark:text-primary-dark leading-[20px]"><svg class="rotate-0 inline me-1.5 text-xl" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg>Show more</span></button></div></div></div></div><!--/$--><div class="max-w-4xl ms-0 2xl:mx-auto"> <p class="whitespace-pre-wrap my-4">To make the component more readable, you might extract the logic into a <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useFadeIn</code> custom Hook:</p> </div><!--$--><div class="sandpack sandpack--playground w-full my-8" dir="ltr"><div class="sp-wrapper"><div class="shadow-lg dark:shadow-lg-dark rounded-lg" style="contain:content"><div class="bg-wash dark:bg-card-dark flex justify-between items-center relative z-10 border-b border-border dark:border-border-dark rounded-t-lg text-lg"><div class="flex-1 grow min-w-0 px-4 lg:px-6"><div><div class="relative overflow-hidden"><div class="w-[fit-content] invisible"><div class=" sp-tabs" translate="no"><div aria-label="Select active file" class=" sp-tabs-scrollable-container" role="tablist"><button aria-selected="true" class=" sp-tab-button" data-active="true" role="tab" title="/src/App.js" type="button">App.js</button><button aria-selected="false" class=" sp-tab-button" data-active="false" role="tab" title="/src/useFadeIn.js" type="button">useFadeIn.js</button></div></div></div><button class="absolute top-0 start-[2px]" id="headlessui-listbox-button-:R16aocq6:" aria-haspopup="true" aria-expanded="false" data-headlessui-state=""><span class="h-full py-2 px-1 mt-px -mb-px flex border-b text-link dark:text-link-dark border-link dark:border-link-dark items-center text-md leading-tight truncate" style="max-width:160px">App.js<span class="ms-2"><svg class="rotate-0" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg></span></span></button></div></div></div><div class="px-3 flex items-center justify-end text-start" translate="yes"><button class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1" title="Reset Sandbox" type="button"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative"><path d="M13.8982 5.20844C12.4626 4.88688 10.9686 4.93769 9.55821 5.35604L11.8524 3.06184C11.8989 3.0154 11.9357 2.96028 11.9608 2.89961C11.986 2.83894 11.9989 2.77391 11.9989 2.70824C11.9989 2.64256 11.986 2.57754 11.9608 2.51686C11.9357 2.45619 11.8989 2.40107 11.8524 2.35464L11.1456 1.64784C11.0992 1.60139 11.0441 1.56455 10.9834 1.53942C10.9227 1.51428 10.8577 1.50134 10.792 1.50134C10.7263 1.50134 10.6613 1.51428 10.6006 1.53942C10.54 1.56455 10.4848 1.60139 10.4384 1.64784L6.14571 5.94054C6.00654 6.07969 5.89615 6.2449 5.82083 6.42673C5.74551 6.60855 5.70675 6.80343 5.70675 7.00024C5.70675 7.19704 5.74551 7.39192 5.82083 7.57374C5.89615 7.75557 6.00654 7.92078 6.14571 8.05994L10.4387 12.3529C10.5325 12.4465 10.6595 12.4991 10.792 12.4991C10.9245 12.4991 11.0516 12.4465 11.1453 12.3529L11.8527 11.6455C11.9463 11.5518 11.9989 11.4247 11.9989 11.2922C11.9989 11.1598 11.9463 11.0327 11.8527 10.9389L8.77481 7.86104C9.99795 7.16236 11.415 6.8801 12.8125 7.05678C14.21 7.23347 15.5122 7.85953 16.523 8.84064C17.5338 9.82176 18.1983 11.1048 18.4165 12.4964C18.6347 13.888 18.3947 15.3129 17.7328 16.5562C17.0708 17.7996 16.0227 18.7942 14.7463 19.3902C13.47 19.9861 12.0345 20.1511 10.6563 19.8603C9.27798 19.5695 8.03152 18.8387 7.10469 17.778C6.17786 16.7172 5.62086 15.384 5.51761 13.9791C5.51156 13.8512 5.45689 13.7303 5.36477 13.6413C5.27265 13.5522 5.15001 13.5017 5.02191 13.5H4.02081C3.95297 13.4996 3.88574 13.5129 3.8232 13.5392C3.76065 13.5655 3.70408 13.6042 3.6569 13.6529C3.60972 13.7017 3.57291 13.7595 3.54869 13.8228C3.52448 13.8862 3.51336 13.9538 3.51601 14.0216C3.61349 15.5965 4.1473 17.1132 5.0577 18.4019C5.9681 19.6906 7.21917 20.7006 8.6709 21.3188C10.1226 21.937 11.7178 22.139 13.2778 21.9022C14.8378 21.6654 16.3011 20.9992 17.504 19.978C18.7069 18.9569 19.6019 17.6212 20.0889 16.1203C20.5759 14.6195 20.6356 13.0128 20.2614 11.4799C19.8872 9.94705 19.0938 8.54858 17.97 7.44098C16.8462 6.33339 15.4363 5.56037 13.8982 5.20844V5.20844Z" fill="currentColor"></path></svg> Reset</button><a href="https://codesandbox.io/api/v1/sandboxes/define?undefined&environment=create-react-app" rel="noreferrer noopener" target="_blank" title="Open in CodeSandbox" class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1 ms-2 md:ms-1"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative top-[1px]"><path d="M20.5001 2H15.5001C15.3675 2 15.2403 2.05268 15.1465 2.14645C15.0528 2.24021 15.0001 2.36739 15.0001 2.5V3.5C15.0001 3.63261 15.0528 3.75979 15.1465 3.85355C15.2403 3.94732 15.3675 4 15.5001 4H18.5901L7.6501 14.94C7.60323 14.9865 7.56604 15.0418 7.54065 15.1027C7.51527 15.1636 7.5022 15.229 7.5022 15.295C7.5022 15.361 7.51527 15.4264 7.54065 15.4873C7.56604 15.5482 7.60323 15.6035 7.6501 15.65L8.3501 16.35C8.39658 16.3969 8.45188 16.4341 8.51281 16.4594C8.57374 16.4848 8.63909 16.4979 8.7051 16.4979C8.7711 16.4979 8.83646 16.4848 8.89738 16.4594C8.95831 16.4341 9.01362 16.3969 9.0601 16.35L20.0001 5.41V8.5C20.0001 8.63261 20.0528 8.75979 20.1465 8.85355C20.2403 8.94732 20.3675 9 20.5001 9H21.5001C21.6327 9 21.7599 8.94732 21.8537 8.85355C21.9474 8.75979 22.0001 8.63261 22.0001 8.5V3.5C22.0001 3.10218 21.8421 2.72064 21.5608 2.43934C21.2795 2.15804 20.8979 2 20.5001 2V2Z" fill="currentColor"></path><path d="M21.5 13H20.5C20.3674 13 20.2402 13.0527 20.1464 13.1464C20.0527 13.2402 20 13.3674 20 13.5V20H4V4H10.5C10.6326 4 10.7598 3.94732 10.8536 3.85355C10.9473 3.75979 11 3.63261 11 3.5V2.5C11 2.36739 10.9473 2.24021 10.8536 2.14645C10.7598 2.05268 10.6326 2 10.5 2H3.5C3.10218 2 2.72064 2.15804 2.43934 2.43934C2.15804 2.72064 2 3.10218 2 3.5V20.5C2 20.8978 2.15804 21.2794 2.43934 21.5607C2.72064 21.842 3.10218 22 3.5 22H20.5C20.8978 22 21.2794 21.842 21.5607 21.5607C21.842 21.2794 22 20.8978 22 20.5V13.5C22 13.3674 21.9473 13.2402 21.8536 13.1464C21.7598 13.0527 21.6326 13 21.5 13Z" fill="currentColor"></path></svg><span class="hidden md:block">Fork</span></a></div></div><div class=" sp-layout"><div class=" sp-editor sp-stack"><div class=" sp-code-editor"><div aria-autocomplete="list" aria-label="Code Editor for App.js" aria-multiline="true" class="sp-pristine sp-javascript sp-cm" role="textbox" tabindex="0" translate="no"><pre class=" sp-pre-placeholder" style="margin-left:var(--sp-space-11)"><span class="sp-syntax-keyword">import</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">useState</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">useEffect</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">useRef</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">from</span> <span class="sp-syntax-string">'react'</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">import</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">useFadeIn</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">from</span> <span class="sp-syntax-string">'./useFadeIn.js'</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">Welcome</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">ref</span> = <span class="sp-syntax-definition">useRef</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-keyword">null</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-definition">useFadeIn</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">ref</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-static">1000</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">h1</span> <span class="sp-syntax-property">className</span>=<span class="sp-syntax-string">"welcome"</span> <span class="sp-syntax-property">ref</span>=<span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">ref</span><span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">></span> Welcome <span class="sp-syntax-punctuation"></</span><span class="sp-syntax-tag">h1</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">export</span> <span class="sp-syntax-keyword">default</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">App</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">show</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setShow</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-static">false</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">button</span> <span class="sp-syntax-property">onClick</span>=<span class="sp-syntax-punctuation">{</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-definition">setShow</span><span class="sp-syntax-punctuation">(</span>!<span class="sp-syntax-plain">show</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">show</span> ? <span class="sp-syntax-string">'Remove'</span> : <span class="sp-syntax-string">'Show'</span><span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-punctuation"></</span><span class="sp-syntax-tag">button</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">hr</span> <span class="sp-syntax-punctuation">/></span> <span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">show</span> && <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-definition">Welcome</span> <span class="sp-syntax-punctuation">/></span><span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-punctuation"></</span><span class="sp-syntax-punctuation">></span> <span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> </pre></div></div></div><div class=" order-last xl:order-2 sp-stack"><div class="p-0 sm:p-2 md:p-4 lg:p-8 bg-card dark:bg-wash-dark h-full relative md:rounded-b-lg lg:rounded-b-none"><div style="position:relative"><iframe class="rounded-t-none bg-white md:shadow-md sm:rounded-lg w-full max-w-full transition-opacity absolute opacity-0 pointer-events-none duration-75" title="Sandbox Preview" style="height:15px;z-index:-1"></iframe></div></div></div><button translate="yes" class="sandpack-expand flex text-base justify-between dark:border-card-dark bg-wash dark:bg-card-dark items-center z-10 p-1 w-full order-2 xl:order-last border-b-1 relative top-0"><span class="flex p-2 focus:outline-none text-primary dark:text-primary-dark leading-[20px]"><svg class="rotate-0 inline me-1.5 text-xl" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg>Show more</span></button></div></div></div></div><!--/$--><div class="max-w-4xl ms-0 2xl:mx-auto"> <p class="whitespace-pre-wrap my-4">You could keep the <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useFadeIn</code> code as is, but you could also refactor it more. For example, you could extract the logic for setting up the animation loop out of <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useFadeIn</code> into a custom <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useAnimationLoop</code> Hook:</p> </div><!--$--><div class="sandpack sandpack--playground w-full my-8" dir="ltr"><div class="sp-wrapper"><div class="shadow-lg dark:shadow-lg-dark rounded-lg" style="contain:content"><div class="bg-wash dark:bg-card-dark flex justify-between items-center relative z-10 border-b border-border dark:border-border-dark rounded-t-lg text-lg"><div class="flex-1 grow min-w-0 px-4 lg:px-6"><div><div class="relative overflow-hidden"><div class="w-[fit-content] invisible"><div class=" sp-tabs" translate="no"><div aria-label="Select active file" class=" sp-tabs-scrollable-container" role="tablist"><button aria-selected="false" class=" sp-tab-button" data-active="false" role="tab" title="/src/App.js" type="button">App.js</button><button aria-selected="true" class=" sp-tab-button" data-active="true" role="tab" title="/src/useFadeIn.js" type="button">useFadeIn.js</button></div></div></div><button class="absolute top-0 start-[2px]" id="headlessui-listbox-button-:R16aqcq6:" aria-haspopup="true" aria-expanded="false" data-headlessui-state=""><span class="h-full py-2 px-1 mt-px -mb-px flex border-b text-link dark:text-link-dark border-link dark:border-link-dark items-center text-md leading-tight truncate" style="max-width:160px">useFadeIn.js<span class="ms-2"><svg class="rotate-0" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg></span></span></button></div></div></div><div class="px-3 flex items-center justify-end text-start" translate="yes"><button class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1" title="Reset Sandbox" type="button"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative"><path d="M13.8982 5.20844C12.4626 4.88688 10.9686 4.93769 9.55821 5.35604L11.8524 3.06184C11.8989 3.0154 11.9357 2.96028 11.9608 2.89961C11.986 2.83894 11.9989 2.77391 11.9989 2.70824C11.9989 2.64256 11.986 2.57754 11.9608 2.51686C11.9357 2.45619 11.8989 2.40107 11.8524 2.35464L11.1456 1.64784C11.0992 1.60139 11.0441 1.56455 10.9834 1.53942C10.9227 1.51428 10.8577 1.50134 10.792 1.50134C10.7263 1.50134 10.6613 1.51428 10.6006 1.53942C10.54 1.56455 10.4848 1.60139 10.4384 1.64784L6.14571 5.94054C6.00654 6.07969 5.89615 6.2449 5.82083 6.42673C5.74551 6.60855 5.70675 6.80343 5.70675 7.00024C5.70675 7.19704 5.74551 7.39192 5.82083 7.57374C5.89615 7.75557 6.00654 7.92078 6.14571 8.05994L10.4387 12.3529C10.5325 12.4465 10.6595 12.4991 10.792 12.4991C10.9245 12.4991 11.0516 12.4465 11.1453 12.3529L11.8527 11.6455C11.9463 11.5518 11.9989 11.4247 11.9989 11.2922C11.9989 11.1598 11.9463 11.0327 11.8527 10.9389L8.77481 7.86104C9.99795 7.16236 11.415 6.8801 12.8125 7.05678C14.21 7.23347 15.5122 7.85953 16.523 8.84064C17.5338 9.82176 18.1983 11.1048 18.4165 12.4964C18.6347 13.888 18.3947 15.3129 17.7328 16.5562C17.0708 17.7996 16.0227 18.7942 14.7463 19.3902C13.47 19.9861 12.0345 20.1511 10.6563 19.8603C9.27798 19.5695 8.03152 18.8387 7.10469 17.778C6.17786 16.7172 5.62086 15.384 5.51761 13.9791C5.51156 13.8512 5.45689 13.7303 5.36477 13.6413C5.27265 13.5522 5.15001 13.5017 5.02191 13.5H4.02081C3.95297 13.4996 3.88574 13.5129 3.8232 13.5392C3.76065 13.5655 3.70408 13.6042 3.6569 13.6529C3.60972 13.7017 3.57291 13.7595 3.54869 13.8228C3.52448 13.8862 3.51336 13.9538 3.51601 14.0216C3.61349 15.5965 4.1473 17.1132 5.0577 18.4019C5.9681 19.6906 7.21917 20.7006 8.6709 21.3188C10.1226 21.937 11.7178 22.139 13.2778 21.9022C14.8378 21.6654 16.3011 20.9992 17.504 19.978C18.7069 18.9569 19.6019 17.6212 20.0889 16.1203C20.5759 14.6195 20.6356 13.0128 20.2614 11.4799C19.8872 9.94705 19.0938 8.54858 17.97 7.44098C16.8462 6.33339 15.4363 5.56037 13.8982 5.20844V5.20844Z" fill="currentColor"></path></svg> Reset</button><a href="https://codesandbox.io/api/v1/sandboxes/define?undefined&environment=create-react-app" rel="noreferrer noopener" target="_blank" title="Open in CodeSandbox" class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1 ms-2 md:ms-1"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative top-[1px]"><path d="M20.5001 2H15.5001C15.3675 2 15.2403 2.05268 15.1465 2.14645C15.0528 2.24021 15.0001 2.36739 15.0001 2.5V3.5C15.0001 3.63261 15.0528 3.75979 15.1465 3.85355C15.2403 3.94732 15.3675 4 15.5001 4H18.5901L7.6501 14.94C7.60323 14.9865 7.56604 15.0418 7.54065 15.1027C7.51527 15.1636 7.5022 15.229 7.5022 15.295C7.5022 15.361 7.51527 15.4264 7.54065 15.4873C7.56604 15.5482 7.60323 15.6035 7.6501 15.65L8.3501 16.35C8.39658 16.3969 8.45188 16.4341 8.51281 16.4594C8.57374 16.4848 8.63909 16.4979 8.7051 16.4979C8.7711 16.4979 8.83646 16.4848 8.89738 16.4594C8.95831 16.4341 9.01362 16.3969 9.0601 16.35L20.0001 5.41V8.5C20.0001 8.63261 20.0528 8.75979 20.1465 8.85355C20.2403 8.94732 20.3675 9 20.5001 9H21.5001C21.6327 9 21.7599 8.94732 21.8537 8.85355C21.9474 8.75979 22.0001 8.63261 22.0001 8.5V3.5C22.0001 3.10218 21.8421 2.72064 21.5608 2.43934C21.2795 2.15804 20.8979 2 20.5001 2V2Z" fill="currentColor"></path><path d="M21.5 13H20.5C20.3674 13 20.2402 13.0527 20.1464 13.1464C20.0527 13.2402 20 13.3674 20 13.5V20H4V4H10.5C10.6326 4 10.7598 3.94732 10.8536 3.85355C10.9473 3.75979 11 3.63261 11 3.5V2.5C11 2.36739 10.9473 2.24021 10.8536 2.14645C10.7598 2.05268 10.6326 2 10.5 2H3.5C3.10218 2 2.72064 2.15804 2.43934 2.43934C2.15804 2.72064 2 3.10218 2 3.5V20.5C2 20.8978 2.15804 21.2794 2.43934 21.5607C2.72064 21.842 3.10218 22 3.5 22H20.5C20.8978 22 21.2794 21.842 21.5607 21.5607C21.842 21.2794 22 20.8978 22 20.5V13.5C22 13.3674 21.9473 13.2402 21.8536 13.1464C21.7598 13.0527 21.6326 13 21.5 13Z" fill="currentColor"></path></svg><span class="hidden md:block">Fork</span></a></div></div><div class=" sp-layout"><div class=" sp-editor sp-stack"><div class=" sp-code-editor"><div aria-autocomplete="list" aria-label="Code Editor for useFadeIn.js" aria-multiline="true" class="sp-pristine sp-javascript sp-cm" role="textbox" tabindex="0" translate="no"><pre class=" sp-pre-placeholder" style="margin-left:var(--sp-space-11)"><span class="sp-syntax-keyword">import</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">useState</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">useEffect</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">from</span> <span class="sp-syntax-string">'react'</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">import</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">experimental_useEffectEvent</span> <span class="sp-syntax-keyword">as</span> <span class="sp-syntax-plain">useEffectEvent</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">from</span> <span class="sp-syntax-string">'react'</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">export</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">useFadeIn</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">ref</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">duration</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">isRunning</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setIsRunning</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-static">true</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-definition">useAnimationLoop</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">isRunning</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">timePassed</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">progress</span> = <span class="sp-syntax-plain">Math</span>.<span class="sp-syntax-property">min</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">timePassed</span> / <span class="sp-syntax-plain">duration</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-static">1</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-plain">ref</span>.<span class="sp-syntax-property">current</span>.<span class="sp-syntax-property">style</span>.<span class="sp-syntax-property">opacity</span> = <span class="sp-syntax-plain">progress</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">if</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">progress</span> === <span class="sp-syntax-static">1</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-definition">setIsRunning</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-static">false</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">useAnimationLoop</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">isRunning</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">drawFrame</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">onFrame</span> = <span class="sp-syntax-definition">useEffectEvent</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">drawFrame</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-definition">useEffect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">if</span> <span class="sp-syntax-punctuation">(</span>!<span class="sp-syntax-plain">isRunning</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">return</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">startTime</span> = <span class="sp-syntax-plain">performance</span>.<span class="sp-syntax-property">now</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">let</span> <span class="sp-syntax-plain">frameId</span> = <span class="sp-syntax-keyword">null</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">tick</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">now</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">timePassed</span> = <span class="sp-syntax-plain">now</span> - <span class="sp-syntax-plain">startTime</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-definition">onFrame</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">timePassed</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-plain">frameId</span> = <span class="sp-syntax-definition">requestAnimationFrame</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">tick</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-definition">tick</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-definition">cancelAnimationFrame</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">frameId</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">isRunning</span><span class="sp-syntax-punctuation">]</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> </pre></div></div></div><div class=" order-last xl:order-2 sp-stack"><div class="p-0 sm:p-2 md:p-4 lg:p-8 bg-card dark:bg-wash-dark h-full relative md:rounded-b-lg lg:rounded-b-none"><div style="position:relative"><iframe class="rounded-t-none bg-white md:shadow-md sm:rounded-lg w-full max-w-full transition-opacity absolute opacity-0 pointer-events-none duration-75" title="Sandbox Preview" style="height:15px;z-index:-1"></iframe></div></div></div><button translate="yes" class="sandpack-expand flex text-base justify-between dark:border-card-dark bg-wash dark:bg-card-dark items-center z-10 p-1 w-full order-2 xl:order-last border-b-1 relative top-0"><span class="flex p-2 focus:outline-none text-primary dark:text-primary-dark leading-[20px]"><svg class="rotate-0 inline me-1.5 text-xl" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg>Show more</span></button></div></div></div></div><!--/$--><div class="max-w-4xl ms-0 2xl:mx-auto"> <p class="whitespace-pre-wrap my-4">However, you didn’t <em>have to</em> do that. As with regular functions, ultimately you decide where to draw the boundaries between different parts of your code. You could also take a very different approach. Instead of keeping the logic in the Effect, you could move most of the imperative logic inside a JavaScript <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes" target="_blank" rel="nofollow noopener noreferrer" class="inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal">class:</a></p> </div><!--$--><div class="sandpack sandpack--playground w-full my-8" dir="ltr"><div class="sp-wrapper"><div class="shadow-lg dark:shadow-lg-dark rounded-lg" style="contain:content"><div class="bg-wash dark:bg-card-dark flex justify-between items-center relative z-10 border-b border-border dark:border-border-dark rounded-t-lg text-lg"><div class="flex-1 grow min-w-0 px-4 lg:px-6"><div><div class="relative overflow-hidden"><div class="w-[fit-content] invisible"><div class=" sp-tabs" translate="no"><div aria-label="Select active file" class=" sp-tabs-scrollable-container" role="tablist"><button aria-selected="false" class=" sp-tab-button" data-active="false" role="tab" title="/src/App.js" type="button">App.js</button><button aria-selected="true" class=" sp-tab-button" data-active="true" role="tab" title="/src/useFadeIn.js" type="button">useFadeIn.js</button><button aria-selected="false" class=" sp-tab-button" data-active="false" role="tab" title="/src/animation.js" type="button">animation.js</button></div></div></div><button class="absolute top-0 start-[2px]" id="headlessui-listbox-button-:R16ascq6:" aria-haspopup="true" aria-expanded="false" data-headlessui-state=""><span class="h-full py-2 px-1 mt-px -mb-px flex border-b text-link dark:text-link-dark border-link dark:border-link-dark items-center text-md leading-tight truncate" style="max-width:160px">useFadeIn.js<span class="ms-2"><svg class="rotate-0" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg></span></span></button></div></div></div><div class="px-3 flex items-center justify-end text-start" translate="yes"><button class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1" title="Reset Sandbox" type="button"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative"><path d="M13.8982 5.20844C12.4626 4.88688 10.9686 4.93769 9.55821 5.35604L11.8524 3.06184C11.8989 3.0154 11.9357 2.96028 11.9608 2.89961C11.986 2.83894 11.9989 2.77391 11.9989 2.70824C11.9989 2.64256 11.986 2.57754 11.9608 2.51686C11.9357 2.45619 11.8989 2.40107 11.8524 2.35464L11.1456 1.64784C11.0992 1.60139 11.0441 1.56455 10.9834 1.53942C10.9227 1.51428 10.8577 1.50134 10.792 1.50134C10.7263 1.50134 10.6613 1.51428 10.6006 1.53942C10.54 1.56455 10.4848 1.60139 10.4384 1.64784L6.14571 5.94054C6.00654 6.07969 5.89615 6.2449 5.82083 6.42673C5.74551 6.60855 5.70675 6.80343 5.70675 7.00024C5.70675 7.19704 5.74551 7.39192 5.82083 7.57374C5.89615 7.75557 6.00654 7.92078 6.14571 8.05994L10.4387 12.3529C10.5325 12.4465 10.6595 12.4991 10.792 12.4991C10.9245 12.4991 11.0516 12.4465 11.1453 12.3529L11.8527 11.6455C11.9463 11.5518 11.9989 11.4247 11.9989 11.2922C11.9989 11.1598 11.9463 11.0327 11.8527 10.9389L8.77481 7.86104C9.99795 7.16236 11.415 6.8801 12.8125 7.05678C14.21 7.23347 15.5122 7.85953 16.523 8.84064C17.5338 9.82176 18.1983 11.1048 18.4165 12.4964C18.6347 13.888 18.3947 15.3129 17.7328 16.5562C17.0708 17.7996 16.0227 18.7942 14.7463 19.3902C13.47 19.9861 12.0345 20.1511 10.6563 19.8603C9.27798 19.5695 8.03152 18.8387 7.10469 17.778C6.17786 16.7172 5.62086 15.384 5.51761 13.9791C5.51156 13.8512 5.45689 13.7303 5.36477 13.6413C5.27265 13.5522 5.15001 13.5017 5.02191 13.5H4.02081C3.95297 13.4996 3.88574 13.5129 3.8232 13.5392C3.76065 13.5655 3.70408 13.6042 3.6569 13.6529C3.60972 13.7017 3.57291 13.7595 3.54869 13.8228C3.52448 13.8862 3.51336 13.9538 3.51601 14.0216C3.61349 15.5965 4.1473 17.1132 5.0577 18.4019C5.9681 19.6906 7.21917 20.7006 8.6709 21.3188C10.1226 21.937 11.7178 22.139 13.2778 21.9022C14.8378 21.6654 16.3011 20.9992 17.504 19.978C18.7069 18.9569 19.6019 17.6212 20.0889 16.1203C20.5759 14.6195 20.6356 13.0128 20.2614 11.4799C19.8872 9.94705 19.0938 8.54858 17.97 7.44098C16.8462 6.33339 15.4363 5.56037 13.8982 5.20844V5.20844Z" fill="currentColor"></path></svg> Reset</button><a href="https://codesandbox.io/api/v1/sandboxes/define?undefined&environment=create-react-app" rel="noreferrer noopener" target="_blank" title="Open in CodeSandbox" class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1 ms-2 md:ms-1"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative top-[1px]"><path d="M20.5001 2H15.5001C15.3675 2 15.2403 2.05268 15.1465 2.14645C15.0528 2.24021 15.0001 2.36739 15.0001 2.5V3.5C15.0001 3.63261 15.0528 3.75979 15.1465 3.85355C15.2403 3.94732 15.3675 4 15.5001 4H18.5901L7.6501 14.94C7.60323 14.9865 7.56604 15.0418 7.54065 15.1027C7.51527 15.1636 7.5022 15.229 7.5022 15.295C7.5022 15.361 7.51527 15.4264 7.54065 15.4873C7.56604 15.5482 7.60323 15.6035 7.6501 15.65L8.3501 16.35C8.39658 16.3969 8.45188 16.4341 8.51281 16.4594C8.57374 16.4848 8.63909 16.4979 8.7051 16.4979C8.7711 16.4979 8.83646 16.4848 8.89738 16.4594C8.95831 16.4341 9.01362 16.3969 9.0601 16.35L20.0001 5.41V8.5C20.0001 8.63261 20.0528 8.75979 20.1465 8.85355C20.2403 8.94732 20.3675 9 20.5001 9H21.5001C21.6327 9 21.7599 8.94732 21.8537 8.85355C21.9474 8.75979 22.0001 8.63261 22.0001 8.5V3.5C22.0001 3.10218 21.8421 2.72064 21.5608 2.43934C21.2795 2.15804 20.8979 2 20.5001 2V2Z" fill="currentColor"></path><path d="M21.5 13H20.5C20.3674 13 20.2402 13.0527 20.1464 13.1464C20.0527 13.2402 20 13.3674 20 13.5V20H4V4H10.5C10.6326 4 10.7598 3.94732 10.8536 3.85355C10.9473 3.75979 11 3.63261 11 3.5V2.5C11 2.36739 10.9473 2.24021 10.8536 2.14645C10.7598 2.05268 10.6326 2 10.5 2H3.5C3.10218 2 2.72064 2.15804 2.43934 2.43934C2.15804 2.72064 2 3.10218 2 3.5V20.5C2 20.8978 2.15804 21.2794 2.43934 21.5607C2.72064 21.842 3.10218 22 3.5 22H20.5C20.8978 22 21.2794 21.842 21.5607 21.5607C21.842 21.2794 22 20.8978 22 20.5V13.5C22 13.3674 21.9473 13.2402 21.8536 13.1464C21.7598 13.0527 21.6326 13 21.5 13Z" fill="currentColor"></path></svg><span class="hidden md:block">Fork</span></a></div></div><div class=" rounded-b-lg overflow-hidden sp-layout"><div class=" sp-editor sp-stack"><div class=" sp-code-editor"><div aria-autocomplete="list" aria-label="Code Editor for useFadeIn.js" aria-multiline="true" class="sp-pristine sp-javascript sp-cm" role="textbox" tabindex="0" translate="no"><pre class=" sp-pre-placeholder" style="margin-left:var(--sp-space-11)"><span class="sp-syntax-keyword">import</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">useState</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">useEffect</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">from</span> <span class="sp-syntax-string">'react'</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">import</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">FadeInAnimation</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">from</span> <span class="sp-syntax-string">'./animation.js'</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">export</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">useFadeIn</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">ref</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">duration</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-definition">useEffect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">animation</span> = <span class="sp-syntax-keyword">new</span> <span class="sp-syntax-plain">FadeInAnimation</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">ref</span>.<span class="sp-syntax-property">current</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-plain">animation</span>.<span class="sp-syntax-property">start</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">duration</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">animation</span>.<span class="sp-syntax-property">stop</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">ref</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">duration</span><span class="sp-syntax-punctuation">]</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> </pre></div></div></div><div class=" order-last xl:order-2 sp-stack"><div class="p-0 sm:p-2 md:p-4 lg:p-8 bg-card dark:bg-wash-dark h-full relative md:rounded-b-lg lg:rounded-b-none"><div style="position:relative"><iframe class="rounded-t-none bg-white md:shadow-md sm:rounded-lg w-full max-w-full transition-opacity absolute opacity-0 pointer-events-none duration-75" title="Sandbox Preview" style="height:15px;z-index:-1"></iframe></div></div></div></div></div></div></div><!--/$--><div class="max-w-4xl ms-0 2xl:mx-auto"> <p class="whitespace-pre-wrap my-4">Effects let you connect React to external systems. The more coordination between Effects is needed (for example, to chain multiple animations), the more it makes sense to extract that logic out of Effects and Hooks <em>completely</em> like in the sandbox above. Then, the code you extracted <em>becomes</em> the “external system”. This lets your Effects stay simple because they only need to send messages to the system you’ve moved outside React.</p> <p class="whitespace-pre-wrap my-4">The examples above assume that the fade-in logic needs to be written in JavaScript. However, this particular fade-in animation is both simpler and much more efficient to implement with a plain <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations" target="_blank" rel="nofollow noopener noreferrer" class="inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal">CSS Animation:</a></p> </div><!--$--><div class="sandpack sandpack--playground w-full my-8" dir="ltr"><div class="sp-wrapper"><div class="shadow-lg dark:shadow-lg-dark rounded-lg" style="contain:content"><div class="bg-wash dark:bg-card-dark flex justify-between items-center relative z-10 border-b border-border dark:border-border-dark rounded-t-lg text-lg"><div class="flex-1 grow min-w-0 px-4 lg:px-6"><div><div class="relative overflow-hidden"><div class="w-[fit-content] invisible"><div class=" sp-tabs" translate="no"><div aria-label="Select active file" class=" sp-tabs-scrollable-container" role="tablist"><button aria-selected="false" class=" sp-tab-button" data-active="false" role="tab" title="/src/App.js" type="button">App.js</button><button aria-selected="true" class=" sp-tab-button" data-active="true" role="tab" title="/src/welcome.css" type="button">welcome.css</button></div></div></div><button class="absolute top-0 start-[2px]" id="headlessui-listbox-button-:R16aucq6:" aria-haspopup="true" aria-expanded="false" data-headlessui-state=""><span class="h-full py-2 px-1 mt-px -mb-px flex border-b text-link dark:text-link-dark border-link dark:border-link-dark items-center text-md leading-tight truncate" style="max-width:160px">welcome.css<span class="ms-2"><svg class="rotate-0" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg></span></span></button></div></div></div><div class="px-3 flex items-center justify-end text-start" translate="yes"><button class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1" title="Reset Sandbox" type="button"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative"><path d="M13.8982 5.20844C12.4626 4.88688 10.9686 4.93769 9.55821 5.35604L11.8524 3.06184C11.8989 3.0154 11.9357 2.96028 11.9608 2.89961C11.986 2.83894 11.9989 2.77391 11.9989 2.70824C11.9989 2.64256 11.986 2.57754 11.9608 2.51686C11.9357 2.45619 11.8989 2.40107 11.8524 2.35464L11.1456 1.64784C11.0992 1.60139 11.0441 1.56455 10.9834 1.53942C10.9227 1.51428 10.8577 1.50134 10.792 1.50134C10.7263 1.50134 10.6613 1.51428 10.6006 1.53942C10.54 1.56455 10.4848 1.60139 10.4384 1.64784L6.14571 5.94054C6.00654 6.07969 5.89615 6.2449 5.82083 6.42673C5.74551 6.60855 5.70675 6.80343 5.70675 7.00024C5.70675 7.19704 5.74551 7.39192 5.82083 7.57374C5.89615 7.75557 6.00654 7.92078 6.14571 8.05994L10.4387 12.3529C10.5325 12.4465 10.6595 12.4991 10.792 12.4991C10.9245 12.4991 11.0516 12.4465 11.1453 12.3529L11.8527 11.6455C11.9463 11.5518 11.9989 11.4247 11.9989 11.2922C11.9989 11.1598 11.9463 11.0327 11.8527 10.9389L8.77481 7.86104C9.99795 7.16236 11.415 6.8801 12.8125 7.05678C14.21 7.23347 15.5122 7.85953 16.523 8.84064C17.5338 9.82176 18.1983 11.1048 18.4165 12.4964C18.6347 13.888 18.3947 15.3129 17.7328 16.5562C17.0708 17.7996 16.0227 18.7942 14.7463 19.3902C13.47 19.9861 12.0345 20.1511 10.6563 19.8603C9.27798 19.5695 8.03152 18.8387 7.10469 17.778C6.17786 16.7172 5.62086 15.384 5.51761 13.9791C5.51156 13.8512 5.45689 13.7303 5.36477 13.6413C5.27265 13.5522 5.15001 13.5017 5.02191 13.5H4.02081C3.95297 13.4996 3.88574 13.5129 3.8232 13.5392C3.76065 13.5655 3.70408 13.6042 3.6569 13.6529C3.60972 13.7017 3.57291 13.7595 3.54869 13.8228C3.52448 13.8862 3.51336 13.9538 3.51601 14.0216C3.61349 15.5965 4.1473 17.1132 5.0577 18.4019C5.9681 19.6906 7.21917 20.7006 8.6709 21.3188C10.1226 21.937 11.7178 22.139 13.2778 21.9022C14.8378 21.6654 16.3011 20.9992 17.504 19.978C18.7069 18.9569 19.6019 17.6212 20.0889 16.1203C20.5759 14.6195 20.6356 13.0128 20.2614 11.4799C19.8872 9.94705 19.0938 8.54858 17.97 7.44098C16.8462 6.33339 15.4363 5.56037 13.8982 5.20844V5.20844Z" fill="currentColor"></path></svg> Reset</button><a href="https://codesandbox.io/api/v1/sandboxes/define?undefined&environment=create-react-app" rel="noreferrer noopener" target="_blank" title="Open in CodeSandbox" class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1 ms-2 md:ms-1"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative top-[1px]"><path d="M20.5001 2H15.5001C15.3675 2 15.2403 2.05268 15.1465 2.14645C15.0528 2.24021 15.0001 2.36739 15.0001 2.5V3.5C15.0001 3.63261 15.0528 3.75979 15.1465 3.85355C15.2403 3.94732 15.3675 4 15.5001 4H18.5901L7.6501 14.94C7.60323 14.9865 7.56604 15.0418 7.54065 15.1027C7.51527 15.1636 7.5022 15.229 7.5022 15.295C7.5022 15.361 7.51527 15.4264 7.54065 15.4873C7.56604 15.5482 7.60323 15.6035 7.6501 15.65L8.3501 16.35C8.39658 16.3969 8.45188 16.4341 8.51281 16.4594C8.57374 16.4848 8.63909 16.4979 8.7051 16.4979C8.7711 16.4979 8.83646 16.4848 8.89738 16.4594C8.95831 16.4341 9.01362 16.3969 9.0601 16.35L20.0001 5.41V8.5C20.0001 8.63261 20.0528 8.75979 20.1465 8.85355C20.2403 8.94732 20.3675 9 20.5001 9H21.5001C21.6327 9 21.7599 8.94732 21.8537 8.85355C21.9474 8.75979 22.0001 8.63261 22.0001 8.5V3.5C22.0001 3.10218 21.8421 2.72064 21.5608 2.43934C21.2795 2.15804 20.8979 2 20.5001 2V2Z" fill="currentColor"></path><path d="M21.5 13H20.5C20.3674 13 20.2402 13.0527 20.1464 13.1464C20.0527 13.2402 20 13.3674 20 13.5V20H4V4H10.5C10.6326 4 10.7598 3.94732 10.8536 3.85355C10.9473 3.75979 11 3.63261 11 3.5V2.5C11 2.36739 10.9473 2.24021 10.8536 2.14645C10.7598 2.05268 10.6326 2 10.5 2H3.5C3.10218 2 2.72064 2.15804 2.43934 2.43934C2.15804 2.72064 2 3.10218 2 3.5V20.5C2 20.8978 2.15804 21.2794 2.43934 21.5607C2.72064 21.842 3.10218 22 3.5 22H20.5C20.8978 22 21.2794 21.842 21.5607 21.5607C21.842 21.2794 22 20.8978 22 20.5V13.5C22 13.3674 21.9473 13.2402 21.8536 13.1464C21.7598 13.0527 21.6326 13 21.5 13Z" fill="currentColor"></path></svg><span class="hidden md:block">Fork</span></a></div></div><div class=" rounded-b-lg overflow-hidden sp-layout"><div class=" sp-editor sp-stack"><div class=" sp-code-editor"><div aria-autocomplete="list" aria-label="Code Editor for welcome.css" aria-multiline="true" class="sp-pristine sp-css sp-cm" role="textbox" tabindex="0" translate="no"><pre class=" sp-pre-placeholder" style="margin-left:var(--sp-space-11)">.welcome <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">color</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-static">white</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-property">padding</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-static">50</span><span class="sp-syntax-keyword">px</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-property">text-align</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-static">center</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-property">font-size</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-static">50</span><span class="sp-syntax-keyword">px</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-property">background-image</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-keyword">radial-gradient</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-static">circle</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-keyword">rgba</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-static">63</span><span class="sp-syntax-punctuation">,</span><span class="sp-syntax-static">94</span><span class="sp-syntax-punctuation">,</span><span class="sp-syntax-static">251</span><span class="sp-syntax-punctuation">,</span><span class="sp-syntax-static">1</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-static">0</span><span class="sp-syntax-keyword">%</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-keyword">rgba</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-static">252</span><span class="sp-syntax-punctuation">,</span><span class="sp-syntax-static">70</span><span class="sp-syntax-punctuation">,</span><span class="sp-syntax-static">107</span><span class="sp-syntax-punctuation">,</span><span class="sp-syntax-static">1</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-static">100</span><span class="sp-syntax-keyword">%</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-property">animation</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-static">fadeIn</span> <span class="sp-syntax-static">1000</span><span class="sp-syntax-keyword">ms</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">@keyframes</span> fadeIn <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-static">0</span><span class="sp-syntax-keyword">%</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">opacity</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-static">0</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-static">100</span><span class="sp-syntax-keyword">%</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-property">opacity</span><span class="sp-syntax-punctuation">:</span> <span class="sp-syntax-static">1</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-punctuation">}</span> </pre></div></div></div><div class=" order-last xl:order-2 sp-stack"><div class="p-0 sm:p-2 md:p-4 lg:p-8 bg-card dark:bg-wash-dark h-full relative md:rounded-b-lg lg:rounded-b-none"><div style="position:relative"><iframe class="rounded-t-none bg-white md:shadow-md sm:rounded-lg w-full max-w-full transition-opacity absolute opacity-0 pointer-events-none duration-75" title="Sandbox Preview" style="height:15px;z-index:-1"></iframe></div></div></div></div></div></div></div><!--/$--><div class="max-w-4xl ms-0 2xl:mx-auto"> <p class="whitespace-pre-wrap my-4">Sometimes, you don’t even need a Hook!</p> <section><h2 id="recap" class="mdx-heading text-3xl font-display leading-10 text-primary dark:text-primary-dark font-bold my-6">Recap<a href="#recap" aria-label="Link for Recap" title="Link for Recap" class="mdx-header-anchor inline-block"><svg width="1em" height="1em" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="text-gray-70 ms-2 h-5 w-5"><g fill="currentColor" fill-rule="evenodd"><path d="M7.778 7.975a2.5 2.5 0 0 0 .347-3.837L6.017 2.03a2.498 2.498 0 0 0-3.542-.007 2.5 2.5 0 0 0 .006 3.543l1.153 1.15c.07-.29.154-.563.25-.773.036-.077.084-.16.14-.25L3.18 4.85a1.496 1.496 0 0 1 .002-2.12 1.496 1.496 0 0 1 2.12 0l2.124 2.123a1.496 1.496 0 0 1-.333 2.37c.16.246.42.504.685.752z"></path><path d="M5.657 4.557a2.5 2.5 0 0 0-.347 3.837l2.108 2.108a2.498 2.498 0 0 0 3.542.007 2.5 2.5 0 0 0-.006-3.543L9.802 5.815c-.07.29-.154.565-.25.774-.036.076-.084.16-.14.25l.842.84c.585.587.59 1.532 0 2.122-.587.585-1.532.59-2.12 0L6.008 7.68a1.496 1.496 0 0 1 .332-2.372c-.16-.245-.42-.503-.685-.75z"></path></g></svg></a></h2><ul class="ms-6 my-3 list-disc"> <li class="leading-relaxed mb-1">Custom Hooks let you share logic between components.</li> <li class="leading-relaxed mb-1">Custom Hooks must be named starting with <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">use</code> followed by a capital letter.</li> <li class="leading-relaxed mb-1">Custom Hooks only share stateful logic, not state itself.</li> <li class="leading-relaxed mb-1">You can pass reactive values from one Hook to another, and they stay up-to-date.</li> <li class="leading-relaxed mb-1">All Hooks re-run every time your component re-renders.</li> <li class="leading-relaxed mb-1">The code of your custom Hooks should be pure, like your component’s code.</li> <li class="leading-relaxed mb-1">Wrap event handlers received by custom Hooks into Effect Events.</li> <li class="leading-relaxed mb-1">Don’t create custom Hooks like <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useMount</code>. Keep their purpose specific.</li> <li class="leading-relaxed mb-1">It’s up to you how and where to choose the boundaries of your code.</li> </ul></section> </div><div class="max-w-7xl mx-auto py-4 w-full"><div class="border-gray-10 bg-card dark:bg-card-dark shadow-inner rounded-none -mx-5 sm:mx-auto sm:rounded-2xl"><div class="py-2 px-5 sm:px-8 pb-0 md:pb-0"><h2 id="challenges" class="mdx-heading text-3xl font-display leading-10 text-primary dark:text-primary-dark font-bold my-6 mb-2 leading-10 relative text-3xl text-link">Try out some challenges<a href="#challenges" aria-label="Link for Try out some challenges" title="Link for Try out some challenges" class="mdx-header-anchor inline-block"><svg width="1em" height="1em" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="text-gray-70 ms-2 h-5 w-5"><g fill="currentColor" fill-rule="evenodd"><path d="M7.778 7.975a2.5 2.5 0 0 0 .347-3.837L6.017 2.03a2.498 2.498 0 0 0-3.542-.007 2.5 2.5 0 0 0 .006 3.543l1.153 1.15c.07-.29.154-.563.25-.773.036-.077.084-.16.14-.25L3.18 4.85a1.496 1.496 0 0 1 .002-2.12 1.496 1.496 0 0 1 2.12 0l2.124 2.123a1.496 1.496 0 0 1-.333 2.37c.16.246.42.504.685.752z"></path><path d="M5.657 4.557a2.5 2.5 0 0 0-.347 3.837l2.108 2.108a2.498 2.498 0 0 0 3.542.007 2.5 2.5 0 0 0-.006-3.543L9.802 5.815c-.07.29-.154.565-.25.774-.036.076-.084.16-.14.25l.842.84c.585.587.59 1.532 0 2.122-.587.585-1.532.59-2.12 0L6.008 7.68a1.496 1.496 0 0 1 .332-2.372c-.16-.245-.42-.503-.685-.75z"></path></g></svg></a></h2><div class="flex items-center justify-between"><div class="overflow-hidden"><div class="flex relative transition-transform content-box overflow-x-auto"><button class="py-2 me-4 text-base border-b-4 duration-100 ease-in transition whitespace-nowrap text-ellipsis text-link border-link hover:text-link dark:text-link-dark dark:border-link-dark dark:hover:text-link-dark">1<!-- -->. <!-- -->Extract a <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useCounter</code> Hook </button><button class="py-2 me-4 text-base border-b-4 duration-100 ease-in transition whitespace-nowrap text-ellipsis">2<!-- -->. <!-- -->Make the counter delay configurable </button><button class="py-2 me-4 text-base border-b-4 duration-100 ease-in transition whitespace-nowrap text-ellipsis">3<!-- -->. <!-- -->Extract <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useInterval</code> out of <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useCounter</code> </button><button class="py-2 me-4 text-base border-b-4 duration-100 ease-in transition whitespace-nowrap text-ellipsis">4<!-- -->. <!-- -->Fix a resetting interval </button><button class="py-2 me-4 text-base border-b-4 duration-100 ease-in transition whitespace-nowrap text-ellipsis">5<!-- -->. <!-- -->Implement a staggering movement </button></div></div><div class="flex z-10 pb-2 ps-2"><button aria-label="Scroll left" class="bg-secondary-button dark:bg-secondary-button-dark h-8 px-2 rounded-l border-gray-20 border-r rtl:rotate-180 text-gray-30"><svg class="rotate-90 rtl:-rotate-90" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg></button><button aria-label="Scroll right" class="bg-secondary-button dark:bg-secondary-button-dark h-8 px-2 rounded-e rtl:rotate-180 text-primary dark:text-primary-dark"><svg class="-rotate-90 rtl:rotate-90" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg></button></div></div></div><div class="p-5 sm:py-8 sm:px-8"><div><h4 id="extract-a-usecounter-hook" class="mdx-heading text-xl text-primary dark:text-primary-dark mb-2 mt-0 font-medium text-xl font-display font-bold leading-9 my-4"><div class="font-bold block md:inline">Challenge<!-- --> <!-- -->1<!-- --> of<!-- --> <!-- -->5<span class="text-primary dark:text-primary-dark">: </span></div>Extract a <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useCounter</code> Hook <a href="#extract-a-usecounter-hook" aria-label="Link for this heading" title="Link for this heading" class="mdx-header-anchor inline-block"><svg width="1em" height="1em" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="text-gray-70 ms-2 h-5 w-5"><g fill="currentColor" fill-rule="evenodd"><path d="M7.778 7.975a2.5 2.5 0 0 0 .347-3.837L6.017 2.03a2.498 2.498 0 0 0-3.542-.007 2.5 2.5 0 0 0 .006 3.543l1.153 1.15c.07-.29.154-.563.25-.773.036-.077.084-.16.14-.25L3.18 4.85a1.496 1.496 0 0 1 .002-2.12 1.496 1.496 0 0 1 2.12 0l2.124 2.123a1.496 1.496 0 0 1-.333 2.37c.16.246.42.504.685.752z"></path><path d="M5.657 4.557a2.5 2.5 0 0 0-.347 3.837l2.108 2.108a2.498 2.498 0 0 0 3.542.007 2.5 2.5 0 0 0-.006-3.543L9.802 5.815c-.07.29-.154.565-.25.774-.036.076-.084.16-.14.25l.842.84c.585.587.59 1.532 0 2.122-.587.585-1.532.59-2.12 0L6.008 7.68a1.496 1.496 0 0 1 .332-2.372c-.16-.245-.42-.503-.685-.75z"></path></g></svg></a></h4><p class="whitespace-pre-wrap my-4">This component uses a state variable and an Effect to display a number that increments every second. Extract this logic into a custom Hook called <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useCounter</code>. Your goal is to make the <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">Counter</code> component implementation look exactly like this:</p><!--$--><div dir="ltr" class="sandpack sandpack--codeblock rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg my-8" style="contain:content"><div class="sp-wrapper"><div class="sp-stack"><div class="sp-code-editor"><pre class="sp-cm sp-pristine sp-javascript flex align-start"><code class="sp-pre-placeholder grow-[2]"><div class="cm-line "><span class="sp-syntax-keyword">export</span> <span class="sp-syntax-keyword">default</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">Counter</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">count</span> = <span class="sp-syntax-definition">useCounter</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">h1</span><span class="sp-syntax-punctuation">></span>Seconds passed: <span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">count</span><span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation"></</span><span class="sp-syntax-tag">h1</span><span class="sp-syntax-punctuation">></span><span class="sp-syntax-punctuation">;</span><br/></div><div class="cm-line "><span class="sp-syntax-punctuation">}</span></div></code></pre></div></div></div></div><!--/$--><p class="whitespace-pre-wrap my-4">You’ll need to write your custom Hook in <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">useCounter.js</code> and import it into the <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">App.js</code> file.</p><!--$--><div class="sandpack sandpack--playground w-full my-8" dir="ltr"><div class="sp-wrapper"><div class="shadow-lg dark:shadow-lg-dark rounded-lg" style="contain:content"><div class="bg-wash dark:bg-card-dark flex justify-between items-center relative z-10 border-b border-border dark:border-border-dark rounded-t-lg text-lg"><div class="flex-1 grow min-w-0 px-4 lg:px-6"><div><div class="relative overflow-hidden"><div class="w-[fit-content] invisible"><div class=" sp-tabs" translate="no"><div aria-label="Select active file" class=" sp-tabs-scrollable-container" role="tablist"><button aria-selected="true" class=" sp-tab-button" data-active="true" role="tab" title="/src/App.js" type="button">App.js</button><button aria-selected="false" class=" sp-tab-button" data-active="false" role="tab" title="/src/useCounter.js" type="button">useCounter.js</button></div></div></div><button class="absolute top-0 start-[2px]" id="headlessui-listbox-button-:R16b4d0cq6:" aria-haspopup="true" aria-expanded="false" data-headlessui-state=""><span class="h-full py-2 px-1 mt-px -mb-px flex border-b text-link dark:text-link-dark border-link dark:border-link-dark items-center text-md leading-tight truncate" style="max-width:160px">App.js<span class="ms-2"><svg class="rotate-0" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg></span></span></button></div></div></div><div class="px-3 flex items-center justify-end text-start" translate="yes"><button class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1" title="Reset Sandbox" type="button"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative"><path d="M13.8982 5.20844C12.4626 4.88688 10.9686 4.93769 9.55821 5.35604L11.8524 3.06184C11.8989 3.0154 11.9357 2.96028 11.9608 2.89961C11.986 2.83894 11.9989 2.77391 11.9989 2.70824C11.9989 2.64256 11.986 2.57754 11.9608 2.51686C11.9357 2.45619 11.8989 2.40107 11.8524 2.35464L11.1456 1.64784C11.0992 1.60139 11.0441 1.56455 10.9834 1.53942C10.9227 1.51428 10.8577 1.50134 10.792 1.50134C10.7263 1.50134 10.6613 1.51428 10.6006 1.53942C10.54 1.56455 10.4848 1.60139 10.4384 1.64784L6.14571 5.94054C6.00654 6.07969 5.89615 6.2449 5.82083 6.42673C5.74551 6.60855 5.70675 6.80343 5.70675 7.00024C5.70675 7.19704 5.74551 7.39192 5.82083 7.57374C5.89615 7.75557 6.00654 7.92078 6.14571 8.05994L10.4387 12.3529C10.5325 12.4465 10.6595 12.4991 10.792 12.4991C10.9245 12.4991 11.0516 12.4465 11.1453 12.3529L11.8527 11.6455C11.9463 11.5518 11.9989 11.4247 11.9989 11.2922C11.9989 11.1598 11.9463 11.0327 11.8527 10.9389L8.77481 7.86104C9.99795 7.16236 11.415 6.8801 12.8125 7.05678C14.21 7.23347 15.5122 7.85953 16.523 8.84064C17.5338 9.82176 18.1983 11.1048 18.4165 12.4964C18.6347 13.888 18.3947 15.3129 17.7328 16.5562C17.0708 17.7996 16.0227 18.7942 14.7463 19.3902C13.47 19.9861 12.0345 20.1511 10.6563 19.8603C9.27798 19.5695 8.03152 18.8387 7.10469 17.778C6.17786 16.7172 5.62086 15.384 5.51761 13.9791C5.51156 13.8512 5.45689 13.7303 5.36477 13.6413C5.27265 13.5522 5.15001 13.5017 5.02191 13.5H4.02081C3.95297 13.4996 3.88574 13.5129 3.8232 13.5392C3.76065 13.5655 3.70408 13.6042 3.6569 13.6529C3.60972 13.7017 3.57291 13.7595 3.54869 13.8228C3.52448 13.8862 3.51336 13.9538 3.51601 14.0216C3.61349 15.5965 4.1473 17.1132 5.0577 18.4019C5.9681 19.6906 7.21917 20.7006 8.6709 21.3188C10.1226 21.937 11.7178 22.139 13.2778 21.9022C14.8378 21.6654 16.3011 20.9992 17.504 19.978C18.7069 18.9569 19.6019 17.6212 20.0889 16.1203C20.5759 14.6195 20.6356 13.0128 20.2614 11.4799C19.8872 9.94705 19.0938 8.54858 17.97 7.44098C16.8462 6.33339 15.4363 5.56037 13.8982 5.20844V5.20844Z" fill="currentColor"></path></svg> Reset</button><a href="https://codesandbox.io/api/v1/sandboxes/define?undefined&environment=create-react-app" rel="noreferrer noopener" target="_blank" title="Open in CodeSandbox" class="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1 ms-2 md:ms-1"><svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="inline mx-1 relative top-[1px]"><path d="M20.5001 2H15.5001C15.3675 2 15.2403 2.05268 15.1465 2.14645C15.0528 2.24021 15.0001 2.36739 15.0001 2.5V3.5C15.0001 3.63261 15.0528 3.75979 15.1465 3.85355C15.2403 3.94732 15.3675 4 15.5001 4H18.5901L7.6501 14.94C7.60323 14.9865 7.56604 15.0418 7.54065 15.1027C7.51527 15.1636 7.5022 15.229 7.5022 15.295C7.5022 15.361 7.51527 15.4264 7.54065 15.4873C7.56604 15.5482 7.60323 15.6035 7.6501 15.65L8.3501 16.35C8.39658 16.3969 8.45188 16.4341 8.51281 16.4594C8.57374 16.4848 8.63909 16.4979 8.7051 16.4979C8.7711 16.4979 8.83646 16.4848 8.89738 16.4594C8.95831 16.4341 9.01362 16.3969 9.0601 16.35L20.0001 5.41V8.5C20.0001 8.63261 20.0528 8.75979 20.1465 8.85355C20.2403 8.94732 20.3675 9 20.5001 9H21.5001C21.6327 9 21.7599 8.94732 21.8537 8.85355C21.9474 8.75979 22.0001 8.63261 22.0001 8.5V3.5C22.0001 3.10218 21.8421 2.72064 21.5608 2.43934C21.2795 2.15804 20.8979 2 20.5001 2V2Z" fill="currentColor"></path><path d="M21.5 13H20.5C20.3674 13 20.2402 13.0527 20.1464 13.1464C20.0527 13.2402 20 13.3674 20 13.5V20H4V4H10.5C10.6326 4 10.7598 3.94732 10.8536 3.85355C10.9473 3.75979 11 3.63261 11 3.5V2.5C11 2.36739 10.9473 2.24021 10.8536 2.14645C10.7598 2.05268 10.6326 2 10.5 2H3.5C3.10218 2 2.72064 2.15804 2.43934 2.43934C2.15804 2.72064 2 3.10218 2 3.5V20.5C2 20.8978 2.15804 21.2794 2.43934 21.5607C2.72064 21.842 3.10218 22 3.5 22H20.5C20.8978 22 21.2794 21.842 21.5607 21.5607C21.842 21.2794 22 20.8978 22 20.5V13.5C22 13.3674 21.9473 13.2402 21.8536 13.1464C21.7598 13.0527 21.6326 13 21.5 13Z" fill="currentColor"></path></svg><span class="hidden md:block">Fork</span></a></div></div><div class=" rounded-b-lg overflow-hidden sp-layout"><div class=" sp-editor sp-stack"><div class=" sp-code-editor"><div aria-autocomplete="list" aria-label="Code Editor for App.js" aria-multiline="true" class="sp-pristine sp-javascript sp-cm" role="textbox" tabindex="0" translate="no"><pre class=" sp-pre-placeholder" style="margin-left:var(--sp-space-11)"><span class="sp-syntax-keyword">import</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-plain">useState</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">useEffect</span> <span class="sp-syntax-punctuation">}</span> <span class="sp-syntax-keyword">from</span> <span class="sp-syntax-string">'react'</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">export</span> <span class="sp-syntax-keyword">default</span> <span class="sp-syntax-keyword">function</span> <span class="sp-syntax-definition">Counter</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-plain">count</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-plain">setCount</span><span class="sp-syntax-punctuation">]</span> = <span class="sp-syntax-definition">useState</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-static">0</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-definition">useEffect</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-keyword">const</span> <span class="sp-syntax-plain">id</span> = <span class="sp-syntax-definition">setInterval</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-punctuation">{</span> <span class="sp-syntax-definition">setCount</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">c</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-plain">c</span> + <span class="sp-syntax-static">1</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-static">1000</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation">(</span><span class="sp-syntax-punctuation">)</span> <span class="sp-syntax-punctuation">=></span> <span class="sp-syntax-definition">clearInterval</span><span class="sp-syntax-punctuation">(</span><span class="sp-syntax-plain">id</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation">,</span> <span class="sp-syntax-punctuation">[</span><span class="sp-syntax-punctuation">]</span><span class="sp-syntax-punctuation">)</span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-keyword">return</span> <span class="sp-syntax-punctuation"><</span><span class="sp-syntax-tag">h1</span><span class="sp-syntax-punctuation">></span>Seconds passed: <span class="sp-syntax-punctuation">{</span><span class="sp-syntax-plain">count</span><span class="sp-syntax-punctuation">}</span><span class="sp-syntax-punctuation"></</span><span class="sp-syntax-tag">h1</span><span class="sp-syntax-punctuation">></span><span class="sp-syntax-punctuation">;</span> <span class="sp-syntax-punctuation">}</span> </pre></div></div></div><div class=" order-last xl:order-2 sp-stack"><div class="p-0 sm:p-2 md:p-4 lg:p-8 bg-card dark:bg-wash-dark h-full relative md:rounded-b-lg lg:rounded-b-none"><div style="position:relative"><iframe class="rounded-t-none bg-white md:shadow-md sm:rounded-lg w-full max-w-full transition-opacity absolute opacity-0 pointer-events-none duration-75" title="Sandbox Preview" style="height:15px;z-index:-1"></iframe></div></div></div></div></div></div></div><!--/$--></div><div class="flex justify-between items-center mt-4"><button class="me-2 text-base leading-tight font-bold rounded-full py-2 px-4 focus:outline focus:outline-offset-2 focus:outline-link dark:focus:outline-link-dark inline-flex items-center my-1 bg-transparent text-primary dark:text-primary-dark active:text-primary shadow-secondary-button-stroke dark:shadow-secondary-button-stroke-dark hover:bg-gray-40/5 active:bg-gray-40/10 hover:dark:bg-gray-60/5 active:dark:bg-gray-60/10"><svg class="inline me-1.5" width="0.75em" height="0.75em" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg"><path d="M2.21908 8.74479V12.7448H0.885742V0.078125H7.14041C7.26418 0.0781911 7.3855 0.112714 7.49076 0.177827C7.59602 0.242939 7.68108 0.336071 7.73641 0.446792L8.21908 1.41146H12.2191C12.3959 1.41146 12.5655 1.4817 12.6905 1.60672C12.8155 1.73174 12.8857 1.90131 12.8857 2.07812V9.41146C12.8857 9.58827 12.8155 9.75784 12.6905 9.88286C12.5655 10.0079 12.3959 10.0781 12.2191 10.0781H7.96441C7.84063 10.0781 7.71932 10.0435 7.61406 9.97842C7.50879 9.91331 7.42374 9.82018 7.36841 9.70946L6.88574 8.74479H2.21908ZM2.21908 1.41146V7.41146H7.70974L8.37641 8.74479H11.5524V2.74479H7.39508L6.72841 1.41146H2.21908Z" fill="currentColor"></path></svg> <!-- -->Show solution</button><button class="bg-link dark:bg-link-dark text-base leading-tight font-bold rounded-full py-2 px-4 focus:outline focus:outline-offset-2 focus:outline-link dark:focus:outline-link-dark inline-flex items-center my-1 bg-link border-link text-white hover:bg-link focus:bg-link active:bg-link">Next <!-- -->Challenge<svg width="1em" height="1em" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" class="block ms-1.5"><path d="M6.86612 13.6161C6.37796 14.1043 6.37796 14.8957 6.86612 15.3839C7.35427 15.872 8.14572 15.872 8.63388 15.3839L13.1339 10.8839C13.622 10.3957 13.622 9.60428 13.1339 9.11612L8.63388 4.61612C8.14572 4.12797 7.35427 4.12797 6.86612 4.61612C6.37796 5.10428 6.37796 5.89573 6.86612 6.38388L10.4822 10L6.86612 13.6161Z" fill="currentColor"></path></svg></button></div></div></div></div></div><div class="grid grid-cols-1 gap-4 py-4 mx-auto max-w-7xl md:grid-cols-2 md:py-12"><a class="flex gap-x-4 md:gap-x-6 items-center w-full md:min-w-80 md:w-fit md:max-w-md px-4 md:px-5 py-6 border-2 border-transparent text-base leading-base text-link dark:text-link-dark rounded-lg group focus:text-link dark:focus:text-link-dark focus:bg-highlight focus:border-link dark:focus:bg-highlight-dark dark:focus:border-link-dark focus:border-opacity-100 focus:border-2 focus:ring-1 focus:ring-offset-4 focus:ring-blue-40 active:ring-0 active:ring-offset-0 hover:bg-gray-5 dark:hover:bg-gray-80" href="/learn/removing-effect-dependencies"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" class="duration-100 ease-in transition rotate-90 rtl:-rotate-90 inline text-tertiary dark:text-tertiary-dark group-focus:text-link dark:group-focus:text-link-dark" style="min-width:20px;min-height:20px"><g fill="none" fill-rule="evenodd" transform="translate(-446 -398)"><path fill="currentColor" fill-rule="nonzero" d="M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z" transform="translate(356.5 164.5)"></path><polygon points="446 418 466 418 466 398 446 398"></polygon></g></svg><div class="flex flex-col overflow-hidden"><span class="text-sm font-bold tracking-wide no-underline uppercase text-secondary dark:text-secondary-dark group-focus:text-link dark:group-focus:text-link-dark group-focus:text-opacity-100">Previous</span><span class="text-lg break-words group-hover:underline">Removing Effect Dependencies</span></div></a><div></div></div></div></div></article><div class="self-stretch w-full"><div class="w-full px-5 pt-10 mx-auto sm:px-12 md:px-12 md:pt-12 lg:pt-10"><hr class="mx-auto max-w-7xl border-border dark:border-border-dark"/></div><div class="py-12 px-5 sm:px-12 md:px-12 sm:py-12 md:py-16 lg:py-14"><footer class="text-secondary dark:text-secondary-dark"><div class="grid grid-cols-2 md:grid-cols-3 xl:grid-cols-5 gap-x-12 gap-y-8 max-w-7xl mx-auto"><div class="col-span-2 md:col-span-1 justify-items-start mt-3.5"><a href="https://opensource.fb.com/" target="_blank" rel="noopener" aria-label="Meta Open Source"><div><svg width="160" height="19" viewBox="0 0 160 19" fill="none" xmlns="http://www.w3.org/2000/svg" class="text-primary dark:text-primary-dark"><path d="M22.0605 3.62598H24.3349L28.202 10.6212L32.0691 3.62598H34.2942V15.1206H32.4387V6.31077L29.0476 12.4111H27.307L23.9162 6.31077V15.1206H22.0605V3.62598Z" fill="currentColor"></path><path d="M40.2785 15.3259C39.4191 15.3259 38.6638 15.1357 38.0124 14.7554C37.367 14.3812 36.8394 13.8336 36.4895 13.1747C36.1253 12.5015 35.9433 11.7297 35.9434 10.8594C35.9434 9.97825 36.1213 9.19824 36.4771 8.5194C36.8329 7.84077 37.3269 7.30982 37.9592 6.92653C38.5913 6.54347 39.3179 6.3519 40.139 6.35181C40.9546 6.35181 41.6566 6.54477 42.2449 6.9307C42.8334 7.31658 43.2863 7.85713 43.6038 8.55232C43.9212 9.24748 44.08 10.063 44.0801 10.9989V11.5081H37.7826C37.8975 12.2088 38.1808 12.7602 38.6323 13.1625C39.0839 13.5648 39.6546 13.7659 40.3443 13.766C40.8971 13.766 41.3733 13.6839 41.7729 13.5196C42.1723 13.3554 42.5473 13.1063 42.8977 12.7724L43.8831 13.9794C42.9031 14.8771 41.7016 15.326 40.2785 15.3259ZM41.6334 8.50718C41.2447 8.11027 40.7356 7.91184 40.1062 7.91189C39.4931 7.91189 38.9799 8.11439 38.5667 8.51941C38.1533 8.92464 37.8919 9.46931 37.7826 10.1534H42.2984C42.2436 9.45273 42.0219 8.90398 41.6334 8.50716V8.50718Z" fill="currentColor"></path><path d="M46.3308 8.07609H44.623V6.55715H46.3308V4.04468H48.1209V6.55715H50.7153V8.07609H48.1209V11.9267C48.1209 12.5672 48.2303 13.0243 48.4492 13.298C48.6682 13.5717 49.0431 13.7086 49.5741 13.7084C49.7742 13.7102 49.9743 13.7006 50.1734 13.6797C50.3376 13.6606 50.5183 13.6346 50.7153 13.6017V15.1043C50.4905 15.1692 50.2614 15.2186 50.0297 15.252C49.7647 15.2911 49.4971 15.3103 49.2292 15.3095C47.2969 15.3095 46.3308 14.2531 46.3308 12.1403L46.3308 8.07609Z" fill="currentColor"></path><path d="M60.0415 15.1207H58.2844V13.9219C57.9815 14.3629 57.572 14.7202 57.094 14.9606C56.6123 15.204 56.0649 15.3258 55.4519 15.3259C54.6966 15.3259 54.0274 15.133 53.4444 14.7472C52.8614 14.3611 52.4029 13.8302 52.0692 13.1543C51.7353 12.4784 51.5684 11.7052 51.5684 10.8348C51.5684 9.95904 51.738 9.1845 52.0774 8.5112C52.4167 7.83795 52.8861 7.30972 53.4855 6.92653C54.0847 6.54347 54.773 6.3519 55.5503 6.35181C56.1361 6.35181 56.6616 6.46538 57.1269 6.69253C57.5858 6.91465 57.9833 7.24591 58.2844 7.65731V6.55718H60.0415V15.1207ZM58.2516 9.55395C58.06 9.06686 57.7576 8.68232 57.3444 8.40033C56.9311 8.11861 56.4535 7.97771 55.9116 7.97762C55.1452 7.97762 54.5349 8.23487 54.0807 8.74939C53.6264 9.2639 53.3993 9.95905 53.3993 10.8349C53.3993 11.7162 53.6182 12.4141 54.0561 12.9285C54.4939 13.443 55.0877 13.7003 55.8377 13.7003C56.3906 13.7003 56.8833 13.5579 57.3156 13.2733C57.7405 12.9979 58.068 12.5957 58.2516 12.1238V9.55395Z" fill="currentColor"></path><path d="M64.4113 11.7585C64.1266 11.0332 63.9843 10.2382 63.9844 9.3733C63.9844 8.50853 64.1267 7.71351 64.4113 6.98824C64.6823 6.28392 65.0929 5.6416 65.6182 5.09981C66.1399 4.56496 66.7659 4.14298 67.4573 3.86003C68.1634 3.56716 68.9379 3.4207 69.7808 3.42065C70.6238 3.42065 71.3984 3.56711 72.1045 3.86003C72.796 4.14302 73.422 4.56499 73.9437 5.09981C74.4689 5.64171 74.8795 6.284 75.1507 6.98824C75.4351 7.71351 75.5774 8.50853 75.5776 9.3733C75.5776 10.2382 75.4353 11.0333 75.1507 11.7585C74.8795 12.4627 74.4689 13.105 73.9437 13.6469C73.422 14.1818 72.796 14.6038 72.1045 14.8867C71.3984 15.1794 70.6239 15.3259 69.7808 15.3259C68.938 15.3259 68.1635 15.1795 67.4573 14.8867C66.7658 14.6038 66.1399 14.1818 65.6182 13.6469C65.0929 13.1051 64.6823 12.4628 64.4113 11.7585ZM73.6152 9.3733C73.6152 8.54697 73.451 7.81763 73.1226 7.18529C72.7942 6.55303 72.3413 6.05904 71.7637 5.70331C71.1862 5.34753 70.5252 5.16962 69.7808 5.16958C69.0365 5.16958 68.3756 5.34749 67.7981 5.70331C67.2205 6.05909 66.7676 6.55308 66.4392 7.18529C66.1108 7.81741 65.9466 8.54674 65.9466 9.3733C65.9466 10.1999 66.1108 10.9293 66.4392 11.5615C66.7677 12.1937 67.2206 12.6877 67.7981 13.0434C68.3755 13.3993 69.0364 13.5773 69.7808 13.5772C70.5252 13.5772 71.1862 13.3993 71.7637 13.0434C72.3413 12.6877 72.7942 12.1937 73.1226 11.5615C73.451 10.9292 73.6152 10.1998 73.6152 9.3733V9.3733Z" fill="currentColor"></path><path d="M77.2188 6.55718H78.9839V7.74763C79.2856 7.30796 79.6938 6.95201 80.1705 6.71309C80.6492 6.47237 81.1952 6.35194 81.8084 6.35181C82.5637 6.35181 83.2342 6.54477 83.8199 6.9307C84.4056 7.31658 84.8641 7.84615 85.1952 8.5194C85.5263 9.19264 85.6919 9.96719 85.6919 10.843C85.6919 11.7133 85.5223 12.4865 85.1829 13.1625C84.8436 13.8386 84.3742 14.3681 83.7747 14.7512C83.1755 15.1343 82.4873 15.3258 81.71 15.3259C81.1353 15.3259 80.618 15.2165 80.1581 14.9976C79.7045 14.7837 79.31 14.4624 79.0087 14.0616V18.569H77.2188V6.55718ZM79.9159 13.2774C80.3291 13.5594 80.8067 13.7004 81.3487 13.7003C82.1148 13.7003 82.7251 13.443 83.1796 12.9285C83.6339 12.414 83.861 11.7188 83.861 10.843C83.861 9.96172 83.6421 9.26383 83.2042 8.74937C82.7662 8.23486 82.1723 7.9776 81.4226 7.9776C80.8697 7.9776 80.377 8.11989 79.9447 8.40448C79.5197 8.67987 79.1923 9.08202 79.0087 9.55393V12.1238C79.2002 12.611 79.5026 12.9956 79.9159 13.2774Z" fill="currentColor"></path><path d="M91.177 15.3259C90.3176 15.3259 89.5622 15.1357 88.9109 14.7554C88.2654 14.3812 87.7377 13.8336 87.3878 13.1747C87.0236 12.5015 86.8417 11.7297 86.8418 10.8594C86.8418 9.97825 87.0197 9.19824 87.3754 8.5194C87.7312 7.84077 88.2252 7.30982 88.8574 6.92653C89.4896 6.54347 90.2163 6.3519 91.0373 6.35181C91.8528 6.35181 92.5548 6.54477 93.1434 6.9307C93.7317 7.31658 94.1846 7.85713 94.5022 8.55232C94.8196 9.24748 94.9782 10.063 94.9783 10.9989V11.5081H88.6809C88.7958 12.2088 89.0791 12.7602 89.5308 13.1625C89.9824 13.5648 90.553 13.7659 91.2426 13.766C91.7954 13.766 92.2716 13.6839 92.6712 13.5196C93.0708 13.3554 93.4457 13.1063 93.796 12.7724L94.7812 13.9794C93.8014 14.8771 92.6 15.326 91.177 15.3259ZM92.5315 8.50718C92.1428 8.11027 91.6338 7.91184 91.0044 7.91189C90.3914 7.91189 89.8782 8.11439 89.465 8.51941C89.0517 8.92464 88.7903 9.46931 88.6809 10.1534H93.1966C93.1419 9.45273 92.9202 8.90398 92.5315 8.50716V8.50718Z" fill="currentColor"></path><path d="M96.4883 6.55718H98.2536V7.80515C98.9158 6.83621 99.8381 6.35176 101.021 6.35181C102.039 6.35181 102.821 6.66928 103.369 7.30422C103.916 7.93929 104.19 8.84793 104.19 10.0301V15.1207H102.4V10.2436C102.4 9.44454 102.258 8.85615 101.973 8.47842C101.688 8.10074 101.242 7.9119 100.635 7.9119C100.104 7.9119 99.6356 8.04872 99.2307 8.32238C98.8255 8.59617 98.508 8.97932 98.2783 9.47183V15.1207H96.4883L96.4883 6.55718Z" fill="currentColor"></path><path d="M116.875 11.8694C116.875 12.9586 116.499 13.8071 115.746 14.4147C114.994 15.0222 113.914 15.3259 112.507 15.3259C111.451 15.3259 110.511 15.1262 109.687 14.7266C108.863 14.3272 108.221 13.7469 107.762 12.9859L109.157 11.8858C109.54 12.466 110.019 12.8971 110.594 13.1789C111.169 13.4609 111.829 13.6018 112.573 13.6018C113.323 13.6018 113.906 13.4594 114.322 13.1747C114.738 12.8902 114.946 12.5043 114.946 12.0171C114.946 11.5957 114.801 11.2468 114.511 10.9703C114.221 10.694 113.728 10.4846 113.033 10.3422L111.309 9.98094C109.196 9.54304 108.139 8.47567 108.139 6.77883C108.139 6.10558 108.315 5.51714 108.665 5.01352C109.015 4.51002 109.512 4.11867 110.155 3.83947C110.798 3.56031 111.552 3.4207 112.417 3.42065C114.338 3.42065 115.775 4.16509 116.727 5.65397L115.315 6.66391C114.976 6.14939 114.572 5.76759 114.104 5.51849C113.636 5.26943 113.068 5.14492 112.401 5.14497C111.662 5.14497 111.088 5.28041 110.681 5.55128C110.273 5.82225 110.069 6.20952 110.069 6.7131C110.069 7.09629 110.202 7.40557 110.467 7.64091C110.732 7.87626 111.196 8.0651 111.859 8.20744L113.583 8.56874C115.778 9.02855 116.875 10.1288 116.875 11.8694Z" fill="currentColor"></path><path d="M118.677 13.1831C118.308 12.5097 118.123 11.7297 118.123 10.843C118.123 9.95083 118.308 9.16809 118.677 8.4948C119.034 7.83483 119.569 7.28851 120.221 6.91833C120.88 6.54065 121.645 6.3518 122.516 6.35181C123.386 6.35181 124.151 6.54065 124.81 6.91833C125.463 7.28862 125.998 7.83491 126.354 8.4948C126.724 9.16805 126.908 9.95079 126.908 10.843C126.908 11.7297 126.724 12.5097 126.354 13.1831C125.998 13.8429 125.463 14.3892 124.81 14.7594C124.151 15.1371 123.386 15.3259 122.516 15.3259C121.651 15.3259 120.887 15.1371 120.225 14.7594C119.571 14.3902 119.034 13.8438 118.677 13.1831ZM125.077 10.843C125.077 9.98377 124.843 9.29408 124.375 8.77396C123.907 8.25393 123.288 7.99394 122.516 7.994C121.744 7.994 121.124 8.25398 120.656 8.77396C120.188 9.29399 119.954 9.98368 119.954 10.843C119.954 11.6969 120.188 12.3839 120.656 12.9039C121.124 13.4239 121.744 13.6839 122.516 13.6839C123.288 13.6839 123.907 13.4239 124.375 12.9039C124.843 12.3839 125.077 11.6969 125.077 10.843Z" fill="currentColor"></path><path d="M135.907 15.1206H134.141V13.8891C133.484 14.847 132.576 15.3259 131.415 15.3259C130.408 15.3259 129.635 15.0084 129.096 14.3735C128.557 13.7385 128.287 12.8299 128.287 11.6475V6.55713H130.077V11.4341C130.077 12.2278 130.217 12.8148 130.496 13.1953C130.775 13.5758 131.213 13.766 131.809 13.7659C132.324 13.7659 132.781 13.6305 133.18 13.3595C133.58 13.0885 133.892 12.7095 134.116 12.2223V6.55713H135.907L135.907 15.1206Z" fill="currentColor"></path><path d="M137.877 6.55733H139.642V7.8709C140.195 6.91311 140.95 6.43417 141.908 6.43408C142.22 6.43408 142.475 6.46146 142.672 6.5162V8.16659C142.403 8.12858 142.131 8.10936 141.859 8.10907C140.786 8.10907 140.055 8.59074 139.667 9.55408V15.1208H137.877L137.877 6.55733Z" fill="currentColor"></path><path d="M143.737 8.49063C144.079 7.83229 144.601 7.28535 145.244 6.9143C145.889 6.53944 146.645 6.35194 147.51 6.35181C149.119 6.35181 150.318 6.98952 151.106 8.26496L149.71 9.20918C149.431 8.78768 149.119 8.47975 148.774 8.2854C148.429 8.09118 148.013 7.99405 147.526 7.994C146.782 7.994 146.18 8.25534 145.72 8.778C145.26 9.30085 145.03 9.98647 145.03 10.8348C145.03 11.727 145.255 12.4249 145.703 12.9285C146.152 13.4321 146.785 13.6839 147.6 13.6839C148.034 13.6874 148.463 13.5845 148.848 13.3841C149.218 13.1962 149.536 12.9215 149.776 12.5836L151.024 13.6839C150.186 14.7786 149.031 15.326 147.559 15.3259C146.678 15.3259 145.91 15.1412 145.256 14.7718C144.609 14.4099 144.081 13.8679 143.737 13.2117C143.378 12.5413 143.199 11.7544 143.199 10.8512C143.199 9.95352 143.378 9.16666 143.737 8.49063Z" fill="currentColor"></path><path d="M156.195 15.3259C155.335 15.3259 154.58 15.1357 153.928 14.7554C153.283 14.3812 152.755 13.8336 152.405 13.1747C152.041 12.5015 151.859 11.7297 151.859 10.8594C151.859 9.97825 152.037 9.19824 152.393 8.5194C152.749 7.84077 153.243 7.30982 153.875 6.92653C154.507 6.54347 155.234 6.3519 156.055 6.35181C156.87 6.35181 157.572 6.54477 158.161 6.9307C158.749 7.31658 159.202 7.85713 159.52 8.55232C159.837 9.24748 159.996 10.063 159.996 10.9989V11.5081H153.698C153.814 12.2088 154.097 12.7602 154.548 13.1625C155 13.5648 155.571 13.7659 156.26 13.766C156.813 13.766 157.289 13.6839 157.689 13.5196C158.088 13.3554 158.463 13.1063 158.814 12.7724L159.799 13.9794C158.819 14.8771 157.618 15.326 156.195 15.3259ZM157.549 8.50718C157.161 8.11027 156.651 7.91184 156.022 7.91189C155.409 7.91189 154.896 8.11439 154.483 8.51941C154.069 8.92464 153.808 9.46931 153.698 10.1534H158.214C158.159 9.45273 157.938 8.90398 157.549 8.50716V8.50718Z" fill="currentColor"></path><path d="M5.26022 3.23511C5.25436 3.23511 5.24854 3.23513 5.24268 3.23516L5.21875 5.21191C5.22423 5.21185 5.22969 5.2118 5.23518 5.2118C6.53629 5.2118 7.54551 6.23768 9.73906 9.93252L9.87278 10.1575L9.88153 10.1722L11.1094 8.32979L11.1009 8.31556C10.812 7.84556 10.5344 7.41312 10.2681 7.01826C9.95934 6.56075 9.66404 6.15204 9.37746 5.78713C7.92635 3.93952 6.71249 3.23511 5.26022 3.23511Z" fill="url(#paint0_linear_627_396207)"></path><path d="M5.24198 3.23516C3.78266 3.24267 2.49251 4.18633 1.56092 5.63032C1.55819 5.63455 1.55546 5.63879 1.55273 5.64302L3.26279 6.57377C3.26556 6.56957 3.26836 6.56535 3.27114 6.56117C3.81514 5.7421 4.49212 5.21969 5.21805 5.21191C5.22353 5.21185 5.229 5.21181 5.23448 5.21181L5.2595 3.23511C5.25364 3.23511 5.24783 3.23513 5.24198 3.23516Z" fill="url(#paint1_linear_627_396207)"></path><path d="M1.56088 5.63037C1.55816 5.6346 1.55543 5.63884 1.5527 5.64307C0.94054 6.596 0.484192 7.76537 0.237567 9.02689C0.236499 9.03235 0.235435 9.03781 0.234375 9.04329L2.15555 9.49659C2.15655 9.49111 2.15756 9.48562 2.15857 9.48015C2.36393 8.37149 2.75488 7.34323 3.26274 6.57382C3.26552 6.56962 3.26831 6.5654 3.2711 6.56122L1.56088 5.63037Z" fill="url(#paint2_linear_627_396207)"></path><path d="M2.15979 9.48011L0.238778 9.02686C0.23771 9.03231 0.236646 9.03778 0.235585 9.04325C0.101104 9.73704 0.0326863 10.442 0.03125 11.1487C0.03125 11.1544 0.03125 11.1601 0.03125 11.1658L2.00149 11.3421C2.00133 11.3364 2.00117 11.3307 2.00103 11.3249C2.00007 11.284 1.99958 11.2424 1.99956 11.2003C2.00054 10.6288 2.05316 10.0586 2.15678 9.49655C2.15776 9.49107 2.15878 9.48558 2.15979 9.48011Z" fill="url(#paint3_linear_627_396207)"></path><path d="M2.06148 11.9568C2.02614 11.7537 2.00611 11.5482 2.00156 11.3421C2.0014 11.3363 2.00124 11.3307 2.0011 11.3249L0.031335 11.1487C0.031335 11.1544 0.031335 11.1601 0.031335 11.1658V11.1669C0.0292535 11.5801 0.0653944 11.9925 0.139296 12.399C0.140327 12.4045 0.14134 12.4099 0.142386 12.4154L2.06448 11.9732C2.06345 11.9678 2.06247 11.9623 2.06148 11.9568Z" fill="url(#paint4_linear_627_396207)"></path><path d="M2.50976 12.9765C2.29536 12.7425 2.14362 12.405 2.06386 11.9732C2.06285 11.9678 2.06187 11.9623 2.06088 11.9568L0.138672 12.399C0.139703 12.4045 0.140716 12.4099 0.141762 12.4154C0.28705 13.1782 0.571996 13.8139 0.980035 14.2949C0.983663 14.2991 0.987305 14.3034 0.990959 14.3077L2.52121 12.9888C2.51738 12.9848 2.51355 12.9807 2.50976 12.9765Z" fill="url(#paint5_linear_627_396207)"></path><path d="M8.20487 7.50854C7.04655 9.28523 6.34486 10.3996 6.34486 10.3996C4.80187 12.8183 4.26806 13.3604 3.409 13.3604C3.05054 13.3604 2.75107 13.2328 2.52164 12.9888C2.51782 12.9848 2.51398 12.9807 2.51019 12.9765L0.980469 14.2949C0.984097 14.2991 0.987738 14.3034 0.991392 14.3077C1.5548 14.9644 2.35009 15.3288 3.33393 15.3288C4.82242 15.3288 5.89296 14.6271 7.79608 11.3004C7.79608 11.3004 8.58943 9.8994 9.1352 8.93436C8.79713 8.38854 8.48948 7.91597 8.20487 7.50854Z" fill="#0082FB"></path><path d="M10.2688 4.7041C10.2649 4.70825 10.261 4.71248 10.2571 4.71664C9.94322 5.05596 9.64935 5.41323 9.37695 5.78663C9.66354 6.15154 9.95939 6.56105 10.2682 7.01855C10.6321 6.45684 10.9718 6.00189 11.3048 5.6532C11.3087 5.64907 11.3126 5.64504 11.3166 5.64094L10.2688 4.7041Z" fill="url(#paint6_linear_627_396207)"></path><path d="M15.8912 4.53007C15.0834 3.71396 14.1202 3.23511 13.0905 3.23511C12.0047 3.23511 11.0914 3.83012 10.2677 4.70423C10.2637 4.70837 10.2598 4.71261 10.2559 4.71677L11.3036 5.65333C11.3075 5.6492 11.3114 5.64517 11.3154 5.64107C11.858 5.0766 12.3832 4.79478 12.9654 4.79478H12.9654C13.592 4.79478 14.1786 5.08975 14.6867 5.60687C14.6906 5.61092 14.6946 5.61494 14.6986 5.61902L15.9032 4.54221C15.8992 4.53815 15.8952 4.53412 15.8912 4.53007Z" fill="#0082FB"></path><path d="M18.2273 10.8885C18.1821 8.26813 17.2651 5.92556 15.904 4.54218C15.9 4.53811 15.896 4.53408 15.892 4.53003L14.6875 5.60684C14.6915 5.61089 14.6954 5.61491 14.6994 5.61899C15.7233 6.67077 16.4256 8.6271 16.4895 10.8879C16.4897 10.8936 16.4898 10.8993 16.49 10.905L18.2276 10.9056C18.2275 10.8999 18.2274 10.8942 18.2273 10.8885Z" fill="url(#paint7_linear_627_396207)"></path><path d="M18.2262 10.9056C18.2261 10.8999 18.226 10.8942 18.2259 10.8885L16.4881 10.8879C16.4883 10.8936 16.4884 10.8993 16.4886 10.905C16.4914 11.0111 16.4928 11.1179 16.4928 11.2253C16.4928 11.8417 16.4007 12.34 16.2135 12.6997C16.2107 12.705 16.2079 12.7104 16.2051 12.7157L17.5007 14.0632C17.504 14.0583 17.5071 14.0535 17.5103 14.0486C17.9807 13.3228 18.2276 12.3145 18.2276 11.0918C18.2276 11.0296 18.2272 10.9675 18.2262 10.9056Z" fill="url(#paint8_linear_627_396207)"></path><path d="M16.2158 12.6997C16.213 12.705 16.2102 12.7104 16.2074 12.7157C16.0453 13.0189 15.814 13.2212 15.5117 13.3096L16.1024 15.1711C16.1806 15.1445 16.2567 15.1147 16.3308 15.0816C16.353 15.0718 16.3749 15.0616 16.3967 15.0512C16.4092 15.0452 16.4217 15.0391 16.4341 15.0329C16.8281 14.8341 17.1672 14.5417 17.4217 14.1812C17.438 14.1587 17.4541 14.1359 17.47 14.1127C17.4811 14.0963 17.4921 14.0798 17.5031 14.0632C17.5063 14.0583 17.5094 14.0534 17.5126 14.0485L16.2158 12.6997Z" fill="url(#paint9_linear_627_396207)"></path><path d="M15.1349 13.3603C14.9481 13.3648 14.7626 13.3286 14.5911 13.2544L13.9863 15.1602C14.3262 15.2763 14.6889 15.3287 15.0932 15.3287C15.4415 15.3319 15.7878 15.2768 16.1179 15.1654L15.5273 13.3046C15.4001 13.3427 15.2678 13.3615 15.1349 13.3603Z" fill="url(#paint10_linear_627_396207)"></path><path d="M13.9243 12.7085C13.9206 12.7042 13.9168 12.6999 13.9131 12.6956L12.5215 14.1429C12.5254 14.147 12.5293 14.1512 12.5332 14.1553C13.0167 14.6706 13.4784 14.9903 14.0021 15.1657L14.6064 13.2613C14.3857 13.1665 14.1723 12.9947 13.9243 12.7085Z" fill="url(#paint11_linear_627_396207)"></path><path d="M13.9142 12.6956C13.4968 12.2101 12.9804 11.4019 12.1682 10.095L11.1097 8.32966L11.1012 8.31543L9.87305 10.1573L9.8818 10.172L10.6318 11.4337C11.3588 12.6503 11.9511 13.5304 12.5226 14.1428C12.5265 14.147 12.5304 14.1512 12.5343 14.1553L13.9254 12.7085C13.9217 12.7042 13.918 12.6999 13.9142 12.6956Z" fill="url(#paint12_linear_627_396207)"></path><defs><linearGradient id="paint0_linear_627_396207" x1="10.2933" y1="9.42293" x2="6.21654" y2="4.08099" gradientUnits="userSpaceOnUse"><stop offset="0.0006" stop-color="#0867DF"></stop><stop offset="0.4539" stop-color="#0668E1"></stop><stop offset="0.8591" stop-color="#0064E0"></stop></linearGradient><linearGradient id="paint1_linear_627_396207" x1="2.35598" y1="5.96246" x2="5.15084" y2="3.84063" gradientUnits="userSpaceOnUse"><stop offset="0.1323" stop-color="#0064DF"></stop><stop offset="0.9988" stop-color="#0064E0"></stop></linearGradient><linearGradient id="paint2_linear_627_396207" x1="1.17132" y1="9.07623" x2="2.29244" y2="6.25404" gradientUnits="userSpaceOnUse"><stop offset="0.0147" stop-color="#0072EC"></stop><stop offset="0.6881" stop-color="#0064DF"></stop></linearGradient><linearGradient id="paint3_linear_627_396207" x1="1.02028" y1="11.115" x2="1.15" y2="9.39138" gradientUnits="userSpaceOnUse"><stop offset="0.0731" stop-color="#007CF6"></stop><stop offset="0.9943" stop-color="#0072EC"></stop></linearGradient><linearGradient id="paint4_linear_627_396207" x1="1.0917" y1="12.0512" x2="0.998912" y2="11.3606" gradientUnits="userSpaceOnUse"><stop offset="0.0731" stop-color="#007FF9"></stop><stop offset="1" stop-color="#007CF6"></stop></linearGradient><linearGradient id="paint5_linear_627_396207" x1="1.03663" y1="12.2326" x2="1.61491" y2="13.4591" gradientUnits="userSpaceOnUse"><stop offset="0.0731" stop-color="#007FF9"></stop><stop offset="1" stop-color="#0082FB"></stop></linearGradient><linearGradient id="paint6_linear_627_396207" x1="9.92449" y1="6.29781" x2="10.689" y2="5.24046" gradientUnits="userSpaceOnUse"><stop offset="0.2799" stop-color="#007FF8"></stop><stop offset="0.9141" stop-color="#0082FB"></stop></linearGradient><linearGradient id="paint7_linear_627_396207" x1="15.7367" y1="4.92752" x2="17.3361" y2="10.8108" gradientUnits="userSpaceOnUse"><stop stop-color="#0082FB"></stop><stop offset="0.9995" stop-color="#0081FA"></stop></linearGradient><linearGradient id="paint8_linear_627_396207" x1="17.7208" y1="11.0359" x2="16.7086" y2="13.0813" gradientUnits="userSpaceOnUse"><stop offset="0.0619" stop-color="#0081FA"></stop><stop offset="1" stop-color="#0080F9"></stop></linearGradient><linearGradient id="paint9_linear_627_396207" x1="15.9065" y1="14.1657" x2="16.8526" y2="13.5213" gradientUnits="userSpaceOnUse"><stop stop-color="#027AF3"></stop><stop offset="1" stop-color="#0080F9"></stop></linearGradient><linearGradient id="paint10_linear_627_396207" x1="14.4218" y1="14.2915" x2="15.7366" y2="14.2915" gradientUnits="userSpaceOnUse"><stop stop-color="#0377EF"></stop><stop offset="0.9994" stop-color="#0279F1"></stop></linearGradient><linearGradient id="paint11_linear_627_396207" x1="13.2783" y1="13.5675" x2="14.2235" y2="14.1236" gradientUnits="userSpaceOnUse"><stop offset="0.0019" stop-color="#0471E9"></stop><stop offset="1" stop-color="#0377EF"></stop></linearGradient><linearGradient id="paint12_linear_627_396207" x1="10.3961" y1="9.46696" x2="13.424" y2="13.274" gradientUnits="userSpaceOnUse"><stop offset="0.2765" stop-color="#0867DF"></stop><stop offset="1" stop-color="#0471E9"></stop></linearGradient></defs></svg></div></a><div class="text-xs text-left rtl:text-right mt-2 pe-0.5" dir="ltr">Copyright © Meta Platforms, Inc</div><div class="uwu-visible text-xs cursor-pointer hover:text-link hover:dark:text-link-dark hover:underline">no uwu plz</div><div class="uwu-hidden text-xs cursor-pointer hover:text-link hover:dark:text-link-dark hover:underline">uwu?</div><div class="uwu-visible text-xs">Logo by<a href="https://twitter.com/sawaratsuki1004" target="_blank" rel="noopener" class="ms-1">@sawaratsuki1004</a></div></div><div class="flex flex-col"><div><a class="border-b inline-block border-transparent text-md text-secondary dark:text-secondary-dark my-2 font-bold hover:border-gray-10" href="/learn">Learn React</a></div><div><a class="border-b inline-block border-transparent text-sm text-primary dark:text-primary-dark hover:border-gray-10" href="/learn">Quick Start</a></div><div><a class="border-b inline-block border-transparent text-sm text-primary dark:text-primary-dark hover:border-gray-10" href="/learn/installation">Installation</a></div><div><a class="border-b inline-block border-transparent text-sm text-primary dark:text-primary-dark hover:border-gray-10" href="/learn/describing-the-ui">Describing the UI</a></div><div><a class="border-b inline-block border-transparent text-sm text-primary dark:text-primary-dark hover:border-gray-10" href="/learn/adding-interactivity">Adding Interactivity</a></div><div><a class="border-b inline-block border-transparent text-sm text-primary dark:text-primary-dark hover:border-gray-10" href="/learn/managing-state">Managing State</a></div><div><a class="border-b inline-block border-transparent text-sm text-primary dark:text-primary-dark hover:border-gray-10" href="/learn/escape-hatches">Escape Hatches</a></div></div><div class="flex flex-col"><div><a class="border-b inline-block border-transparent text-md text-secondary dark:text-secondary-dark my-2 font-bold hover:border-gray-10" href="/reference/react">API Reference</a></div><div><a class="border-b inline-block border-transparent text-sm text-primary dark:text-primary-dark hover:border-gray-10" href="/reference/react">React APIs</a></div><div><a class="border-b inline-block border-transparent text-sm text-primary dark:text-primary-dark hover:border-gray-10" href="/reference/react-dom">React DOM APIs</a></div></div><div class="md:col-start-2 xl:col-start-4 flex flex-col"><div><a class="border-b inline-block border-transparent text-md text-secondary dark:text-secondary-dark my-2 font-bold hover:border-gray-10" href="/community">Community</a></div><div><a href="https://github.com/facebook/react/blob/main/CODE_OF_CONDUCT.md" target="_blank" rel="noopener" class="border-b inline-block border-transparent text-sm text-primary dark:text-primary-dark hover:border-gray-10">Code of Conduct</a></div><div><a class="border-b inline-block border-transparent text-sm text-primary dark:text-primary-dark hover:border-gray-10" href="/community/team">Meet the Team</a></div><div><a class="border-b inline-block border-transparent text-sm text-primary dark:text-primary-dark hover:border-gray-10" href="/community/docs-contributors">Docs Contributors</a></div><div><a class="border-b inline-block border-transparent text-sm text-primary dark:text-primary-dark hover:border-gray-10" href="/community/acknowledgements">Acknowledgements</a></div></div><div class="flex flex-col"><div class="border-b inline-block border-transparent text-md text-secondary dark:text-secondary-dark my-2 font-bold">More</div><div><a class="border-b inline-block border-transparent text-sm text-primary dark:text-primary-dark hover:border-gray-10" href="/blog">Blog</a></div><div><a href="https://reactnative.dev/" target="_blank" rel="noopener" class="border-b inline-block border-transparent text-sm text-primary dark:text-primary-dark hover:border-gray-10">React Native</a></div><div><a href="https://opensource.facebook.com/legal/privacy" target="_blank" rel="noopener" class="border-b inline-block border-transparent text-sm text-primary dark:text-primary-dark hover:border-gray-10">Privacy</a></div><div><a href="https://opensource.fb.com/legal/terms/" target="_blank" rel="noopener" class="border-b inline-block border-transparent text-sm text-primary dark:text-primary-dark hover:border-gray-10">Terms</a></div><div class="flex flex-row items-center mt-8 gap-x-2"><a href="https://www.facebook.com/react" target="_blank" rel="noopener" aria-label="React on Facebook" class="hover:text-primary dark:text-primary-dark"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="1.33em" height="1.33em" fill="currentColor"><path fill="none" d="M0 0h24v24H0z"></path><path d="M12 2C6.477 2 2 6.477 2 12c0 4.991 3.657 9.128 8.438 9.879V14.89h-2.54V12h2.54V9.797c0-2.506 1.492-3.89 3.777-3.89 1.094 0 2.238.195 2.238.195v2.46h-1.26c-1.243 0-1.63.771-1.63 1.562V12h2.773l-.443 2.89h-2.33v6.989C18.343 21.129 22 16.99 22 12c0-5.523-4.477-10-10-10z"></path></svg></a><a href="https://twitter.com/reactjs" target="_blank" rel="noopener" aria-label="React on Twitter" class="hover:text-primary dark:text-primary-dark"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" height="1.30em" width="1.30em" fill="currentColor"><path fill="none" d="M0 0h24v24H0z"></path><path d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z"></path></svg></a><a href="https://bsky.app/profile/react.dev" target="_blank" rel="noopener" aria-label="React on Bluesky" class="hover:text-primary dark:text-primary-dark"><svg aria-label="Bluesky" viewBox="0 0 16 16" height="1.25em" width="1.25em" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path class="x19hqcy" d="M3.468 1.948C5.303 3.325 7.276 6.118 8 7.616c.725-1.498 2.697-4.29 4.532-5.668C13.855.955 16 .186 16 2.632c0 .489-.28 4.105-.444 4.692-.572 2.04-2.653 2.561-4.504 2.246 3.236.551 4.06 2.375 2.281 4.2-3.376 3.464-4.852-.87-5.23-1.98-.07-.204-.103-.3-.103-.218 0-.081-.033.014-.102.218-.379 1.11-1.855 5.444-5.231 1.98-1.778-1.825-.955-3.65 2.28-4.2-1.85.315-3.932-.205-4.503-2.246C.28 6.737 0 3.12 0 2.632 0 .186 2.145.955 3.468 1.948Z"></path></svg></a><a href="https://github.com/facebook/react" target="_blank" rel="noopener" aria-label="React on Github" class="hover:text-primary dark:text-primary-dark"><svg xmlns="http://www.w3.org/2000/svg" width="1.5em" height="1.5em" viewBox="0 -2 24 24" fill="currentColor"><path d="M10 0a10 10 0 0 0-3.16 19.49c.5.1.68-.22.68-.48l-.01-1.7c-2.78.6-3.37-1.34-3.37-1.34-.46-1.16-1.11-1.47-1.11-1.47-.9-.62.07-.6.07-.6 1 .07 1.53 1.03 1.53 1.03.9 1.52 2.34 1.08 2.91.83.1-.65.35-1.09.63-1.34-2.22-.25-4.55-1.11-4.55-4.94 0-1.1.39-1.99 1.03-2.69a3.6 3.6 0 0 1 .1-2.64s.84-.27 2.75 1.02a9.58 9.58 0 0 1 5 0c1.91-1.3 2.75-1.02 2.75-1.02.55 1.37.2 2.4.1 2.64.64.7 1.03 1.6 1.03 2.69 0 3.84-2.34 4.68-4.57 4.93.36.31.68.92.68 1.85l-.01 2.75c0 .26.18.58.69.48A10 10 0 0 0 10 0"></path></svg></a></div></div></div></footer></div></div></main><!--/$--><div class="hidden -mt-16 lg:max-w-custom-xs 2xl:block"><nav role="navigation" class="pt-20 sticky top-0 end-0"><h2 class="mb-3 lg:mb-3 uppercase tracking-wide font-bold text-sm text-secondary dark:text-secondary-dark px-4 w-full">On this page</h2><div class="h-full overflow-y-auto ps-4 max-h-[calc(100vh-7.5rem)]" style="overscroll-behavior:contain"><ul class="space-y-2 pb-16"><li class="text-sm px-2 rounded-s-xl bg-highlight dark:bg-highlight-dark"><a class="text-link dark:text-link-dark font-bold block hover:text-link dark:hover:text-link-dark leading-normal py-2" href="#">Overview</a></li><li class="text-sm px-2 rounded-s-xl"><a class="text-secondary dark:text-secondary-dark block hover:text-link dark:hover:text-link-dark leading-normal py-2" href="#custom-hooks-sharing-logic-between-components">Custom Hooks: Sharing logic between components </a></li><li class="text-sm px-2 rounded-s-xl ps-4"><a class="text-secondary dark:text-secondary-dark block hover:text-link dark:hover:text-link-dark leading-normal py-2" href="#extracting-your-own-custom-hook-from-a-component">Extracting your own custom Hook from a component </a></li><li class="text-sm px-2 rounded-s-xl ps-4"><a class="text-secondary dark:text-secondary-dark block hover:text-link dark:hover:text-link-dark leading-normal py-2" href="#hook-names-always-start-with-use">Hook names always start with <code dir="ltr" class="inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline bg-gray-30 bg-opacity-10 py-px">use</code> </a></li><li class="text-sm px-2 rounded-s-xl ps-4"><a class="text-secondary dark:text-secondary-dark block hover:text-link dark:hover:text-link-dark leading-normal py-2" href="#custom-hooks-let-you-share-stateful-logic-not-state-itself">Custom Hooks let you share stateful logic, not state itself </a></li><li class="text-sm px-2 rounded-s-xl"><a class="text-secondary dark:text-secondary-dark block hover:text-link dark:hover:text-link-dark leading-normal py-2" href="#passing-reactive-values-between-hooks">Passing reactive values between Hooks </a></li><li class="text-sm px-2 rounded-s-xl ps-4"><a class="text-secondary dark:text-secondary-dark block hover:text-link dark:hover:text-link-dark leading-normal py-2" href="#passing-event-handlers-to-custom-hooks">Passing event handlers to custom Hooks </a></li><li class="text-sm px-2 rounded-s-xl"><a class="text-secondary dark:text-secondary-dark block hover:text-link dark:hover:text-link-dark leading-normal py-2" href="#when-to-use-custom-hooks">When to use custom Hooks </a></li><li class="text-sm px-2 rounded-s-xl ps-4"><a class="text-secondary dark:text-secondary-dark block hover:text-link dark:hover:text-link-dark leading-normal py-2" href="#custom-hooks-help-you-migrate-to-better-patterns">Custom Hooks help you migrate to better patterns </a></li><li class="text-sm px-2 rounded-s-xl ps-4"><a class="text-secondary dark:text-secondary-dark block hover:text-link dark:hover:text-link-dark leading-normal py-2" href="#there-is-more-than-one-way-to-do-it">There is more than one way to do it </a></li><li class="text-sm px-2 rounded-s-xl"><a class="text-secondary dark:text-secondary-dark block hover:text-link dark:hover:text-link-dark leading-normal py-2" href="#recap">Recap</a></li><li class="text-sm px-2 rounded-s-xl"><a class="text-secondary dark:text-secondary-dark block hover:text-link dark:hover:text-link-dark leading-normal py-2" href="#challenges">Challenges</a></li></ul></div></nav></div></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"toc":"[{\"url\":\"#\",\"text\":\"Overview\",\"depth\":2},{\"url\":\"#custom-hooks-sharing-logic-between-components\",\"depth\":2,\"text\":\"Custom Hooks: Sharing logic between components \"},{\"url\":\"#extracting-your-own-custom-hook-from-a-component\",\"depth\":3,\"text\":\"Extracting your own custom Hook from a component \"},{\"url\":\"#hook-names-always-start-with-use\",\"depth\":3,\"text\":[\"Hook names always start with \",[\"$r\",\"code\",null,{\"children\":\"use\"}],\" \"]},{\"url\":\"#custom-hooks-let-you-share-stateful-logic-not-state-itself\",\"depth\":3,\"text\":\"Custom Hooks let you share stateful logic, not state itself \"},{\"url\":\"#passing-reactive-values-between-hooks\",\"depth\":2,\"text\":\"Passing reactive values between Hooks \"},{\"url\":\"#passing-event-handlers-to-custom-hooks\",\"depth\":3,\"text\":\"Passing event handlers to custom Hooks \"},{\"url\":\"#when-to-use-custom-hooks\",\"depth\":2,\"text\":\"When to use custom Hooks \"},{\"url\":\"#custom-hooks-help-you-migrate-to-better-patterns\",\"depth\":3,\"text\":\"Custom Hooks help you migrate to better patterns \"},{\"url\":\"#there-is-more-than-one-way-to-do-it\",\"depth\":3,\"text\":\"There is more than one way to do it \"},{\"url\":\"#recap\",\"depth\":2,\"text\":\"Recap\"},{\"url\":\"#challenges\",\"depth\":2,\"text\":\"Challenges\"}]","content":"[[\"$r\",\"MaxWidth\",\"12\",{\"children\":[[\"$r\",\"Intro\",null,{\"children\":[\"$r\",\"p\",null,{\"children\":[\"React comes with several built-in Hooks like \",[\"$r\",\"code\",null,{\"children\":\"useState\"}],\", \",[\"$r\",\"code\",null,{\"children\":\"useContext\"}],\", and \",[\"$r\",\"code\",null,{\"children\":\"useEffect\"}],\". Sometimes, you’ll wish that there was a Hook for some more specific purpose: for example, to fetch data, to keep track of whether the user is online, or to connect to a chat room. You might not find these Hooks in React, but you can create your own Hooks for your application’s needs.\"]}]}],\"\\n\",[\"$r\",\"YouWillLearn\",null,{\"children\":[\"$r\",\"ul\",null,{\"children\":[\"\\n\",[\"$r\",\"li\",null,{\"children\":\"What custom Hooks are, and how to write your own\"}],\"\\n\",[\"$r\",\"li\",null,{\"children\":\"How to reuse logic between components\"}],\"\\n\",[\"$r\",\"li\",null,{\"children\":\"How to name and structure your custom Hooks\"}],\"\\n\",[\"$r\",\"li\",null,{\"children\":\"When and why to extract custom Hooks\"}],\"\\n\"]}]}],\"\\n\",[\"$r\",\"h2\",null,{\"id\":\"custom-hooks-sharing-logic-between-components\",\"children\":\"Custom Hooks: Sharing logic between components \"}],\"\\n\",[\"$r\",\"p\",null,{\"children\":\"Imagine you’re developing an app that heavily relies on the network (as most apps do). You want to warn the user if their network connection has accidentally gone off while they were using your app. How would you go about it? It seems like you’ll need two things in your component:\"}],\"\\n\",[\"$r\",\"ol\",null,{\"children\":[\"\\n\",[\"$r\",\"li\",null,{\"children\":\"A piece of state that tracks whether the network is online.\"}],\"\\n\",[\"$r\",\"li\",null,{\"children\":[\"An Effect that subscribes to the global \",[\"$r\",\"a\",null,{\"href\":\"https://developer.mozilla.org/en-US/docs/Web/API/Window/online_event\",\"target\":\"_blank\",\"rel\":\"nofollow noopener noreferrer\",\"children\":[\"$r\",\"code\",null,{\"children\":\"online\"}]}],\" and \",[\"$r\",\"a\",null,{\"href\":\"https://developer.mozilla.org/en-US/docs/Web/API/Window/offline_event\",\"target\":\"_blank\",\"rel\":\"nofollow noopener noreferrer\",\"children\":[\"$r\",\"code\",null,{\"children\":\"offline\"}]}],\" events, and updates that state.\"]}],\"\\n\"]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"This will keep your component \",[\"$r\",\"a\",null,{\"href\":\"/learn/synchronizing-with-effects\",\"children\":\"synchronized\"}],\" with the network status. You might start with something like this:\"]}],\"\\n\"]}],[\"$r\",\"Sandpack\",null,{\"children\":[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"import { useState, useEffect } from 'react';\\n\\nexport default function StatusBar() {\\n const [isOnline, setIsOnline] = useState(true);\\n useEffect(() =\u003e {\\n function handleOnline() {\\n setIsOnline(true);\\n }\\n function handleOffline() {\\n setIsOnline(false);\\n }\\n window.addEventListener('online', handleOnline);\\n window.addEventListener('offline', handleOffline);\\n return () =\u003e {\\n window.removeEventListener('online', handleOnline);\\n window.removeEventListener('offline', handleOffline);\\n };\\n }, []);\\n\\n return \u003ch1\u003e{isOnline ? '✅ Online' : '❌ Disconnected'}\u003c/h1\u003e;\\n}\\n\"}]}]}],[\"$r\",\"MaxWidth\",\"20\",{\"children\":[\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"Try turning your network on and off, and notice how this \",[\"$r\",\"code\",null,{\"children\":\"StatusBar\"}],\" updates in response to your actions.\"]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"Now imagine you \",[\"$r\",\"em\",null,{\"children\":\"also\"}],\" want to use the same logic in a different component. You want to implement a Save button that will become disabled and show “Reconnecting…” instead of “Save” while the network is off.\"]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"To start, you can copy and paste the \",[\"$r\",\"code\",null,{\"children\":\"isOnline\"}],\" state and the Effect into \",[\"$r\",\"code\",null,{\"children\":\"SaveButton\"}],\":\"]}],\"\\n\"]}],[\"$r\",\"Sandpack\",null,{\"children\":[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"import { useState, useEffect } from 'react';\\n\\nexport default function SaveButton() {\\n const [isOnline, setIsOnline] = useState(true);\\n useEffect(() =\u003e {\\n function handleOnline() {\\n setIsOnline(true);\\n }\\n function handleOffline() {\\n setIsOnline(false);\\n }\\n window.addEventListener('online', handleOnline);\\n window.addEventListener('offline', handleOffline);\\n return () =\u003e {\\n window.removeEventListener('online', handleOnline);\\n window.removeEventListener('offline', handleOffline);\\n };\\n }, []);\\n\\n function handleSaveClick() {\\n console.log('✅ Progress saved');\\n }\\n\\n return (\\n \u003cbutton disabled={!isOnline} onClick={handleSaveClick}\u003e\\n {isOnline ? 'Save progress' : 'Reconnecting...'}\\n \u003c/button\u003e\\n );\\n}\\n\"}]}]}],[\"$r\",\"MaxWidth\",\"38\",{\"children\":[\"\\n\",[\"$r\",\"p\",null,{\"children\":\"Verify that, if you turn off the network, the button will change its appearance.\"}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"These two components work fine, but the duplication in logic between them is unfortunate. It seems like even though they have different \",[\"$r\",\"em\",null,{\"children\":\"visual appearance,\"}],\" you want to reuse the logic between them.\"]}],\"\\n\",[\"$r\",\"h3\",null,{\"id\":\"extracting-your-own-custom-hook-from-a-component\",\"children\":\"Extracting your own custom Hook from a component \"}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"Imagine for a moment that, similar to \",[\"$r\",\"a\",null,{\"href\":\"/reference/react/useState\",\"children\":[\"$r\",\"code\",null,{\"children\":\"useState\"}]}],\" and \",[\"$r\",\"a\",null,{\"href\":\"/reference/react/useEffect\",\"children\":[\"$r\",\"code\",null,{\"children\":\"useEffect\"}]}],\", there was a built-in \",[\"$r\",\"code\",null,{\"children\":\"useOnlineStatus\"}],\" Hook. Then both of these components could be simplified and you could remove the duplication between them:\"]}],\"\\n\",[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"{2,7}\",\"children\":\"function StatusBar() {\\n const isOnline = useOnlineStatus();\\n return \u003ch1\u003e{isOnline ? '✅ Online' : '❌ Disconnected'}\u003c/h1\u003e;\\n}\\n\\nfunction SaveButton() {\\n const isOnline = useOnlineStatus();\\n\\n function handleSaveClick() {\\n console.log('✅ Progress saved');\\n }\\n\\n return (\\n \u003cbutton disabled={!isOnline} onClick={handleSaveClick}\u003e\\n {isOnline ? 'Save progress' : 'Reconnecting...'}\\n \u003c/button\u003e\\n );\\n}\\n\"}]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"Although there is no such built-in Hook, you can write it yourself. Declare a function called \",[\"$r\",\"code\",null,{\"children\":\"useOnlineStatus\"}],\" and move all the duplicated code into it from the components you wrote earlier:\"]}],\"\\n\",[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"{2-16}\",\"children\":\"function useOnlineStatus() {\\n const [isOnline, setIsOnline] = useState(true);\\n useEffect(() =\u003e {\\n function handleOnline() {\\n setIsOnline(true);\\n }\\n function handleOffline() {\\n setIsOnline(false);\\n }\\n window.addEventListener('online', handleOnline);\\n window.addEventListener('offline', handleOffline);\\n return () =\u003e {\\n window.removeEventListener('online', handleOnline);\\n window.removeEventListener('offline', handleOffline);\\n };\\n }, []);\\n return isOnline;\\n}\\n\"}]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"At the end of the function, return \",[\"$r\",\"code\",null,{\"children\":\"isOnline\"}],\". This lets your components read that value:\"]}],\"\\n\"]}],[\"$r\",\"Sandpack\",null,{\"children\":[[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"import { useOnlineStatus } from './useOnlineStatus.js';\\n\\nfunction StatusBar() {\\n const isOnline = useOnlineStatus();\\n return \u003ch1\u003e{isOnline ? '✅ Online' : '❌ Disconnected'}\u003c/h1\u003e;\\n}\\n\\nfunction SaveButton() {\\n const isOnline = useOnlineStatus();\\n\\n function handleSaveClick() {\\n console.log('✅ Progress saved');\\n }\\n\\n return (\\n \u003cbutton disabled={!isOnline} onClick={handleSaveClick}\u003e\\n {isOnline ? 'Save progress' : 'Reconnecting...'}\\n \u003c/button\u003e\\n );\\n}\\n\\nexport default function App() {\\n return (\\n \u003c\u003e\\n \u003cSaveButton /\u003e\\n \u003cStatusBar /\u003e\\n \u003c/\u003e\\n );\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/useOnlineStatus.js\",\"children\":\"import { useState, useEffect } from 'react';\\n\\nexport function useOnlineStatus() {\\n const [isOnline, setIsOnline] = useState(true);\\n useEffect(() =\u003e {\\n function handleOnline() {\\n setIsOnline(true);\\n }\\n function handleOffline() {\\n setIsOnline(false);\\n }\\n window.addEventListener('online', handleOnline);\\n window.addEventListener('offline', handleOffline);\\n return () =\u003e {\\n window.removeEventListener('online', handleOnline);\\n window.removeEventListener('offline', handleOffline);\\n };\\n }, []);\\n return isOnline;\\n}\\n\"}]}]]}],[\"$r\",\"MaxWidth\",\"74\",{\"children\":[\"\\n\",[\"$r\",\"p\",null,{\"children\":\"Verify that switching the network on and off updates both components.\"}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"Now your components don’t have as much repetitive logic. \",[\"$r\",\"strong\",null,{\"children\":[\"More importantly, the code inside them describes \",[\"$r\",\"em\",null,{\"children\":\"what they want to do\"}],\" (use the online status!) rather than \",[\"$r\",\"em\",null,{\"children\":\"how to do it\"}],\" (by subscribing to the browser events).\"]}]]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":\"When you extract logic into custom Hooks, you can hide the gnarly details of how you deal with some external system or a browser API. The code of your components expresses your intent, not the implementation.\"}],\"\\n\",[\"$r\",\"h3\",null,{\"id\":\"hook-names-always-start-with-use\",\"children\":[\"Hook names always start with \",[\"$r\",\"code\",null,{\"children\":\"use\"}],\" \"]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":\"React applications are built from components. Components are built from Hooks, whether built-in or custom. You’ll likely often use custom Hooks created by others, but occasionally you might write one yourself!\"}],\"\\n\",[\"$r\",\"p\",null,{\"children\":\"You must follow these naming conventions:\"}],\"\\n\",[\"$r\",\"ol\",null,{\"children\":[\"\\n\",[\"$r\",\"li\",null,{\"children\":[[\"$r\",\"strong\",null,{\"children\":\"React component names must start with a capital letter,\"}],\" like \",[\"$r\",\"code\",null,{\"children\":\"StatusBar\"}],\" and \",[\"$r\",\"code\",null,{\"children\":\"SaveButton\"}],\". React components also need to return something that React knows how to display, like a piece of JSX.\"]}],\"\\n\",[\"$r\",\"li\",null,{\"children\":[[\"$r\",\"strong\",null,{\"children\":[\"Hook names must start with \",[\"$r\",\"code\",null,{\"children\":\"use\"}],\" followed by a capital letter,\"]}],\" like \",[\"$r\",\"a\",null,{\"href\":\"/reference/react/useState\",\"children\":[\"$r\",\"code\",null,{\"children\":\"useState\"}]}],\" (built-in) or \",[\"$r\",\"code\",null,{\"children\":\"useOnlineStatus\"}],\" (custom, like earlier on the page). Hooks may return arbitrary values.\"]}],\"\\n\"]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"This convention guarantees that you can always look at a component and know where its state, Effects, and other React features might “hide”. For example, if you see a \",[\"$r\",\"code\",null,{\"children\":\"getColor()\"}],\" function call inside your component, you can be sure that it can’t possibly contain React state inside because its name doesn’t start with \",[\"$r\",\"code\",null,{\"children\":\"use\"}],\". However, a function call like \",[\"$r\",\"code\",null,{\"children\":\"useOnlineStatus()\"}],\" will most likely contain calls to other Hooks inside!\"]}],\"\\n\",[\"$r\",\"Note\",null,{\"children\":[\"$r\",\"p\",null,{\"children\":[\"If your linter is \",[\"$r\",\"a\",null,{\"href\":\"/learn/editor-setup#linting\",\"children\":\"configured for React,\"}],\" it will enforce this naming convention. Scroll up to the sandbox above and rename \",[\"$r\",\"code\",null,{\"children\":\"useOnlineStatus\"}],\" to \",[\"$r\",\"code\",null,{\"children\":\"getOnlineStatus\"}],\". Notice that the linter won’t allow you to call \",[\"$r\",\"code\",null,{\"children\":\"useState\"}],\" or \",[\"$r\",\"code\",null,{\"children\":\"useEffect\"}],\" inside of it anymore. Only Hooks and components can call other Hooks!\"]}]}],\"\\n\",[\"$r\",\"DeepDive\",null,{\"children\":[[\"$r\",\"h4\",null,{\"id\":\"should-all-functions-called-during-rendering-start-with-the-use-prefix\",\"children\":\"Should all functions called during rendering start with the use prefix? \"}],[\"$r\",\"p\",null,{\"children\":[\"No. Functions that don’t \",[\"$r\",\"em\",null,{\"children\":\"call\"}],\" Hooks don’t need to \",[\"$r\",\"em\",null,{\"children\":\"be\"}],\" Hooks.\"]}],[\"$r\",\"p\",null,{\"children\":[\"If your function doesn’t call any Hooks, avoid the \",[\"$r\",\"code\",null,{\"children\":\"use\"}],\" prefix. Instead, write it as a regular function \",[\"$r\",\"em\",null,{\"children\":\"without\"}],\" the \",[\"$r\",\"code\",null,{\"children\":\"use\"}],\" prefix. For example, \",[\"$r\",\"code\",null,{\"children\":\"useSorted\"}],\" below doesn’t call Hooks, so call it \",[\"$r\",\"code\",null,{\"children\":\"getSorted\"}],\" instead:\"]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"// 🔴 Avoid: A Hook that doesn't use Hooks\\nfunction useSorted(items) {\\n return items.slice().sort();\\n}\\n\\n// ✅ Good: A regular function that doesn't use Hooks\\nfunction getSorted(items) {\\n return items.slice().sort();\\n}\\n\"}]}],[\"$r\",\"p\",null,{\"children\":\"This ensures that your code can call this regular function anywhere, including conditions:\"}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"function List({ items, shouldSort }) {\\n let displayedItems = items;\\n if (shouldSort) {\\n // ✅ It's ok to call getSorted() conditionally because it's not a Hook\\n displayedItems = getSorted(items);\\n }\\n // ...\\n}\\n\"}]}],[\"$r\",\"p\",null,{\"children\":[\"You should give \",[\"$r\",\"code\",null,{\"children\":\"use\"}],\" prefix to a function (and thus make it a Hook) if it uses at least one Hook inside of it:\"]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"// ✅ Good: A Hook that uses other Hooks\\nfunction useAuth() {\\n return useContext(Auth);\\n}\\n\"}]}],[\"$r\",\"p\",null,{\"children\":[\"Technically, this isn’t enforced by React. In principle, you could make a Hook that doesn’t call other Hooks. This is often confusing and limiting so it’s best to avoid that pattern. However, there may be rare cases where it is helpful. For example, maybe your function doesn’t use any Hooks right now, but you plan to add some Hook calls to it in the future. Then it makes sense to name it with the \",[\"$r\",\"code\",null,{\"children\":\"use\"}],\" prefix:\"]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"{3-4}\",\"children\":\"// ✅ Good: A Hook that will likely use some other Hooks later\\nfunction useAuth() {\\n // TODO: Replace with this line when authentication is implemented:\\n // return useContext(Auth);\\n return TEST_USER;\\n}\\n\"}]}],[\"$r\",\"p\",null,{\"children\":\"Then components won’t be able to call it conditionally. This will become important when you actually add Hook calls inside. If you don’t plan to use Hooks inside it (now or later), don’t make it a Hook.\"}]]}],\"\\n\",[\"$r\",\"h3\",null,{\"id\":\"custom-hooks-let-you-share-stateful-logic-not-state-itself\",\"children\":\"Custom Hooks let you share stateful logic, not state itself \"}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"In the earlier example, when you turned the network on and off, both components updated together. However, it’s wrong to think that a single \",[\"$r\",\"code\",null,{\"children\":\"isOnline\"}],\" state variable is shared between them. Look at this code:\"]}],\"\\n\",[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"{2,7}\",\"children\":\"function StatusBar() {\\n const isOnline = useOnlineStatus();\\n // ...\\n}\\n\\nfunction SaveButton() {\\n const isOnline = useOnlineStatus();\\n // ...\\n}\\n\"}]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":\"It works the same way as before you extracted the duplication:\"}],\"\\n\",[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"{2-5,10-13}\",\"children\":\"function StatusBar() {\\n const [isOnline, setIsOnline] = useState(true);\\n useEffect(() =\u003e {\\n // ...\\n }, []);\\n // ...\\n}\\n\\nfunction SaveButton() {\\n const [isOnline, setIsOnline] = useState(true);\\n useEffect(() =\u003e {\\n // ...\\n }, []);\\n // ...\\n}\\n\"}]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":\"These are two completely independent state variables and Effects! They happened to have the same value at the same time because you synchronized them with the same external value (whether the network is on).\"}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"To better illustrate this, we’ll need a different example. Consider this \",[\"$r\",\"code\",null,{\"children\":\"Form\"}],\" component:\"]}],\"\\n\"]}],[\"$r\",\"Sandpack\",null,{\"children\":[[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"import { useState } from 'react';\\n\\nexport default function Form() {\\n const [firstName, setFirstName] = useState('Mary');\\n const [lastName, setLastName] = useState('Poppins');\\n\\n function handleFirstNameChange(e) {\\n setFirstName(e.target.value);\\n }\\n\\n function handleLastNameChange(e) {\\n setLastName(e.target.value);\\n }\\n\\n return (\\n \u003c\u003e\\n \u003clabel\u003e\\n First name:\\n \u003cinput value={firstName} onChange={handleFirstNameChange} /\u003e\\n \u003c/label\u003e\\n \u003clabel\u003e\\n Last name:\\n \u003cinput value={lastName} onChange={handleLastNameChange} /\u003e\\n \u003c/label\u003e\\n \u003cp\u003e\u003cb\u003eGood morning, {firstName} {lastName}.\u003c/b\u003e\u003c/p\u003e\\n \u003c/\u003e\\n );\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-css\",\"children\":\"label { display: block; }\\ninput { margin-left: 10px; }\\n\"}]}]]}],[\"$r\",\"MaxWidth\",\"82\",{\"children\":[\"\\n\",[\"$r\",\"p\",null,{\"children\":\"There’s some repetitive logic for each form field:\"}],\"\\n\",[\"$r\",\"ol\",null,{\"children\":[\"\\n\",[\"$r\",\"li\",null,{\"children\":[\"There’s a piece of state (\",[\"$r\",\"code\",null,{\"children\":\"firstName\"}],\" and \",[\"$r\",\"code\",null,{\"children\":\"lastName\"}],\").\"]}],\"\\n\",[\"$r\",\"li\",null,{\"children\":[\"There’s a change handler (\",[\"$r\",\"code\",null,{\"children\":\"handleFirstNameChange\"}],\" and \",[\"$r\",\"code\",null,{\"children\":\"handleLastNameChange\"}],\").\"]}],\"\\n\",[\"$r\",\"li\",null,{\"children\":[\"There’s a piece of JSX that specifies the \",[\"$r\",\"code\",null,{\"children\":\"value\"}],\" and \",[\"$r\",\"code\",null,{\"children\":\"onChange\"}],\" attributes for that input.\"]}],\"\\n\"]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"You can extract the repetitive logic into this \",[\"$r\",\"code\",null,{\"children\":\"useFormInput\"}],\" custom Hook:\"]}],\"\\n\"]}],[\"$r\",\"Sandpack\",null,{\"children\":[[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"import { useFormInput } from './useFormInput.js';\\n\\nexport default function Form() {\\n const firstNameProps = useFormInput('Mary');\\n const lastNameProps = useFormInput('Poppins');\\n\\n return (\\n \u003c\u003e\\n \u003clabel\u003e\\n First name:\\n \u003cinput {...firstNameProps} /\u003e\\n \u003c/label\u003e\\n \u003clabel\u003e\\n Last name:\\n \u003cinput {...lastNameProps} /\u003e\\n \u003c/label\u003e\\n \u003cp\u003e\u003cb\u003eGood morning, {firstNameProps.value} {lastNameProps.value}.\u003c/b\u003e\u003c/p\u003e\\n \u003c/\u003e\\n );\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/useFormInput.js active\",\"children\":\"import { useState } from 'react';\\n\\nexport function useFormInput(initialValue) {\\n const [value, setValue] = useState(initialValue);\\n\\n function handleChange(e) {\\n setValue(e.target.value);\\n }\\n\\n const inputProps = {\\n value: value,\\n onChange: handleChange\\n };\\n\\n return inputProps;\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-css\",\"children\":\"label { display: block; }\\ninput { margin-left: 10px; }\\n\"}]}]]}],[\"$r\",\"MaxWidth\",\"102\",{\"children\":[\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"Notice that it only declares \",[\"$r\",\"em\",null,{\"children\":\"one\"}],\" state variable called \",[\"$r\",\"code\",null,{\"children\":\"value\"}],\".\"]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"However, the \",[\"$r\",\"code\",null,{\"children\":\"Form\"}],\" component calls \",[\"$r\",\"code\",null,{\"children\":\"useFormInput\"}],\" \",[\"$r\",\"em\",null,{\"children\":\"two times:\"}]]}],\"\\n\",[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"function Form() {\\n const firstNameProps = useFormInput('Mary');\\n const lastNameProps = useFormInput('Poppins');\\n // ...\\n\"}]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":\"This is why it works like declaring two separate state variables!\"}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[[\"$r\",\"strong\",null,{\"children\":[\"Custom Hooks let you share \",[\"$r\",\"em\",null,{\"children\":\"stateful logic\"}],\" but not \",[\"$r\",\"em\",null,{\"children\":\"state itself.\"}],\" Each call to a Hook is completely independent from every other call to the same Hook.\"]}],\" This is why the two sandboxes above are completely equivalent. If you’d like, scroll back up and compare them. The behavior before and after extracting a custom Hook is identical.\"]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"When you need to share the state itself between multiple components, \",[\"$r\",\"a\",null,{\"href\":\"/learn/sharing-state-between-components\",\"children\":\"lift it up and pass it down\"}],\" instead.\"]}],\"\\n\",[\"$r\",\"h2\",null,{\"id\":\"passing-reactive-values-between-hooks\",\"children\":\"Passing reactive values between Hooks \"}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"The code inside your custom Hooks will re-run during every re-render of your component. This is why, like components, custom Hooks \",[\"$r\",\"a\",null,{\"href\":\"/learn/keeping-components-pure\",\"children\":\"need to be pure.\"}],\" Think of custom Hooks’ code as part of your component’s body!\"]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":\"Because custom Hooks re-render together with your component, they always receive the latest props and state. To see what this means, consider this chat room example. Change the server URL or the chat room:\"}],\"\\n\"]}],[\"$r\",\"Sandpack\",null,{\"children\":[[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/App.js\",\"children\":\"import { useState } from 'react';\\nimport ChatRoom from './ChatRoom.js';\\n\\nexport default function App() {\\n const [roomId, setRoomId] = useState('general');\\n return (\\n \u003c\u003e\\n \u003clabel\u003e\\n Choose the chat room:{' '}\\n \u003cselect\\n value={roomId}\\n onChange={e =\u003e setRoomId(e.target.value)}\\n \u003e\\n \u003coption value=\\\"general\\\"\u003egeneral\u003c/option\u003e\\n \u003coption value=\\\"travel\\\"\u003etravel\u003c/option\u003e\\n \u003coption value=\\\"music\\\"\u003emusic\u003c/option\u003e\\n \u003c/select\u003e\\n \u003c/label\u003e\\n \u003chr /\u003e\\n \u003cChatRoom\\n roomId={roomId}\\n /\u003e\\n \u003c/\u003e\\n );\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/ChatRoom.js active\",\"children\":\"import { useState, useEffect } from 'react';\\nimport { createConnection } from './chat.js';\\nimport { showNotification } from './notifications.js';\\n\\nexport default function ChatRoom({ roomId }) {\\n const [serverUrl, setServerUrl] = useState('https://localhost:1234');\\n\\n useEffect(() =\u003e {\\n const options = {\\n serverUrl: serverUrl,\\n roomId: roomId\\n };\\n const connection = createConnection(options);\\n connection.on('message', (msg) =\u003e {\\n showNotification('New message: ' + msg);\\n });\\n connection.connect();\\n return () =\u003e connection.disconnect();\\n }, [roomId, serverUrl]);\\n\\n return (\\n \u003c\u003e\\n \u003clabel\u003e\\n Server URL:\\n \u003cinput value={serverUrl} onChange={e =\u003e setServerUrl(e.target.value)} /\u003e\\n \u003c/label\u003e\\n \u003ch1\u003eWelcome to the {roomId} room!\u003c/h1\u003e\\n \u003c/\u003e\\n );\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/chat.js\",\"children\":\"export function createConnection({ serverUrl, roomId }) {\\n // A real implementation would actually connect to the server\\n if (typeof serverUrl !== 'string') {\\n throw Error('Expected serverUrl to be a string. Received: ' + serverUrl);\\n }\\n if (typeof roomId !== 'string') {\\n throw Error('Expected roomId to be a string. Received: ' + roomId);\\n }\\n let intervalId;\\n let messageCallback;\\n return {\\n connect() {\\n console.log('✅ Connecting to \\\"' + roomId + '\\\" room at ' + serverUrl + '...');\\n clearInterval(intervalId);\\n intervalId = setInterval(() =\u003e {\\n if (messageCallback) {\\n if (Math.random() \u003e 0.5) {\\n messageCallback('hey')\\n } else {\\n messageCallback('lol');\\n }\\n }\\n }, 3000);\\n },\\n disconnect() {\\n clearInterval(intervalId);\\n messageCallback = null;\\n console.log('❌ Disconnected from \\\"' + roomId + '\\\" room at ' + serverUrl + '');\\n },\\n on(event, callback) {\\n if (messageCallback) {\\n throw Error('Cannot add the handler twice.');\\n }\\n if (event !== 'message') {\\n throw Error('Only \\\"message\\\" event is supported.');\\n }\\n messageCallback = callback;\\n },\\n };\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/notifications.js\",\"children\":\"import Toastify from 'toastify-js';\\nimport 'toastify-js/src/toastify.css';\\n\\nexport function showNotification(message, theme = 'dark') {\\n Toastify({\\n text: message,\\n duration: 2000,\\n gravity: 'top',\\n position: 'right',\\n style: {\\n background: theme === 'dark' ? 'black' : 'white',\\n color: theme === 'dark' ? 'white' : 'black',\\n },\\n }).showToast();\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-json\",\"meta\":\"package.json hidden\",\"children\":\"{\\n \\\"dependencies\\\": {\\n \\\"react\\\": \\\"latest\\\",\\n \\\"react-dom\\\": \\\"latest\\\",\\n \\\"react-scripts\\\": \\\"latest\\\",\\n \\\"toastify-js\\\": \\\"1.12.0\\\"\\n },\\n \\\"scripts\\\": {\\n \\\"start\\\": \\\"react-scripts start\\\",\\n \\\"build\\\": \\\"react-scripts build\\\",\\n \\\"test\\\": \\\"react-scripts test --env=jsdom\\\",\\n \\\"eject\\\": \\\"react-scripts eject\\\"\\n }\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-css\",\"children\":\"input { display: block; margin-bottom: 20px; }\\nbutton { margin-left: 10px; }\\n\"}]}]]}],[\"$r\",\"MaxWidth\",\"118\",{\"children\":[\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"When you change \",[\"$r\",\"code\",null,{\"children\":\"serverUrl\"}],\" or \",[\"$r\",\"code\",null,{\"children\":\"roomId\"}],\", the Effect \",[\"$r\",\"a\",null,{\"href\":\"/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values\",\"children\":\"“reacts” to your changes\"}],\" and re-synchronizes. You can tell by the console messages that the chat re-connects every time that you change your Effect’s dependencies.\"]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":\"Now move the Effect’s code into a custom Hook:\"}],\"\\n\",[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"{2-13}\",\"children\":\"export function useChatRoom({ serverUrl, roomId }) {\\n useEffect(() =\u003e {\\n const options = {\\n serverUrl: serverUrl,\\n roomId: roomId\\n };\\n const connection = createConnection(options);\\n connection.connect();\\n connection.on('message', (msg) =\u003e {\\n showNotification('New message: ' + msg);\\n });\\n return () =\u003e connection.disconnect();\\n }, [roomId, serverUrl]);\\n}\\n\"}]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"This lets your \",[\"$r\",\"code\",null,{\"children\":\"ChatRoom\"}],\" component call your custom Hook without worrying about how it works inside:\"]}],\"\\n\",[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"{4-7}\",\"children\":\"export default function ChatRoom({ roomId }) {\\n const [serverUrl, setServerUrl] = useState('https://localhost:1234');\\n\\n useChatRoom({\\n roomId: roomId,\\n serverUrl: serverUrl\\n });\\n\\n return (\\n \u003c\u003e\\n \u003clabel\u003e\\n Server URL:\\n \u003cinput value={serverUrl} onChange={e =\u003e setServerUrl(e.target.value)} /\u003e\\n \u003c/label\u003e\\n \u003ch1\u003eWelcome to the {roomId} room!\u003c/h1\u003e\\n \u003c/\u003e\\n );\\n}\\n\"}]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":\"This looks much simpler! (But it does the same thing.)\"}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"Notice that the logic \",[\"$r\",\"em\",null,{\"children\":\"still responds\"}],\" to prop and state changes. Try editing the server URL or the selected room:\"]}],\"\\n\"]}],[\"$r\",\"Sandpack\",null,{\"children\":[[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/App.js\",\"children\":\"import { useState } from 'react';\\nimport ChatRoom from './ChatRoom.js';\\n\\nexport default function App() {\\n const [roomId, setRoomId] = useState('general');\\n return (\\n \u003c\u003e\\n \u003clabel\u003e\\n Choose the chat room:{' '}\\n \u003cselect\\n value={roomId}\\n onChange={e =\u003e setRoomId(e.target.value)}\\n \u003e\\n \u003coption value=\\\"general\\\"\u003egeneral\u003c/option\u003e\\n \u003coption value=\\\"travel\\\"\u003etravel\u003c/option\u003e\\n \u003coption value=\\\"music\\\"\u003emusic\u003c/option\u003e\\n \u003c/select\u003e\\n \u003c/label\u003e\\n \u003chr /\u003e\\n \u003cChatRoom\\n roomId={roomId}\\n /\u003e\\n \u003c/\u003e\\n );\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/ChatRoom.js active\",\"children\":\"import { useState } from 'react';\\nimport { useChatRoom } from './useChatRoom.js';\\n\\nexport default function ChatRoom({ roomId }) {\\n const [serverUrl, setServerUrl] = useState('https://localhost:1234');\\n\\n useChatRoom({\\n roomId: roomId,\\n serverUrl: serverUrl\\n });\\n\\n return (\\n \u003c\u003e\\n \u003clabel\u003e\\n Server URL:\\n \u003cinput value={serverUrl} onChange={e =\u003e setServerUrl(e.target.value)} /\u003e\\n \u003c/label\u003e\\n \u003ch1\u003eWelcome to the {roomId} room!\u003c/h1\u003e\\n \u003c/\u003e\\n );\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/useChatRoom.js\",\"children\":\"import { useEffect } from 'react';\\nimport { createConnection } from './chat.js';\\nimport { showNotification } from './notifications.js';\\n\\nexport function useChatRoom({ serverUrl, roomId }) {\\n useEffect(() =\u003e {\\n const options = {\\n serverUrl: serverUrl,\\n roomId: roomId\\n };\\n const connection = createConnection(options);\\n connection.connect();\\n connection.on('message', (msg) =\u003e {\\n showNotification('New message: ' + msg);\\n });\\n return () =\u003e connection.disconnect();\\n }, [roomId, serverUrl]);\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/chat.js\",\"children\":\"export function createConnection({ serverUrl, roomId }) {\\n // A real implementation would actually connect to the server\\n if (typeof serverUrl !== 'string') {\\n throw Error('Expected serverUrl to be a string. Received: ' + serverUrl);\\n }\\n if (typeof roomId !== 'string') {\\n throw Error('Expected roomId to be a string. Received: ' + roomId);\\n }\\n let intervalId;\\n let messageCallback;\\n return {\\n connect() {\\n console.log('✅ Connecting to \\\"' + roomId + '\\\" room at ' + serverUrl + '...');\\n clearInterval(intervalId);\\n intervalId = setInterval(() =\u003e {\\n if (messageCallback) {\\n if (Math.random() \u003e 0.5) {\\n messageCallback('hey')\\n } else {\\n messageCallback('lol');\\n }\\n }\\n }, 3000);\\n },\\n disconnect() {\\n clearInterval(intervalId);\\n messageCallback = null;\\n console.log('❌ Disconnected from \\\"' + roomId + '\\\" room at ' + serverUrl + '');\\n },\\n on(event, callback) {\\n if (messageCallback) {\\n throw Error('Cannot add the handler twice.');\\n }\\n if (event !== 'message') {\\n throw Error('Only \\\"message\\\" event is supported.');\\n }\\n messageCallback = callback;\\n },\\n };\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/notifications.js\",\"children\":\"import Toastify from 'toastify-js';\\nimport 'toastify-js/src/toastify.css';\\n\\nexport function showNotification(message, theme = 'dark') {\\n Toastify({\\n text: message,\\n duration: 2000,\\n gravity: 'top',\\n position: 'right',\\n style: {\\n background: theme === 'dark' ? 'black' : 'white',\\n color: theme === 'dark' ? 'white' : 'black',\\n },\\n }).showToast();\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-json\",\"meta\":\"package.json hidden\",\"children\":\"{\\n \\\"dependencies\\\": {\\n \\\"react\\\": \\\"latest\\\",\\n \\\"react-dom\\\": \\\"latest\\\",\\n \\\"react-scripts\\\": \\\"latest\\\",\\n \\\"toastify-js\\\": \\\"1.12.0\\\"\\n },\\n \\\"scripts\\\": {\\n \\\"start\\\": \\\"react-scripts start\\\",\\n \\\"build\\\": \\\"react-scripts build\\\",\\n \\\"test\\\": \\\"react-scripts test --env=jsdom\\\",\\n \\\"eject\\\": \\\"react-scripts eject\\\"\\n }\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-css\",\"children\":\"input { display: block; margin-bottom: 20px; }\\nbutton { margin-left: 10px; }\\n\"}]}]]}],[\"$r\",\"MaxWidth\",\"154\",{\"children\":[\"\\n\",[\"$r\",\"p\",null,{\"children\":\"Notice how you’re taking the return value of one Hook:\"}],\"\\n\",[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"{2}\",\"children\":\"export default function ChatRoom({ roomId }) {\\n const [serverUrl, setServerUrl] = useState('https://localhost:1234');\\n\\n useChatRoom({\\n roomId: roomId,\\n serverUrl: serverUrl\\n });\\n // ...\\n\"}]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":\"and pass it as an input to another Hook:\"}],\"\\n\",[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"{6}\",\"children\":\"export default function ChatRoom({ roomId }) {\\n const [serverUrl, setServerUrl] = useState('https://localhost:1234');\\n\\n useChatRoom({\\n roomId: roomId,\\n serverUrl: serverUrl\\n });\\n // ...\\n\"}]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"Every time your \",[\"$r\",\"code\",null,{\"children\":\"ChatRoom\"}],\" component re-renders, it passes the latest \",[\"$r\",\"code\",null,{\"children\":\"roomId\"}],\" and \",[\"$r\",\"code\",null,{\"children\":\"serverUrl\"}],\" to your Hook. This is why your Effect re-connects to the chat whenever their values are different after a re-render. (If you ever worked with audio or video processing software, chaining Hooks like this might remind you of chaining visual or audio effects. It’s as if the output of \",[\"$r\",\"code\",null,{\"children\":\"useState\"}],\" “feeds into” the input of the \",[\"$r\",\"code\",null,{\"children\":\"useChatRoom\"}],\".)\"]}],\"\\n\",[\"$r\",\"h3\",null,{\"id\":\"passing-event-handlers-to-custom-hooks\",\"children\":\"Passing event handlers to custom Hooks \"}],\"\\n\",[\"$r\",\"Wip\",null,{\"children\":[\"$r\",\"p\",null,{\"children\":[\"This section describes an \",[\"$r\",\"strong\",null,{\"children\":\"experimental API that has not yet been released\"}],\" in a stable version of React.\"]}]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"As you start using \",[\"$r\",\"code\",null,{\"children\":\"useChatRoom\"}],\" in more components, you might want to let components customize its behavior. For example, currently, the logic for what to do when a message arrives is hardcoded inside the Hook:\"]}],\"\\n\",[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"{9-11}\",\"children\":\"export function useChatRoom({ serverUrl, roomId }) {\\n useEffect(() =\u003e {\\n const options = {\\n serverUrl: serverUrl,\\n roomId: roomId\\n };\\n const connection = createConnection(options);\\n connection.connect();\\n connection.on('message', (msg) =\u003e {\\n showNotification('New message: ' + msg);\\n });\\n return () =\u003e connection.disconnect();\\n }, [roomId, serverUrl]);\\n}\\n\"}]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":\"Let’s say you want to move this logic back to your component:\"}],\"\\n\",[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"{7-9}\",\"children\":\"export default function ChatRoom({ roomId }) {\\n const [serverUrl, setServerUrl] = useState('https://localhost:1234');\\n\\n useChatRoom({\\n roomId: roomId,\\n serverUrl: serverUrl,\\n onReceiveMessage(msg) {\\n showNotification('New message: ' + msg);\\n }\\n });\\n // ...\\n\"}]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"To make this work, change your custom Hook to take \",[\"$r\",\"code\",null,{\"children\":\"onReceiveMessage\"}],\" as one of its named options:\"]}],\"\\n\",[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"{1,10,13}\",\"children\":\"export function useChatRoom({ serverUrl, roomId, onReceiveMessage }) {\\n useEffect(() =\u003e {\\n const options = {\\n serverUrl: serverUrl,\\n roomId: roomId\\n };\\n const connection = createConnection(options);\\n connection.connect();\\n connection.on('message', (msg) =\u003e {\\n onReceiveMessage(msg);\\n });\\n return () =\u003e connection.disconnect();\\n }, [roomId, serverUrl, onReceiveMessage]); // ✅ All dependencies declared\\n}\\n\"}]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":\"This will work, but there’s one more improvement you can do when your custom Hook accepts event handlers.\"}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"Adding a dependency on \",[\"$r\",\"code\",null,{\"children\":\"onReceiveMessage\"}],\" is not ideal because it will cause the chat to re-connect every time the component re-renders. \",[\"$r\",\"a\",null,{\"href\":\"/learn/removing-effect-dependencies#wrapping-an-event-handler-from-the-props\",\"children\":\"Wrap this event handler into an Effect Event to remove it from the dependencies:\"}]]}],\"\\n\",[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"{1,4,5,15,18}\",\"children\":\"import { useEffect, useEffectEvent } from 'react';\\n// ...\\n\\nexport function useChatRoom({ serverUrl, roomId, onReceiveMessage }) {\\n const onMessage = useEffectEvent(onReceiveMessage);\\n\\n useEffect(() =\u003e {\\n const options = {\\n serverUrl: serverUrl,\\n roomId: roomId\\n };\\n const connection = createConnection(options);\\n connection.connect();\\n connection.on('message', (msg) =\u003e {\\n onMessage(msg);\\n });\\n return () =\u003e connection.disconnect();\\n }, [roomId, serverUrl]); // ✅ All dependencies declared\\n}\\n\"}]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"Now the chat won’t re-connect every time that the \",[\"$r\",\"code\",null,{\"children\":\"ChatRoom\"}],\" component re-renders. Here is a fully working demo of passing an event handler to a custom Hook that you can play with:\"]}],\"\\n\"]}],[\"$r\",\"Sandpack\",null,{\"children\":[[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/App.js\",\"children\":\"import { useState } from 'react';\\nimport ChatRoom from './ChatRoom.js';\\n\\nexport default function App() {\\n const [roomId, setRoomId] = useState('general');\\n return (\\n \u003c\u003e\\n \u003clabel\u003e\\n Choose the chat room:{' '}\\n \u003cselect\\n value={roomId}\\n onChange={e =\u003e setRoomId(e.target.value)}\\n \u003e\\n \u003coption value=\\\"general\\\"\u003egeneral\u003c/option\u003e\\n \u003coption value=\\\"travel\\\"\u003etravel\u003c/option\u003e\\n \u003coption value=\\\"music\\\"\u003emusic\u003c/option\u003e\\n \u003c/select\u003e\\n \u003c/label\u003e\\n \u003chr /\u003e\\n \u003cChatRoom\\n roomId={roomId}\\n /\u003e\\n \u003c/\u003e\\n );\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/ChatRoom.js active\",\"children\":\"import { useState } from 'react';\\nimport { useChatRoom } from './useChatRoom.js';\\nimport { showNotification } from './notifications.js';\\n\\nexport default function ChatRoom({ roomId }) {\\n const [serverUrl, setServerUrl] = useState('https://localhost:1234');\\n\\n useChatRoom({\\n roomId: roomId,\\n serverUrl: serverUrl,\\n onReceiveMessage(msg) {\\n showNotification('New message: ' + msg);\\n }\\n });\\n\\n return (\\n \u003c\u003e\\n \u003clabel\u003e\\n Server URL:\\n \u003cinput value={serverUrl} onChange={e =\u003e setServerUrl(e.target.value)} /\u003e\\n \u003c/label\u003e\\n \u003ch1\u003eWelcome to the {roomId} room!\u003c/h1\u003e\\n \u003c/\u003e\\n );\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/useChatRoom.js\",\"children\":\"import { useEffect } from 'react';\\nimport { experimental_useEffectEvent as useEffectEvent } from 'react';\\nimport { createConnection } from './chat.js';\\n\\nexport function useChatRoom({ serverUrl, roomId, onReceiveMessage }) {\\n const onMessage = useEffectEvent(onReceiveMessage);\\n\\n useEffect(() =\u003e {\\n const options = {\\n serverUrl: serverUrl,\\n roomId: roomId\\n };\\n const connection = createConnection(options);\\n connection.connect();\\n connection.on('message', (msg) =\u003e {\\n onMessage(msg);\\n });\\n return () =\u003e connection.disconnect();\\n }, [roomId, serverUrl]);\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/chat.js\",\"children\":\"export function createConnection({ serverUrl, roomId }) {\\n // A real implementation would actually connect to the server\\n if (typeof serverUrl !== 'string') {\\n throw Error('Expected serverUrl to be a string. Received: ' + serverUrl);\\n }\\n if (typeof roomId !== 'string') {\\n throw Error('Expected roomId to be a string. Received: ' + roomId);\\n }\\n let intervalId;\\n let messageCallback;\\n return {\\n connect() {\\n console.log('✅ Connecting to \\\"' + roomId + '\\\" room at ' + serverUrl + '...');\\n clearInterval(intervalId);\\n intervalId = setInterval(() =\u003e {\\n if (messageCallback) {\\n if (Math.random() \u003e 0.5) {\\n messageCallback('hey')\\n } else {\\n messageCallback('lol');\\n }\\n }\\n }, 3000);\\n },\\n disconnect() {\\n clearInterval(intervalId);\\n messageCallback = null;\\n console.log('❌ Disconnected from \\\"' + roomId + '\\\" room at ' + serverUrl + '');\\n },\\n on(event, callback) {\\n if (messageCallback) {\\n throw Error('Cannot add the handler twice.');\\n }\\n if (event !== 'message') {\\n throw Error('Only \\\"message\\\" event is supported.');\\n }\\n messageCallback = callback;\\n },\\n };\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/notifications.js\",\"children\":\"import Toastify from 'toastify-js';\\nimport 'toastify-js/src/toastify.css';\\n\\nexport function showNotification(message, theme = 'dark') {\\n Toastify({\\n text: message,\\n duration: 2000,\\n gravity: 'top',\\n position: 'right',\\n style: {\\n background: theme === 'dark' ? 'black' : 'white',\\n color: theme === 'dark' ? 'white' : 'black',\\n },\\n }).showToast();\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-json\",\"meta\":\"package.json hidden\",\"children\":\"{\\n \\\"dependencies\\\": {\\n \\\"react\\\": \\\"experimental\\\",\\n \\\"react-dom\\\": \\\"experimental\\\",\\n \\\"react-scripts\\\": \\\"latest\\\",\\n \\\"toastify-js\\\": \\\"1.12.0\\\"\\n },\\n \\\"scripts\\\": {\\n \\\"start\\\": \\\"react-scripts start\\\",\\n \\\"build\\\": \\\"react-scripts build\\\",\\n \\\"test\\\": \\\"react-scripts test --env=jsdom\\\",\\n \\\"eject\\\": \\\"react-scripts eject\\\"\\n }\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-css\",\"children\":\"input { display: block; margin-bottom: 20px; }\\nbutton { margin-left: 10px; }\\n\"}]}]]}],[\"$r\",\"MaxWidth\",\"186\",{\"children\":[\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"Notice how you no longer need to know \",[\"$r\",\"em\",null,{\"children\":\"how\"}],\" \",[\"$r\",\"code\",null,{\"children\":\"useChatRoom\"}],\" works in order to use it. You could add it to any other component, pass any other options, and it would work the same way. That’s the power of custom Hooks.\"]}],\"\\n\",[\"$r\",\"h2\",null,{\"id\":\"when-to-use-custom-hooks\",\"children\":\"When to use custom Hooks \"}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"You don’t need to extract a custom Hook for every little duplicated bit of code. Some duplication is fine. For example, extracting a \",[\"$r\",\"code\",null,{\"children\":\"useFormInput\"}],\" Hook to wrap a single \",[\"$r\",\"code\",null,{\"children\":\"useState\"}],\" call like earlier is probably unnecessary.\"]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"However, whenever you write an Effect, consider whether it would be clearer to also wrap it in a custom Hook. \",[\"$r\",\"a\",null,{\"href\":\"/learn/you-might-not-need-an-effect\",\"children\":\"You shouldn’t need Effects very often,\"}],\" so if you’re writing one, it means that you need to “step outside React” to synchronize with some external system or to do something that React doesn’t have a built-in API for. Wrapping it into a custom Hook lets you precisely communicate your intent and how the data flows through it.\"]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"For example, consider a \",[\"$r\",\"code\",null,{\"children\":\"ShippingForm\"}],\" component that displays two dropdowns: one shows the list of cities, and another shows the list of areas in the selected city. You might start with some code that looks like this:\"]}],\"\\n\",[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"{3-16,20-35}\",\"children\":\"function ShippingForm({ country }) {\\n const [cities, setCities] = useState(null);\\n // This Effect fetches cities for a country\\n useEffect(() =\u003e {\\n let ignore = false;\\n fetch(`/api/cities?country=${country}`)\\n .then(response =\u003e response.json())\\n .then(json =\u003e {\\n if (!ignore) {\\n setCities(json);\\n }\\n });\\n return () =\u003e {\\n ignore = true;\\n };\\n }, [country]);\\n\\n const [city, setCity] = useState(null);\\n const [areas, setAreas] = useState(null);\\n // This Effect fetches areas for the selected city\\n useEffect(() =\u003e {\\n if (city) {\\n let ignore = false;\\n fetch(`/api/areas?city=${city}`)\\n .then(response =\u003e response.json())\\n .then(json =\u003e {\\n if (!ignore) {\\n setAreas(json);\\n }\\n });\\n return () =\u003e {\\n ignore = true;\\n };\\n }\\n }, [city]);\\n\\n // ...\\n\"}]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"Although this code is quite repetitive, \",[\"$r\",\"a\",null,{\"href\":\"/learn/removing-effect-dependencies#is-your-effect-doing-several-unrelated-things\",\"children\":\"it’s correct to keep these Effects separate from each other.\"}],\" They synchronize two different things, so you shouldn’t merge them into one Effect. Instead, you can simplify the \",[\"$r\",\"code\",null,{\"children\":\"ShippingForm\"}],\" component above by extracting the common logic between them into your own \",[\"$r\",\"code\",null,{\"children\":\"useData\"}],\" Hook:\"]}],\"\\n\",[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"{2-18}\",\"children\":\"function useData(url) {\\n const [data, setData] = useState(null);\\n useEffect(() =\u003e {\\n if (url) {\\n let ignore = false;\\n fetch(url)\\n .then(response =\u003e response.json())\\n .then(json =\u003e {\\n if (!ignore) {\\n setData(json);\\n }\\n });\\n return () =\u003e {\\n ignore = true;\\n };\\n }\\n }, [url]);\\n return data;\\n}\\n\"}]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"Now you can replace both Effects in the \",[\"$r\",\"code\",null,{\"children\":\"ShippingForm\"}],\" components with calls to \",[\"$r\",\"code\",null,{\"children\":\"useData\"}],\":\"]}],\"\\n\",[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"{2,4}\",\"children\":\"function ShippingForm({ country }) {\\n const cities = useData(`/api/cities?country=${country}`);\\n const [city, setCity] = useState(null);\\n const areas = useData(city ? `/api/areas?city=${city}` : null);\\n // ...\\n\"}]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"Extracting a custom Hook makes the data flow explicit. You feed the \",[\"$r\",\"code\",null,{\"children\":\"url\"}],\" in and you get the \",[\"$r\",\"code\",null,{\"children\":\"data\"}],\" out. By “hiding” your Effect inside \",[\"$r\",\"code\",null,{\"children\":\"useData\"}],\", you also prevent someone working on the \",[\"$r\",\"code\",null,{\"children\":\"ShippingForm\"}],\" component from adding \",[\"$r\",\"a\",null,{\"href\":\"/learn/removing-effect-dependencies\",\"children\":\"unnecessary dependencies\"}],\" to it. With time, most of your app’s Effects will be in custom Hooks.\"]}],\"\\n\",[\"$r\",\"DeepDive\",null,{\"children\":[[\"$r\",\"h4\",null,{\"id\":\"keep-your-custom-hooks-focused-on-concrete-high-level-use-cases\",\"children\":\"Keep your custom Hooks focused on concrete high-level use cases \"}],[\"$r\",\"p\",null,{\"children\":\"Start by choosing your custom Hook’s name. If you struggle to pick a clear name, it might mean that your Effect is too coupled to the rest of your component’s logic, and is not yet ready to be extracted.\"}],[\"$r\",\"p\",null,{\"children\":\"Ideally, your custom Hook’s name should be clear enough that even a person who doesn’t write code often could have a good guess about what your custom Hook does, what it takes, and what it returns:\"}],[\"$r\",\"ul\",null,{\"children\":[\"\\n\",[\"$r\",\"li\",null,{\"children\":[\"✅ \",[\"$r\",\"code\",null,{\"children\":\"useData(url)\"}]]}],\"\\n\",[\"$r\",\"li\",null,{\"children\":[\"✅ \",[\"$r\",\"code\",null,{\"children\":\"useImpressionLog(eventName, extraData)\"}]]}],\"\\n\",[\"$r\",\"li\",null,{\"children\":[\"✅ \",[\"$r\",\"code\",null,{\"children\":\"useChatRoom(options)\"}]]}],\"\\n\"]}],[\"$r\",\"p\",null,{\"children\":\"When you synchronize with an external system, your custom Hook name may be more technical and use jargon specific to that system. It’s good as long as it would be clear to a person familiar with that system:\"}],[\"$r\",\"ul\",null,{\"children\":[\"\\n\",[\"$r\",\"li\",null,{\"children\":[\"✅ \",[\"$r\",\"code\",null,{\"children\":\"useMediaQuery(query)\"}]]}],\"\\n\",[\"$r\",\"li\",null,{\"children\":[\"✅ \",[\"$r\",\"code\",null,{\"children\":\"useSocket(url)\"}]]}],\"\\n\",[\"$r\",\"li\",null,{\"children\":[\"✅ \",[\"$r\",\"code\",null,{\"children\":\"useIntersectionObserver(ref, options)\"}]]}],\"\\n\"]}],[\"$r\",\"p\",null,{\"children\":[[\"$r\",\"strong\",null,{\"children\":\"Keep custom Hooks focused on concrete high-level use cases.\"}],\" Avoid creating and using custom “lifecycle” Hooks that act as alternatives and convenience wrappers for the \",[\"$r\",\"code\",null,{\"children\":\"useEffect\"}],\" API itself:\"]}],[\"$r\",\"ul\",null,{\"children\":[\"\\n\",[\"$r\",\"li\",null,{\"children\":[\"🔴 \",[\"$r\",\"code\",null,{\"children\":\"useMount(fn)\"}]]}],\"\\n\",[\"$r\",\"li\",null,{\"children\":[\"🔴 \",[\"$r\",\"code\",null,{\"children\":\"useEffectOnce(fn)\"}]]}],\"\\n\",[\"$r\",\"li\",null,{\"children\":[\"🔴 \",[\"$r\",\"code\",null,{\"children\":\"useUpdateEffect(fn)\"}]]}],\"\\n\"]}],[\"$r\",\"p\",null,{\"children\":[\"For example, this \",[\"$r\",\"code\",null,{\"children\":\"useMount\"}],\" Hook tries to ensure some code only runs “on mount”:\"]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"{4-5,14-15}\",\"children\":\"function ChatRoom({ roomId }) {\\n const [serverUrl, setServerUrl] = useState('https://localhost:1234');\\n\\n // 🔴 Avoid: using custom \\\"lifecycle\\\" Hooks\\n useMount(() =\u003e {\\n const connection = createConnection({ roomId, serverUrl });\\n connection.connect();\\n\\n post('/analytics/event', { eventName: 'visit_chat' });\\n });\\n // ...\\n}\\n\\n// 🔴 Avoid: creating custom \\\"lifecycle\\\" Hooks\\nfunction useMount(fn) {\\n useEffect(() =\u003e {\\n fn();\\n }, []); // 🔴 React Hook useEffect has a missing dependency: 'fn'\\n}\\n\"}]}],[\"$r\",\"p\",null,{\"children\":[[\"$r\",\"strong\",null,{\"children\":[\"Custom “lifecycle” Hooks like \",[\"$r\",\"code\",null,{\"children\":\"useMount\"}],\" don’t fit well into the React paradigm.\"]}],\" For example, this code example has a mistake (it doesn’t “react” to \",[\"$r\",\"code\",null,{\"children\":\"roomId\"}],\" or \",[\"$r\",\"code\",null,{\"children\":\"serverUrl\"}],\" changes), but the linter won’t warn you about it because the linter only checks direct \",[\"$r\",\"code\",null,{\"children\":\"useEffect\"}],\" calls. It won’t know about your Hook.\"]}],[\"$r\",\"p\",null,{\"children\":\"If you’re writing an Effect, start by using the React API directly:\"}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"function ChatRoom({ roomId }) {\\n const [serverUrl, setServerUrl] = useState('https://localhost:1234');\\n\\n // ✅ Good: two raw Effects separated by purpose\\n\\n useEffect(() =\u003e {\\n const connection = createConnection({ serverUrl, roomId });\\n connection.connect();\\n return () =\u003e connection.disconnect();\\n }, [serverUrl, roomId]);\\n\\n useEffect(() =\u003e {\\n post('/analytics/event', { eventName: 'visit_chat', roomId });\\n }, [roomId]);\\n\\n // ...\\n}\\n\"}]}],[\"$r\",\"p\",null,{\"children\":\"Then, you can (but don’t have to) extract custom Hooks for different high-level use cases:\"}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"function ChatRoom({ roomId }) {\\n const [serverUrl, setServerUrl] = useState('https://localhost:1234');\\n\\n // ✅ Great: custom Hooks named after their purpose\\n useChatRoom({ serverUrl, roomId });\\n useImpressionLog('visit_chat', { roomId });\\n // ...\\n}\\n\"}]}],[\"$r\",\"p\",null,{\"children\":[[\"$r\",\"strong\",null,{\"children\":\"A good custom Hook makes the calling code more declarative by constraining what it does.\"}],\" For example, \",[\"$r\",\"code\",null,{\"children\":\"useChatRoom(options)\"}],\" can only connect to the chat room, while \",[\"$r\",\"code\",null,{\"children\":\"useImpressionLog(eventName, extraData)\"}],\" can only send an impression log to the analytics. If your custom Hook API doesn’t constrain the use cases and is very abstract, in the long run it’s likely to introduce more problems than it solves.\"]}]]}],\"\\n\",[\"$r\",\"h3\",null,{\"id\":\"custom-hooks-help-you-migrate-to-better-patterns\",\"children\":\"Custom Hooks help you migrate to better patterns \"}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"Effects are an \",[\"$r\",\"a\",null,{\"href\":\"/learn/escape-hatches\",\"children\":\"“escape hatch”\"}],\": you use them when you need to “step outside React” and when there is no better built-in solution for your use case. With time, the React team’s goal is to reduce the number of the Effects in your app to the minimum by providing more specific solutions to more specific problems. Wrapping your Effects in custom Hooks makes it easier to upgrade your code when these solutions become available.\"]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":\"Let’s return to this example:\"}],\"\\n\"]}],[\"$r\",\"Sandpack\",null,{\"children\":[[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"import { useOnlineStatus } from './useOnlineStatus.js';\\n\\nfunction StatusBar() {\\n const isOnline = useOnlineStatus();\\n return \u003ch1\u003e{isOnline ? '✅ Online' : '❌ Disconnected'}\u003c/h1\u003e;\\n}\\n\\nfunction SaveButton() {\\n const isOnline = useOnlineStatus();\\n\\n function handleSaveClick() {\\n console.log('✅ Progress saved');\\n }\\n\\n return (\\n \u003cbutton disabled={!isOnline} onClick={handleSaveClick}\u003e\\n {isOnline ? 'Save progress' : 'Reconnecting...'}\\n \u003c/button\u003e\\n );\\n}\\n\\nexport default function App() {\\n return (\\n \u003c\u003e\\n \u003cSaveButton /\u003e\\n \u003cStatusBar /\u003e\\n \u003c/\u003e\\n );\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/useOnlineStatus.js active\",\"children\":\"import { useState, useEffect } from 'react';\\n\\nexport function useOnlineStatus() {\\n const [isOnline, setIsOnline] = useState(true);\\n useEffect(() =\u003e {\\n function handleOnline() {\\n setIsOnline(true);\\n }\\n function handleOffline() {\\n setIsOnline(false);\\n }\\n window.addEventListener('online', handleOnline);\\n window.addEventListener('offline', handleOffline);\\n return () =\u003e {\\n window.removeEventListener('online', handleOnline);\\n window.removeEventListener('offline', handleOffline);\\n };\\n }, []);\\n return isOnline;\\n}\\n\"}]}]]}],[\"$r\",\"MaxWidth\",\"192\",{\"children\":[\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"In the above example, \",[\"$r\",\"code\",null,{\"children\":\"useOnlineStatus\"}],\" is implemented with a pair of \",[\"$r\",\"a\",null,{\"href\":\"/reference/react/useState\",\"children\":[\"$r\",\"code\",null,{\"children\":\"useState\"}]}],\" and \",[\"$r\",\"a\",null,{\"href\":\"/reference/react/useEffect\",\"children\":[[\"$r\",\"code\",null,{\"children\":\"useEffect\"}],\".\"]}],\" However, this isn’t the best possible solution. There is a number of edge cases it doesn’t consider. For example, it assumes that when the component mounts, \",[\"$r\",\"code\",null,{\"children\":\"isOnline\"}],\" is already \",[\"$r\",\"code\",null,{\"children\":\"true\"}],\", but this may be wrong if the network already went offline. You can use the browser \",[\"$r\",\"a\",null,{\"href\":\"https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine\",\"target\":\"_blank\",\"rel\":\"nofollow noopener noreferrer\",\"children\":[\"$r\",\"code\",null,{\"children\":\"navigator.onLine\"}]}],\" API to check for that, but using it directly would not work on the server for generating the initial HTML. In short, this code could be improved.\"]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"Luckily, React 18 includes a dedicated API called \",[\"$r\",\"a\",null,{\"href\":\"/reference/react/useSyncExternalStore\",\"children\":[\"$r\",\"code\",null,{\"children\":\"useSyncExternalStore\"}]}],\" which takes care of all of these problems for you. Here is how your \",[\"$r\",\"code\",null,{\"children\":\"useOnlineStatus\"}],\" Hook, rewritten to take advantage of this new API:\"]}],\"\\n\"]}],[\"$r\",\"Sandpack\",null,{\"children\":[[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"import { useOnlineStatus } from './useOnlineStatus.js';\\n\\nfunction StatusBar() {\\n const isOnline = useOnlineStatus();\\n return \u003ch1\u003e{isOnline ? '✅ Online' : '❌ Disconnected'}\u003c/h1\u003e;\\n}\\n\\nfunction SaveButton() {\\n const isOnline = useOnlineStatus();\\n\\n function handleSaveClick() {\\n console.log('✅ Progress saved');\\n }\\n\\n return (\\n \u003cbutton disabled={!isOnline} onClick={handleSaveClick}\u003e\\n {isOnline ? 'Save progress' : 'Reconnecting...'}\\n \u003c/button\u003e\\n );\\n}\\n\\nexport default function App() {\\n return (\\n \u003c\u003e\\n \u003cSaveButton /\u003e\\n \u003cStatusBar /\u003e\\n \u003c/\u003e\\n );\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/useOnlineStatus.js active\",\"children\":\"import { useSyncExternalStore } from 'react';\\n\\nfunction subscribe(callback) {\\n window.addEventListener('online', callback);\\n window.addEventListener('offline', callback);\\n return () =\u003e {\\n window.removeEventListener('online', callback);\\n window.removeEventListener('offline', callback);\\n };\\n}\\n\\nexport function useOnlineStatus() {\\n return useSyncExternalStore(\\n subscribe,\\n () =\u003e navigator.onLine, // How to get the value on the client\\n () =\u003e true // How to get the value on the server\\n );\\n}\\n\\n\"}]}]]}],[\"$r\",\"MaxWidth\",\"210\",{\"children\":[\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"Notice how \",[\"$r\",\"strong\",null,{\"children\":\"you didn’t need to change any of the components\"}],\" to make this migration:\"]}],\"\\n\",[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"{2,7}\",\"children\":\"function StatusBar() {\\n const isOnline = useOnlineStatus();\\n // ...\\n}\\n\\nfunction SaveButton() {\\n const isOnline = useOnlineStatus();\\n // ...\\n}\\n\"}]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":\"This is another reason for why wrapping Effects in custom Hooks is often beneficial:\"}],\"\\n\",[\"$r\",\"ol\",null,{\"children\":[\"\\n\",[\"$r\",\"li\",null,{\"children\":\"You make the data flow to and from your Effects very explicit.\"}],\"\\n\",[\"$r\",\"li\",null,{\"children\":\"You let your components focus on the intent rather than on the exact implementation of your Effects.\"}],\"\\n\",[\"$r\",\"li\",null,{\"children\":\"When React adds new features, you can remove those Effects without changing any of your components.\"}],\"\\n\"]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"Similar to a \",[\"$r\",\"a\",null,{\"href\":\"https://uxdesign.cc/everything-you-need-to-know-about-design-systems-54b109851969\",\"target\":\"_blank\",\"rel\":\"nofollow noopener noreferrer\",\"children\":\"design system,\"}],\" you might find it helpful to start extracting common idioms from your app’s components into custom Hooks. This will keep your components’ code focused on the intent, and let you avoid writing raw Effects very often. Many excellent custom Hooks are maintained by the React community.\"]}],\"\\n\",[\"$r\",\"DeepDive\",null,{\"children\":[[\"$r\",\"h4\",null,{\"id\":\"will-react-provide-any-built-in-solution-for-data-fetching\",\"children\":\"Will React provide any built-in solution for data fetching? \"}],[\"$r\",\"p\",null,{\"children\":\"We’re still working out the details, but we expect that in the future, you’ll write data fetching like this:\"}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"{1,4,6}\",\"children\":\"import { use } from 'react'; // Not available yet!\\n\\nfunction ShippingForm({ country }) {\\n const cities = use(fetch(`/api/cities?country=${country}`));\\n const [city, setCity] = useState(null);\\n const areas = city ? use(fetch(`/api/areas?city=${city}`)) : null;\\n // ...\\n\"}]}],[\"$r\",\"p\",null,{\"children\":[\"If you use custom Hooks like \",[\"$r\",\"code\",null,{\"children\":\"useData\"}],\" above in your app, it will require fewer changes to migrate to the eventually recommended approach than if you write raw Effects in every component manually. However, the old approach will still work fine, so if you feel happy writing raw Effects, you can continue to do that.\"]}]]}],\"\\n\",[\"$r\",\"h3\",null,{\"id\":\"there-is-more-than-one-way-to-do-it\",\"children\":\"There is more than one way to do it \"}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"Let’s say you want to implement a fade-in animation \",[\"$r\",\"em\",null,{\"children\":\"from scratch\"}],\" using the browser \",[\"$r\",\"a\",null,{\"href\":\"https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame\",\"target\":\"_blank\",\"rel\":\"nofollow noopener noreferrer\",\"children\":[\"$r\",\"code\",null,{\"children\":\"requestAnimationFrame\"}]}],\" API. You might start with an Effect that sets up an animation loop. During each frame of the animation, you could change the opacity of the DOM node you \",[\"$r\",\"a\",null,{\"href\":\"/learn/manipulating-the-dom-with-refs\",\"children\":\"hold in a ref\"}],\" until it reaches \",[\"$r\",\"code\",null,{\"children\":\"1\"}],\". Your code might start like this:\"]}],\"\\n\"]}],[\"$r\",\"Sandpack\",null,{\"children\":[[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"import { useState, useEffect, useRef } from 'react';\\n\\nfunction Welcome() {\\n const ref = useRef(null);\\n\\n useEffect(() =\u003e {\\n const duration = 1000;\\n const node = ref.current;\\n\\n let startTime = performance.now();\\n let frameId = null;\\n\\n function onFrame(now) {\\n const timePassed = now - startTime;\\n const progress = Math.min(timePassed / duration, 1);\\n onProgress(progress);\\n if (progress \u003c 1) {\\n // We still have more frames to paint\\n frameId = requestAnimationFrame(onFrame);\\n }\\n }\\n\\n function onProgress(progress) {\\n node.style.opacity = progress;\\n }\\n\\n function start() {\\n onProgress(0);\\n startTime = performance.now();\\n frameId = requestAnimationFrame(onFrame);\\n }\\n\\n function stop() {\\n cancelAnimationFrame(frameId);\\n startTime = null;\\n frameId = null;\\n }\\n\\n start();\\n return () =\u003e stop();\\n }, []);\\n\\n return (\\n \u003ch1 className=\\\"welcome\\\" ref={ref}\u003e\\n Welcome\\n \u003c/h1\u003e\\n );\\n}\\n\\nexport default function App() {\\n const [show, setShow] = useState(false);\\n return (\\n \u003c\u003e\\n \u003cbutton onClick={() =\u003e setShow(!show)}\u003e\\n {show ? 'Remove' : 'Show'}\\n \u003c/button\u003e\\n \u003chr /\u003e\\n {show \u0026\u0026 \u003cWelcome /\u003e}\\n \u003c/\u003e\\n );\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-css\",\"children\":\"label, button { display: block; margin-bottom: 20px; }\\nhtml, body { min-height: 300px; }\\n.welcome {\\n opacity: 0;\\n color: white;\\n padding: 50px;\\n text-align: center;\\n font-size: 50px;\\n background-image: radial-gradient(circle, rgba(63,94,251,1) 0%, rgba(252,70,107,1) 100%);\\n}\\n\"}]}]]}],[\"$r\",\"MaxWidth\",\"214\",{\"children\":[\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"To make the component more readable, you might extract the logic into a \",[\"$r\",\"code\",null,{\"children\":\"useFadeIn\"}],\" custom Hook:\"]}],\"\\n\"]}],[\"$r\",\"Sandpack\",null,{\"children\":[[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"import { useState, useEffect, useRef } from 'react';\\nimport { useFadeIn } from './useFadeIn.js';\\n\\nfunction Welcome() {\\n const ref = useRef(null);\\n\\n useFadeIn(ref, 1000);\\n\\n return (\\n \u003ch1 className=\\\"welcome\\\" ref={ref}\u003e\\n Welcome\\n \u003c/h1\u003e\\n );\\n}\\n\\nexport default function App() {\\n const [show, setShow] = useState(false);\\n return (\\n \u003c\u003e\\n \u003cbutton onClick={() =\u003e setShow(!show)}\u003e\\n {show ? 'Remove' : 'Show'}\\n \u003c/button\u003e\\n \u003chr /\u003e\\n {show \u0026\u0026 \u003cWelcome /\u003e}\\n \u003c/\u003e\\n );\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/useFadeIn.js\",\"children\":\"import { useEffect } from 'react';\\n\\nexport function useFadeIn(ref, duration) {\\n useEffect(() =\u003e {\\n const node = ref.current;\\n\\n let startTime = performance.now();\\n let frameId = null;\\n\\n function onFrame(now) {\\n const timePassed = now - startTime;\\n const progress = Math.min(timePassed / duration, 1);\\n onProgress(progress);\\n if (progress \u003c 1) {\\n // We still have more frames to paint\\n frameId = requestAnimationFrame(onFrame);\\n }\\n }\\n\\n function onProgress(progress) {\\n node.style.opacity = progress;\\n }\\n\\n function start() {\\n onProgress(0);\\n startTime = performance.now();\\n frameId = requestAnimationFrame(onFrame);\\n }\\n\\n function stop() {\\n cancelAnimationFrame(frameId);\\n startTime = null;\\n frameId = null;\\n }\\n\\n start();\\n return () =\u003e stop();\\n }, [ref, duration]);\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-css\",\"children\":\"label, button { display: block; margin-bottom: 20px; }\\nhtml, body { min-height: 300px; }\\n.welcome {\\n opacity: 0;\\n color: white;\\n padding: 50px;\\n text-align: center;\\n font-size: 50px;\\n background-image: radial-gradient(circle, rgba(63,94,251,1) 0%, rgba(252,70,107,1) 100%);\\n}\\n\"}]}]]}],[\"$r\",\"MaxWidth\",\"218\",{\"children\":[\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"You could keep the \",[\"$r\",\"code\",null,{\"children\":\"useFadeIn\"}],\" code as is, but you could also refactor it more. For example, you could extract the logic for setting up the animation loop out of \",[\"$r\",\"code\",null,{\"children\":\"useFadeIn\"}],\" into a custom \",[\"$r\",\"code\",null,{\"children\":\"useAnimationLoop\"}],\" Hook:\"]}],\"\\n\"]}],[\"$r\",\"Sandpack\",null,{\"children\":[[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"import { useState, useEffect, useRef } from 'react';\\nimport { useFadeIn } from './useFadeIn.js';\\n\\nfunction Welcome() {\\n const ref = useRef(null);\\n\\n useFadeIn(ref, 1000);\\n\\n return (\\n \u003ch1 className=\\\"welcome\\\" ref={ref}\u003e\\n Welcome\\n \u003c/h1\u003e\\n );\\n}\\n\\nexport default function App() {\\n const [show, setShow] = useState(false);\\n return (\\n \u003c\u003e\\n \u003cbutton onClick={() =\u003e setShow(!show)}\u003e\\n {show ? 'Remove' : 'Show'}\\n \u003c/button\u003e\\n \u003chr /\u003e\\n {show \u0026\u0026 \u003cWelcome /\u003e}\\n \u003c/\u003e\\n );\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/useFadeIn.js active\",\"children\":\"import { useState, useEffect } from 'react';\\nimport { experimental_useEffectEvent as useEffectEvent } from 'react';\\n\\nexport function useFadeIn(ref, duration) {\\n const [isRunning, setIsRunning] = useState(true);\\n\\n useAnimationLoop(isRunning, (timePassed) =\u003e {\\n const progress = Math.min(timePassed / duration, 1);\\n ref.current.style.opacity = progress;\\n if (progress === 1) {\\n setIsRunning(false);\\n }\\n });\\n}\\n\\nfunction useAnimationLoop(isRunning, drawFrame) {\\n const onFrame = useEffectEvent(drawFrame);\\n\\n useEffect(() =\u003e {\\n if (!isRunning) {\\n return;\\n }\\n\\n const startTime = performance.now();\\n let frameId = null;\\n\\n function tick(now) {\\n const timePassed = now - startTime;\\n onFrame(timePassed);\\n frameId = requestAnimationFrame(tick);\\n }\\n\\n tick();\\n return () =\u003e cancelAnimationFrame(frameId);\\n }, [isRunning]);\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-css\",\"children\":\"label, button { display: block; margin-bottom: 20px; }\\nhtml, body { min-height: 300px; }\\n.welcome {\\n opacity: 0;\\n color: white;\\n padding: 50px;\\n text-align: center;\\n font-size: 50px;\\n background-image: radial-gradient(circle, rgba(63,94,251,1) 0%, rgba(252,70,107,1) 100%);\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-json\",\"meta\":\"package.json hidden\",\"children\":\"{\\n \\\"dependencies\\\": {\\n \\\"react\\\": \\\"experimental\\\",\\n \\\"react-dom\\\": \\\"experimental\\\",\\n \\\"react-scripts\\\": \\\"latest\\\"\\n },\\n \\\"scripts\\\": {\\n \\\"start\\\": \\\"react-scripts start\\\",\\n \\\"build\\\": \\\"react-scripts build\\\",\\n \\\"test\\\": \\\"react-scripts test --env=jsdom\\\",\\n \\\"eject\\\": \\\"react-scripts eject\\\"\\n }\\n}\\n\"}]}]]}],[\"$r\",\"MaxWidth\",\"222\",{\"children\":[\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"However, you didn’t \",[\"$r\",\"em\",null,{\"children\":\"have to\"}],\" do that. As with regular functions, ultimately you decide where to draw the boundaries between different parts of your code. You could also take a very different approach. Instead of keeping the logic in the Effect, you could move most of the imperative logic inside a JavaScript \",[\"$r\",\"a\",null,{\"href\":\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes\",\"target\":\"_blank\",\"rel\":\"nofollow noopener noreferrer\",\"children\":\"class:\"}]]}],\"\\n\"]}],[\"$r\",\"Sandpack\",null,{\"children\":[[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"import { useState, useEffect, useRef } from 'react';\\nimport { useFadeIn } from './useFadeIn.js';\\n\\nfunction Welcome() {\\n const ref = useRef(null);\\n\\n useFadeIn(ref, 1000);\\n\\n return (\\n \u003ch1 className=\\\"welcome\\\" ref={ref}\u003e\\n Welcome\\n \u003c/h1\u003e\\n );\\n}\\n\\nexport default function App() {\\n const [show, setShow] = useState(false);\\n return (\\n \u003c\u003e\\n \u003cbutton onClick={() =\u003e setShow(!show)}\u003e\\n {show ? 'Remove' : 'Show'}\\n \u003c/button\u003e\\n \u003chr /\u003e\\n {show \u0026\u0026 \u003cWelcome /\u003e}\\n \u003c/\u003e\\n );\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/useFadeIn.js active\",\"children\":\"import { useState, useEffect } from 'react';\\nimport { FadeInAnimation } from './animation.js';\\n\\nexport function useFadeIn(ref, duration) {\\n useEffect(() =\u003e {\\n const animation = new FadeInAnimation(ref.current);\\n animation.start(duration);\\n return () =\u003e {\\n animation.stop();\\n };\\n }, [ref, duration]);\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/animation.js\",\"children\":\"export class FadeInAnimation {\\n constructor(node) {\\n this.node = node;\\n }\\n start(duration) {\\n this.duration = duration;\\n this.onProgress(0);\\n this.startTime = performance.now();\\n this.frameId = requestAnimationFrame(() =\u003e this.onFrame());\\n }\\n onFrame() {\\n const timePassed = performance.now() - this.startTime;\\n const progress = Math.min(timePassed / this.duration, 1);\\n this.onProgress(progress);\\n if (progress === 1) {\\n this.stop();\\n } else {\\n // We still have more frames to paint\\n this.frameId = requestAnimationFrame(() =\u003e this.onFrame());\\n }\\n }\\n onProgress(progress) {\\n this.node.style.opacity = progress;\\n }\\n stop() {\\n cancelAnimationFrame(this.frameId);\\n this.startTime = null;\\n this.frameId = null;\\n this.duration = 0;\\n }\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-css\",\"children\":\"label, button { display: block; margin-bottom: 20px; }\\nhtml, body { min-height: 300px; }\\n.welcome {\\n opacity: 0;\\n color: white;\\n padding: 50px;\\n text-align: center;\\n font-size: 50px;\\n background-image: radial-gradient(circle, rgba(63,94,251,1) 0%, rgba(252,70,107,1) 100%);\\n}\\n\"}]}]]}],[\"$r\",\"MaxWidth\",\"228\",{\"children\":[\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"Effects let you connect React to external systems. The more coordination between Effects is needed (for example, to chain multiple animations), the more it makes sense to extract that logic out of Effects and Hooks \",[\"$r\",\"em\",null,{\"children\":\"completely\"}],\" like in the sandbox above. Then, the code you extracted \",[\"$r\",\"em\",null,{\"children\":\"becomes\"}],\" the “external system”. This lets your Effects stay simple because they only need to send messages to the system you’ve moved outside React.\"]}],\"\\n\",[\"$r\",\"p\",null,{\"children\":[\"The examples above assume that the fade-in logic needs to be written in JavaScript. However, this particular fade-in animation is both simpler and much more efficient to implement with a plain \",[\"$r\",\"a\",null,{\"href\":\"https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations\",\"target\":\"_blank\",\"rel\":\"nofollow noopener noreferrer\",\"children\":\"CSS Animation:\"}]]}],\"\\n\"]}],[\"$r\",\"Sandpack\",null,{\"children\":[[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"import { useState, useEffect, useRef } from 'react';\\nimport './welcome.css';\\n\\nfunction Welcome() {\\n return (\\n \u003ch1 className=\\\"welcome\\\"\u003e\\n Welcome\\n \u003c/h1\u003e\\n );\\n}\\n\\nexport default function App() {\\n const [show, setShow] = useState(false);\\n return (\\n \u003c\u003e\\n \u003cbutton onClick={() =\u003e setShow(!show)}\u003e\\n {show ? 'Remove' : 'Show'}\\n \u003c/button\u003e\\n \u003chr /\u003e\\n {show \u0026\u0026 \u003cWelcome /\u003e}\\n \u003c/\u003e\\n );\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-css\",\"meta\":\"src/styles.css\",\"children\":\"label, button { display: block; margin-bottom: 20px; }\\nhtml, body { min-height: 300px; }\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-css\",\"meta\":\"src/welcome.css active\",\"children\":\".welcome {\\n color: white;\\n padding: 50px;\\n text-align: center;\\n font-size: 50px;\\n background-image: radial-gradient(circle, rgba(63,94,251,1) 0%, rgba(252,70,107,1) 100%);\\n\\n animation: fadeIn 1000ms;\\n}\\n\\n@keyframes fadeIn {\\n 0% { opacity: 0; }\\n 100% { opacity: 1; }\\n}\\n\\n\"}]}]]}],[\"$r\",\"MaxWidth\",\"234\",{\"children\":[\"\\n\",[\"$r\",\"p\",null,{\"children\":\"Sometimes, you don’t even need a Hook!\"}],\"\\n\",[\"$r\",\"Recap\",null,{\"children\":[\"$r\",\"ul\",null,{\"children\":[\"\\n\",[\"$r\",\"li\",null,{\"children\":\"Custom Hooks let you share logic between components.\"}],\"\\n\",[\"$r\",\"li\",null,{\"children\":[\"Custom Hooks must be named starting with \",[\"$r\",\"code\",null,{\"children\":\"use\"}],\" followed by a capital letter.\"]}],\"\\n\",[\"$r\",\"li\",null,{\"children\":\"Custom Hooks only share stateful logic, not state itself.\"}],\"\\n\",[\"$r\",\"li\",null,{\"children\":\"You can pass reactive values from one Hook to another, and they stay up-to-date.\"}],\"\\n\",[\"$r\",\"li\",null,{\"children\":\"All Hooks re-run every time your component re-renders.\"}],\"\\n\",[\"$r\",\"li\",null,{\"children\":\"The code of your custom Hooks should be pure, like your component’s code.\"}],\"\\n\",[\"$r\",\"li\",null,{\"children\":\"Wrap event handlers received by custom Hooks into Effect Events.\"}],\"\\n\",[\"$r\",\"li\",null,{\"children\":[\"Don’t create custom Hooks like \",[\"$r\",\"code\",null,{\"children\":\"useMount\"}],\". Keep their purpose specific.\"]}],\"\\n\",[\"$r\",\"li\",null,{\"children\":\"It’s up to you how and where to choose the boundaries of your code.\"}],\"\\n\"]}]}],\"\\n\"]}],[\"$r\",\"Challenges\",null,{\"children\":[[\"$r\",\"h4\",null,{\"id\":\"extract-a-usecounter-hook\",\"children\":[\"Extract a \",[\"$r\",\"code\",null,{\"children\":\"useCounter\"}],\" Hook \"]}],[\"$r\",\"p\",null,{\"children\":[\"This component uses a state variable and an Effect to display a number that increments every second. Extract this logic into a custom Hook called \",[\"$r\",\"code\",null,{\"children\":\"useCounter\"}],\". Your goal is to make the \",[\"$r\",\"code\",null,{\"children\":\"Counter\"}],\" component implementation look exactly like this:\"]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"export default function Counter() {\\n const count = useCounter();\\n return \u003ch1\u003eSeconds passed: {count}\u003c/h1\u003e;\\n}\\n\"}]}],[\"$r\",\"p\",null,{\"children\":[\"You’ll need to write your custom Hook in \",[\"$r\",\"code\",null,{\"children\":\"useCounter.js\"}],\" and import it into the \",[\"$r\",\"code\",null,{\"children\":\"App.js\"}],\" file.\"]}],[\"$r\",\"Sandpack\",null,{\"children\":[[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"import { useState, useEffect } from 'react';\\n\\nexport default function Counter() {\\n const [count, setCount] = useState(0);\\n useEffect(() =\u003e {\\n const id = setInterval(() =\u003e {\\n setCount(c =\u003e c + 1);\\n }, 1000);\\n return () =\u003e clearInterval(id);\\n }, []);\\n return \u003ch1\u003eSeconds passed: {count}\u003c/h1\u003e;\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/useCounter.js\",\"children\":\"// Write your custom Hook in this file!\\n\"}]}]]}],[\"$r\",\"Solution\",null,{\"children\":[[\"$r\",\"p\",null,{\"children\":\"Your code should look like this:\"}],[\"$r\",\"Sandpack\",null,{\"children\":[[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"import { useCounter } from './useCounter.js';\\n\\nexport default function Counter() {\\n const count = useCounter();\\n return \u003ch1\u003eSeconds passed: {count}\u003c/h1\u003e;\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/useCounter.js\",\"children\":\"import { useState, useEffect } from 'react';\\n\\nexport function useCounter() {\\n const [count, setCount] = useState(0);\\n useEffect(() =\u003e {\\n const id = setInterval(() =\u003e {\\n setCount(c =\u003e c + 1);\\n }, 1000);\\n return () =\u003e clearInterval(id);\\n }, []);\\n return count;\\n}\\n\"}]}]]}],[\"$r\",\"p\",null,{\"children\":[\"Notice that \",[\"$r\",\"code\",null,{\"children\":\"App.js\"}],\" doesn’t need to import \",[\"$r\",\"code\",null,{\"children\":\"useState\"}],\" or \",[\"$r\",\"code\",null,{\"children\":\"useEffect\"}],\" anymore.\"]}]]}],[\"$r\",\"h4\",null,{\"id\":\"make-the-counter-delay-configurable\",\"children\":\"Make the counter delay configurable \"}],[\"$r\",\"p\",null,{\"children\":[\"In this example, there is a \",[\"$r\",\"code\",null,{\"children\":\"delay\"}],\" state variable controlled by a slider, but its value is not used. Pass the \",[\"$r\",\"code\",null,{\"children\":\"delay\"}],\" value to your custom \",[\"$r\",\"code\",null,{\"children\":\"useCounter\"}],\" Hook, and change the \",[\"$r\",\"code\",null,{\"children\":\"useCounter\"}],\" Hook to use the passed \",[\"$r\",\"code\",null,{\"children\":\"delay\"}],\" instead of hardcoding \",[\"$r\",\"code\",null,{\"children\":\"1000\"}],\" ms.\"]}],[\"$r\",\"Sandpack\",null,{\"children\":[[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"import { useState } from 'react';\\nimport { useCounter } from './useCounter.js';\\n\\nexport default function Counter() {\\n const [delay, setDelay] = useState(1000);\\n const count = useCounter();\\n return (\\n \u003c\u003e\\n \u003clabel\u003e\\n Tick duration: {delay} ms\\n \u003cbr /\u003e\\n \u003cinput\\n type=\\\"range\\\"\\n value={delay}\\n min=\\\"10\\\"\\n max=\\\"2000\\\"\\n onChange={e =\u003e setDelay(Number(e.target.value))}\\n /\u003e\\n \u003c/label\u003e\\n \u003chr /\u003e\\n \u003ch1\u003eTicks: {count}\u003c/h1\u003e\\n \u003c/\u003e\\n );\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/useCounter.js\",\"children\":\"import { useState, useEffect } from 'react';\\n\\nexport function useCounter() {\\n const [count, setCount] = useState(0);\\n useEffect(() =\u003e {\\n const id = setInterval(() =\u003e {\\n setCount(c =\u003e c + 1);\\n }, 1000);\\n return () =\u003e clearInterval(id);\\n }, []);\\n return count;\\n}\\n\"}]}]]}],[\"$r\",\"Solution\",null,{\"children\":[[\"$r\",\"p\",null,{\"children\":[\"Pass the \",[\"$r\",\"code\",null,{\"children\":\"delay\"}],\" to your Hook with \",[\"$r\",\"code\",null,{\"children\":\"useCounter(delay)\"}],\". Then, inside the Hook, use \",[\"$r\",\"code\",null,{\"children\":\"delay\"}],\" instead of the hardcoded \",[\"$r\",\"code\",null,{\"children\":\"1000\"}],\" value. You’ll need to add \",[\"$r\",\"code\",null,{\"children\":\"delay\"}],\" to your Effect’s dependencies. This ensures that a change in \",[\"$r\",\"code\",null,{\"children\":\"delay\"}],\" will reset the interval.\"]}],[\"$r\",\"Sandpack\",null,{\"children\":[[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"import { useState } from 'react';\\nimport { useCounter } from './useCounter.js';\\n\\nexport default function Counter() {\\n const [delay, setDelay] = useState(1000);\\n const count = useCounter(delay);\\n return (\\n \u003c\u003e\\n \u003clabel\u003e\\n Tick duration: {delay} ms\\n \u003cbr /\u003e\\n \u003cinput\\n type=\\\"range\\\"\\n value={delay}\\n min=\\\"10\\\"\\n max=\\\"2000\\\"\\n onChange={e =\u003e setDelay(Number(e.target.value))}\\n /\u003e\\n \u003c/label\u003e\\n \u003chr /\u003e\\n \u003ch1\u003eTicks: {count}\u003c/h1\u003e\\n \u003c/\u003e\\n );\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/useCounter.js\",\"children\":\"import { useState, useEffect } from 'react';\\n\\nexport function useCounter(delay) {\\n const [count, setCount] = useState(0);\\n useEffect(() =\u003e {\\n const id = setInterval(() =\u003e {\\n setCount(c =\u003e c + 1);\\n }, delay);\\n return () =\u003e clearInterval(id);\\n }, [delay]);\\n return count;\\n}\\n\"}]}]]}]]}],[\"$r\",\"h4\",null,{\"id\":\"extract-useinterval-out-of-usecounter\",\"children\":[\"Extract \",[\"$r\",\"code\",null,{\"children\":\"useInterval\"}],\" out of \",[\"$r\",\"code\",null,{\"children\":\"useCounter\"}],\" \"]}],[\"$r\",\"p\",null,{\"children\":[\"Currently, your \",[\"$r\",\"code\",null,{\"children\":\"useCounter\"}],\" Hook does two things. It sets up an interval, and it also increments a state variable on every interval tick. Split out the logic that sets up the interval into a separate Hook called \",[\"$r\",\"code\",null,{\"children\":\"useInterval\"}],\". It should take two arguments: the \",[\"$r\",\"code\",null,{\"children\":\"onTick\"}],\" callback, and the \",[\"$r\",\"code\",null,{\"children\":\"delay\"}],\". After this change, your \",[\"$r\",\"code\",null,{\"children\":\"useCounter\"}],\" implementation should look like this:\"]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"export function useCounter(delay) {\\n const [count, setCount] = useState(0);\\n useInterval(() =\u003e {\\n setCount(c =\u003e c + 1);\\n }, delay);\\n return count;\\n}\\n\"}]}],[\"$r\",\"p\",null,{\"children\":[\"Write \",[\"$r\",\"code\",null,{\"children\":\"useInterval\"}],\" in the \",[\"$r\",\"code\",null,{\"children\":\"useInterval.js\"}],\" file and import it into the \",[\"$r\",\"code\",null,{\"children\":\"useCounter.js\"}],\" file.\"]}],[\"$r\",\"Sandpack\",null,{\"children\":[[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"import { useState } from 'react';\\nimport { useCounter } from './useCounter.js';\\n\\nexport default function Counter() {\\n const count = useCounter(1000);\\n return \u003ch1\u003eSeconds passed: {count}\u003c/h1\u003e;\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/useCounter.js\",\"children\":\"import { useState, useEffect } from 'react';\\n\\nexport function useCounter(delay) {\\n const [count, setCount] = useState(0);\\n useEffect(() =\u003e {\\n const id = setInterval(() =\u003e {\\n setCount(c =\u003e c + 1);\\n }, delay);\\n return () =\u003e clearInterval(id);\\n }, [delay]);\\n return count;\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/useInterval.js\",\"children\":\"// Write your Hook here!\\n\"}]}]]}],[\"$r\",\"Solution\",null,{\"children\":[[\"$r\",\"p\",null,{\"children\":[\"The logic inside \",[\"$r\",\"code\",null,{\"children\":\"useInterval\"}],\" should set up and clear the interval. It doesn’t need to do anything else.\"]}],[\"$r\",\"Sandpack\",null,{\"children\":[[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"import { useCounter } from './useCounter.js';\\n\\nexport default function Counter() {\\n const count = useCounter(1000);\\n return \u003ch1\u003eSeconds passed: {count}\u003c/h1\u003e;\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/useCounter.js\",\"children\":\"import { useState } from 'react';\\nimport { useInterval } from './useInterval.js';\\n\\nexport function useCounter(delay) {\\n const [count, setCount] = useState(0);\\n useInterval(() =\u003e {\\n setCount(c =\u003e c + 1);\\n }, delay);\\n return count;\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/useInterval.js active\",\"children\":\"import { useEffect } from 'react';\\n\\nexport function useInterval(onTick, delay) {\\n useEffect(() =\u003e {\\n const id = setInterval(onTick, delay);\\n return () =\u003e clearInterval(id);\\n }, [onTick, delay]);\\n}\\n\"}]}]]}],[\"$r\",\"p\",null,{\"children\":\"Note that there is a bit of a problem with this solution, which you’ll solve in the next challenge.\"}]]}],[\"$r\",\"h4\",null,{\"id\":\"fix-a-resetting-interval\",\"children\":\"Fix a resetting interval \"}],[\"$r\",\"p\",null,{\"children\":[\"In this example, there are \",[\"$r\",\"em\",null,{\"children\":\"two\"}],\" separate intervals.\"]}],[\"$r\",\"p\",null,{\"children\":[\"The \",[\"$r\",\"code\",null,{\"children\":\"App\"}],\" component calls \",[\"$r\",\"code\",null,{\"children\":\"useCounter\"}],\", which calls \",[\"$r\",\"code\",null,{\"children\":\"useInterval\"}],\" to update the counter every second. But the \",[\"$r\",\"code\",null,{\"children\":\"App\"}],\" component \",[\"$r\",\"em\",null,{\"children\":\"also\"}],\" calls \",[\"$r\",\"code\",null,{\"children\":\"useInterval\"}],\" to randomly update the page background color every two seconds.\"]}],[\"$r\",\"p\",null,{\"children\":[\"For some reason, the callback that updates the page background never runs. Add some logs inside \",[\"$r\",\"code\",null,{\"children\":\"useInterval\"}],\":\"]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"{2,5}\",\"children\":\" useEffect(() =\u003e {\\n console.log('✅ Setting up an interval with delay ', delay)\\n const id = setInterval(onTick, delay);\\n return () =\u003e {\\n console.log('❌ Clearing an interval with delay ', delay)\\n clearInterval(id);\\n };\\n }, [onTick, delay]);\\n\"}]}],[\"$r\",\"p\",null,{\"children\":[\"Do the logs match what you expect to happen? If some of your Effects seem to re-synchronize unnecessarily, can you guess which dependency is causing that to happen? Is there some way to \",[\"$r\",\"a\",null,{\"href\":\"/learn/removing-effect-dependencies\",\"children\":\"remove that dependency\"}],\" from your Effect?\"]}],[\"$r\",\"p\",null,{\"children\":\"After you fix the issue, you should expect the page background to update every two seconds.\"}],[\"$r\",\"Hint\",null,{\"children\":[\"$r\",\"p\",null,{\"children\":[\"It looks like your \",[\"$r\",\"code\",null,{\"children\":\"useInterval\"}],\" Hook accepts an event listener as an argument. Can you think of some way to wrap that event listener so that it doesn’t need to be a dependency of your Effect?\"]}]}],[\"$r\",\"Sandpack\",null,{\"children\":[[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-json\",\"meta\":\"package.json hidden\",\"children\":\"{\\n \\\"dependencies\\\": {\\n \\\"react\\\": \\\"experimental\\\",\\n \\\"react-dom\\\": \\\"experimental\\\",\\n \\\"react-scripts\\\": \\\"latest\\\"\\n },\\n \\\"scripts\\\": {\\n \\\"start\\\": \\\"react-scripts start\\\",\\n \\\"build\\\": \\\"react-scripts build\\\",\\n \\\"test\\\": \\\"react-scripts test --env=jsdom\\\",\\n \\\"eject\\\": \\\"react-scripts eject\\\"\\n }\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"import { useCounter } from './useCounter.js';\\nimport { useInterval } from './useInterval.js';\\n\\nexport default function Counter() {\\n const count = useCounter(1000);\\n\\n useInterval(() =\u003e {\\n const randomColor = `hsla(${Math.random() * 360}, 100%, 50%, 0.2)`;\\n document.body.style.backgroundColor = randomColor;\\n }, 2000);\\n\\n return \u003ch1\u003eSeconds passed: {count}\u003c/h1\u003e;\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/useCounter.js\",\"children\":\"import { useState } from 'react';\\nimport { useInterval } from './useInterval.js';\\n\\nexport function useCounter(delay) {\\n const [count, setCount] = useState(0);\\n useInterval(() =\u003e {\\n setCount(c =\u003e c + 1);\\n }, delay);\\n return count;\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/useInterval.js\",\"children\":\"import { useEffect } from 'react';\\nimport { experimental_useEffectEvent as useEffectEvent } from 'react';\\n\\nexport function useInterval(onTick, delay) {\\n useEffect(() =\u003e {\\n const id = setInterval(onTick, delay);\\n return () =\u003e {\\n clearInterval(id);\\n };\\n }, [onTick, delay]);\\n}\\n\"}]}]]}],[\"$r\",\"Solution\",null,{\"children\":[[\"$r\",\"p\",null,{\"children\":[\"Inside \",[\"$r\",\"code\",null,{\"children\":\"useInterval\"}],\", wrap the tick callback into an Effect Event, as you did \",[\"$r\",\"a\",null,{\"href\":\"/learn/reusing-logic-with-custom-hooks#passing-event-handlers-to-custom-hooks\",\"children\":\"earlier on this page.\"}]]}],[\"$r\",\"p\",null,{\"children\":[\"This will allow you to omit \",[\"$r\",\"code\",null,{\"children\":\"onTick\"}],\" from dependencies of your Effect. The Effect won’t re-synchronize on every re-render of the component, so the page background color change interval won’t get reset every second before it has a chance to fire.\"]}],[\"$r\",\"p\",null,{\"children\":\"With this change, both intervals work as expected and don’t interfere with each other:\"}],[\"$r\",\"Sandpack\",null,{\"children\":[[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-json\",\"meta\":\"package.json hidden\",\"children\":\"{\\n \\\"dependencies\\\": {\\n \\\"react\\\": \\\"experimental\\\",\\n \\\"react-dom\\\": \\\"experimental\\\",\\n \\\"react-scripts\\\": \\\"latest\\\"\\n },\\n \\\"scripts\\\": {\\n \\\"start\\\": \\\"react-scripts start\\\",\\n \\\"build\\\": \\\"react-scripts build\\\",\\n \\\"test\\\": \\\"react-scripts test --env=jsdom\\\",\\n \\\"eject\\\": \\\"react-scripts eject\\\"\\n }\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"import { useCounter } from './useCounter.js';\\nimport { useInterval } from './useInterval.js';\\n\\nexport default function Counter() {\\n const count = useCounter(1000);\\n\\n useInterval(() =\u003e {\\n const randomColor = `hsla(${Math.random() * 360}, 100%, 50%, 0.2)`;\\n document.body.style.backgroundColor = randomColor;\\n }, 2000);\\n\\n return \u003ch1\u003eSeconds passed: {count}\u003c/h1\u003e;\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/useCounter.js\",\"children\":\"import { useState } from 'react';\\nimport { useInterval } from './useInterval.js';\\n\\nexport function useCounter(delay) {\\n const [count, setCount] = useState(0);\\n useInterval(() =\u003e {\\n setCount(c =\u003e c + 1);\\n }, delay);\\n return count;\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/useInterval.js active\",\"children\":\"import { useEffect } from 'react';\\nimport { experimental_useEffectEvent as useEffectEvent } from 'react';\\n\\nexport function useInterval(callback, delay) {\\n const onTick = useEffectEvent(callback);\\n useEffect(() =\u003e {\\n const id = setInterval(onTick, delay);\\n return () =\u003e clearInterval(id);\\n }, [delay]);\\n}\\n\"}]}]]}]]}],[\"$r\",\"h4\",null,{\"id\":\"implement-a-staggering-movement\",\"children\":\"Implement a staggering movement \"}],[\"$r\",\"p\",null,{\"children\":[\"In this example, the \",[\"$r\",\"code\",null,{\"children\":\"usePointerPosition()\"}],\" Hook tracks the current pointer position. Try moving your cursor or your finger over the preview area and see the red dot follow your movement. Its position is saved in the \",[\"$r\",\"code\",null,{\"children\":\"pos1\"}],\" variable.\"]}],[\"$r\",\"p\",null,{\"children\":\"In fact, there are five (!) different red dots being rendered. You don’t see them because currently they all appear at the same position. This is what you need to fix. What you want to implement instead is a “staggered” movement: each dot should “follow” the previous dot’s path. For example, if you quickly move your cursor, the first dot should follow it immediately, the second dot should follow the first dot with a small delay, the third dot should follow the second dot, and so on.\"}],[\"$r\",\"p\",null,{\"children\":[\"You need to implement the \",[\"$r\",\"code\",null,{\"children\":\"useDelayedValue\"}],\" custom Hook. Its current implementation returns the \",[\"$r\",\"code\",null,{\"children\":\"value\"}],\" provided to it. Instead, you want to return the value back from \",[\"$r\",\"code\",null,{\"children\":\"delay\"}],\" milliseconds ago. You might need some state and an Effect to do this.\"]}],[\"$r\",\"p\",null,{\"children\":[\"After you implement \",[\"$r\",\"code\",null,{\"children\":\"useDelayedValue\"}],\", you should see the dots move following one another.\"]}],[\"$r\",\"Hint\",null,{\"children\":[[\"$r\",\"p\",null,{\"children\":[\"You’ll need to store the \",[\"$r\",\"code\",null,{\"children\":\"delayedValue\"}],\" as a state variable inside your custom Hook. When the \",[\"$r\",\"code\",null,{\"children\":\"value\"}],\" changes, you’ll want to run an Effect. This Effect should update \",[\"$r\",\"code\",null,{\"children\":\"delayedValue\"}],\" after the \",[\"$r\",\"code\",null,{\"children\":\"delay\"}],\". You might find it helpful to call \",[\"$r\",\"code\",null,{\"children\":\"setTimeout\"}],\".\"]}],[\"$r\",\"p\",null,{\"children\":\"Does this Effect need cleanup? Why or why not?\"}]]}],[\"$r\",\"Sandpack\",null,{\"children\":[[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"import { usePointerPosition } from './usePointerPosition.js';\\n\\nfunction useDelayedValue(value, delay) {\\n // TODO: Implement this Hook\\n return value;\\n}\\n\\nexport default function Canvas() {\\n const pos1 = usePointerPosition();\\n const pos2 = useDelayedValue(pos1, 100);\\n const pos3 = useDelayedValue(pos2, 200);\\n const pos4 = useDelayedValue(pos3, 100);\\n const pos5 = useDelayedValue(pos3, 50);\\n return (\\n \u003c\u003e\\n \u003cDot position={pos1} opacity={1} /\u003e\\n \u003cDot position={pos2} opacity={0.8} /\u003e\\n \u003cDot position={pos3} opacity={0.6} /\u003e\\n \u003cDot position={pos4} opacity={0.4} /\u003e\\n \u003cDot position={pos5} opacity={0.2} /\u003e\\n \u003c/\u003e\\n );\\n}\\n\\nfunction Dot({ position, opacity }) {\\n return (\\n \u003cdiv style={{\\n position: 'absolute',\\n backgroundColor: 'pink',\\n borderRadius: '50%',\\n opacity,\\n transform: `translate(${position.x}px, ${position.y}px)`,\\n pointerEvents: 'none',\\n left: -20,\\n top: -20,\\n width: 40,\\n height: 40,\\n }} /\u003e\\n );\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/usePointerPosition.js\",\"children\":\"import { useState, useEffect } from 'react';\\n\\nexport function usePointerPosition() {\\n const [position, setPosition] = useState({ x: 0, y: 0 });\\n useEffect(() =\u003e {\\n function handleMove(e) {\\n setPosition({ x: e.clientX, y: e.clientY });\\n }\\n window.addEventListener('pointermove', handleMove);\\n return () =\u003e window.removeEventListener('pointermove', handleMove);\\n }, []);\\n return position;\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-css\",\"children\":\"body { min-height: 300px; }\\n\"}]}]]}],[\"$r\",\"Solution\",null,{\"children\":[[\"$r\",\"p\",null,{\"children\":[\"Here is a working version. You keep the \",[\"$r\",\"code\",null,{\"children\":\"delayedValue\"}],\" as a state variable. When \",[\"$r\",\"code\",null,{\"children\":\"value\"}],\" updates, your Effect schedules a timeout to update the \",[\"$r\",\"code\",null,{\"children\":\"delayedValue\"}],\". This is why the \",[\"$r\",\"code\",null,{\"children\":\"delayedValue\"}],\" always “lags behind” the actual \",[\"$r\",\"code\",null,{\"children\":\"value\"}],\".\"]}],[\"$r\",\"Sandpack\",null,{\"children\":[[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"children\":\"import { useState, useEffect } from 'react';\\nimport { usePointerPosition } from './usePointerPosition.js';\\n\\nfunction useDelayedValue(value, delay) {\\n const [delayedValue, setDelayedValue] = useState(value);\\n\\n useEffect(() =\u003e {\\n setTimeout(() =\u003e {\\n setDelayedValue(value);\\n }, delay);\\n }, [value, delay]);\\n\\n return delayedValue;\\n}\\n\\nexport default function Canvas() {\\n const pos1 = usePointerPosition();\\n const pos2 = useDelayedValue(pos1, 100);\\n const pos3 = useDelayedValue(pos2, 200);\\n const pos4 = useDelayedValue(pos3, 100);\\n const pos5 = useDelayedValue(pos3, 50);\\n return (\\n \u003c\u003e\\n \u003cDot position={pos1} opacity={1} /\u003e\\n \u003cDot position={pos2} opacity={0.8} /\u003e\\n \u003cDot position={pos3} opacity={0.6} /\u003e\\n \u003cDot position={pos4} opacity={0.4} /\u003e\\n \u003cDot position={pos5} opacity={0.2} /\u003e\\n \u003c/\u003e\\n );\\n}\\n\\nfunction Dot({ position, opacity }) {\\n return (\\n \u003cdiv style={{\\n position: 'absolute',\\n backgroundColor: 'pink',\\n borderRadius: '50%',\\n opacity,\\n transform: `translate(${position.x}px, ${position.y}px)`,\\n pointerEvents: 'none',\\n left: -20,\\n top: -20,\\n width: 40,\\n height: 40,\\n }} /\u003e\\n );\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-js\",\"meta\":\"src/usePointerPosition.js\",\"children\":\"import { useState, useEffect } from 'react';\\n\\nexport function usePointerPosition() {\\n const [position, setPosition] = useState({ x: 0, y: 0 });\\n useEffect(() =\u003e {\\n function handleMove(e) {\\n setPosition({ x: e.clientX, y: e.clientY });\\n }\\n window.addEventListener('pointermove', handleMove);\\n return () =\u003e window.removeEventListener('pointermove', handleMove);\\n }, []);\\n return position;\\n}\\n\"}]}],[\"$r\",\"pre\",null,{\"children\":[\"$r\",\"code\",null,{\"className\":\"language-css\",\"children\":\"body { min-height: 300px; }\\n\"}]}]]}],[\"$r\",\"p\",null,{\"children\":[\"Note that this Effect \",[\"$r\",\"em\",null,{\"children\":\"does not\"}],\" need cleanup. If you called \",[\"$r\",\"code\",null,{\"children\":\"clearTimeout\"}],\" in the cleanup function, then each time the \",[\"$r\",\"code\",null,{\"children\":\"value\"}],\" changes, it would reset the already scheduled timeout. To keep the movement continuous, you want all the timeouts to fire.\"]}]]}]]}]]","meta":{"title":"Reusing Logic with Custom Hooks"},"languages":null},"__N_SSG":true},"page":"/[[...markdownPath]]","query":{"markdownPath":["learn","reusing-logic-with-custom-hooks"]},"buildId":"_Fz6u7K2TvuR-_M7SFBnH","isFallback":false,"gsp":true,"scriptLoader":[]}</script></body></html>