From f5ffb3f0fb67565927fc07789a0ebe04dbac33f7 Mon Sep 17 00:00:00 2001 From: yuta-ike <38308823+yuta-ike@users.noreply.github.com> Date: Mon, 26 Jun 2023 14:55:19 +0900 Subject: [PATCH 1/4] fix: broken link (#6115) --- src/content/reference/react/useRef.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/reference/react/useRef.md b/src/content/reference/react/useRef.md index bdec1ec8d..4af7e9535 100644 --- a/src/content/reference/react/useRef.md +++ b/src/content/reference/react/useRef.md @@ -228,7 +228,7 @@ function MyComponent() { If you *have to* read [or write](/reference/react/useState#storing-information-from-previous-renders) something during rendering, [use state](/reference/react/useState) instead. -When you break these rules, your component might still work, but most of the newer features we're adding to React will rely on these expectations. Read more about [keeping your components pure.](/learn/keeping-components-pure#where-you-can-cause-side-effects) +When you break these rules, your component might still work, but most of the newer features we're adding to React will rely on these expectations. Read more about [keeping your components pure.](/learn/keeping-components-pure#where-you-_can_-cause-side-effects) From 292534e97563f13e66fe7c9f004a637c97ca51b9 Mon Sep 17 00:00:00 2001 From: Sophie Alpert Date: Fri, 30 Jun 2023 11:59:55 -0700 Subject: [PATCH 2/4] Add basic reference pages for use client and server (#5976) * Add basic reference pages for use client and server I guess this turned into a bit more of an FAQ but I think it's useful to have this written down in a canonical place. * Oops, push final tweaks --- src/content/reference/react/directives.md | 16 +++++++ src/content/reference/react/use-client.md | 57 +++++++++++++++++++++++ src/content/reference/react/use-server.md | 48 +++++++++++++++++++ src/sidebarReference.json | 14 ++++++ 4 files changed, 135 insertions(+) create mode 100644 src/content/reference/react/directives.md create mode 100644 src/content/reference/react/use-client.md create mode 100644 src/content/reference/react/use-server.md diff --git a/src/content/reference/react/directives.md b/src/content/reference/react/directives.md new file mode 100644 index 000000000..49b3d624e --- /dev/null +++ b/src/content/reference/react/directives.md @@ -0,0 +1,16 @@ +--- +title: "Directives" +--- + + + +React uses two directives to provide instructions to [bundlers compatible with React Server Components](/learn/start-a-new-react-project#bleeding-edge-react-frameworks). + + + +--- + +## Source code directives {/*source-code-directives*/} + +* [`'use client'`](/reference/react/use-client) marks source files whose components execute on the client. +* [`'use server'`](/reference/react/use-server) marks server-side functions that can be called from client-side code. \ No newline at end of file diff --git a/src/content/reference/react/use-client.md b/src/content/reference/react/use-client.md new file mode 100644 index 000000000..4fc5d7ace --- /dev/null +++ b/src/content/reference/react/use-client.md @@ -0,0 +1,57 @@ +--- +title: "'use client'" +--- + + + +These directives are needed only if you're [using React Server Components](/learn/start-a-new-react-project#bleeding-edge-react-frameworks) or building a library compatible with them. + + + + + + +`'use client'` marks source files whose components execute on the client. + + + + + +--- + +## Reference {/*reference*/} + +### `'use client'` {/*use-client*/} + +Add `'use client';` at the very top of a file to mark that the file (including any child components it uses) executes on the client, regardless of where it's imported. + +```js +'use client'; + +import { useState } from 'react'; + +export default function RichTextEditor(props) { + // ... +``` + +When a file marked `'use client'` is imported from a server component, [compatible bundlers](/learn/start-a-new-react-project#bleeding-edge-react-frameworks) will treat the import as the "cut-off point" between server-only code and client code. Components at or below this point in the module graph can use client-only React features like [`useState`](/reference/react/useState). + +#### Caveats {/*caveats*/} + +* It's not necessary to add `'use client'` to every file that uses client-only React features, only the files that are imported from server component files. `'use client'` denotes the _boundary_ between server-only and client code; any components further down the tree will automatically be executed on the client. In order to be rendered from server components, components exported from `'use client'` files must have serializable props. +* When a `'use client'` file is imported from a server file, the imported values can be rendered as a React component or passed via props to a client component. Any other use will throw an exception. +* When a `'use client'` file is imported from another client file, the directive has no effect. This allows you to write client-only components that are simultaneously usable from server and client components. +* All the code in `'use client'` file as well as any modules it imports (directly or indirectly) will become a part of the client module graph and must be sent to and executed by the client in order to be rendered by the browser. To reduce client bundle size and take full advantage of the server, move state (and the `'use client'` directives) lower in the tree when possible, and pass rendered server components [as children](/learn/passing-props-to-a-component#passing-jsx-as-children) to client components. +* Because props are serialized across the server–client boundary, note that the placement of these directives can affect the amount of data sent to the client; avoid data structures that are larger than necessary. +* Components like a `` that use neither server-only nor client-only features should generally not be marked with `'use client'`. That way, they can render exclusively on the server when used from a server component, but they'll be added to the client bundle when used from a client component. +* Libraries published to npm should include `'use client'` on exported React components that can be rendered with serializable props that use client-only React features, to allow those components to be imported and rendered by server components. Otherwise, users will need to wrap library components in their own `'use client'` files which can be cumbersome and prevents the library from moving logic to the server later. When publishing prebundled files to npm, ensure that `'use client'` source files end up in a bundle marked with `'use client'`, separate from any bundle containing exports that can be used directly on the server. +* Client components will still run as part of server-side rendering (SSR) or build-time static site generation (SSG), which act as clients to transform React components' initial render output to HTML that can be rendered before JavaScript bundles are downloaded. But they can't use server-only features like reading directly from a database. +* Directives like `'use client'` must be at the very beginning of a file, above any imports or other code (comments above directives are OK). They must be written with single or double quotes, not backticks. (The `'use xyz'` directive format somewhat resembles the `useXyz()` Hook naming convention, but the similarity is coincidental.) + +## Usage {/*usage*/} + + + +This section is incomplete. See also the [Next.js documentation for Server Components](https://beta.nextjs.org/docs/rendering/server-and-client-components). + + diff --git a/src/content/reference/react/use-server.md b/src/content/reference/react/use-server.md new file mode 100644 index 000000000..61f895cab --- /dev/null +++ b/src/content/reference/react/use-server.md @@ -0,0 +1,48 @@ +--- +title: "'use server'" +--- + + + +This section is incomplete. + +These directives are needed only if you're [using React Server Components](/learn/start-a-new-react-project#bleeding-edge-react-frameworks) or building a library compatible with them. + + + + + + +`'use server'` marks server-side functions that can be called from client-side code. + + + + + +--- + +## Reference {/*reference*/} + +### `'use server'` {/*use-server*/} + +Add `'use server';` at the very top of an async function to mark that the function can be executed by the client. + +```js +async function addToCart(data) { + 'use server'; + // ... +} + +// +``` + +This function can be passed to the client. When called on the client, it will make a network request to the server that includes a serialized copy of any arguments passed. If the server function returns a value, that value will be serialized and returned to the client. + +Alternatively, add `'use server';` at the very top of a file to mark all exports within that file as async server functions that can be used anywhere, including imported in client component files. + +#### Caveats {/*caveats*/} + +* Remember that parameters to functions marked with `'use server'` are fully client-controlled. For security, always treat them as untrusted input, making sure to validate and escape the arguments as appropriate. +* To avoid the confusion that might result from mixing client- and server-side code in the same file, `'use server'` can only be used in server-side files; the resulting functions can be passed to client components through props. +* Because the underlying network calls are always asynchronous, `'use server'` can be used only on async functions. +* Directives like `'use server'` must be at the very beginning of their function or file, above any other code including imports (comments above directives are OK). They must be written with single or double quotes, not backticks. (The `'use xyz'` directive format somewhat resembles the `useXyz()` Hook naming convention, but the similarity is coincidental.) diff --git a/src/sidebarReference.json b/src/sidebarReference.json index f009cca0e..5809e8fb6 100644 --- a/src/sidebarReference.json +++ b/src/sidebarReference.json @@ -120,6 +120,20 @@ } ] }, + { + "title": "Directives", + "path": "/reference/react/directives", + "routes": [ + { + "title": "'use client'", + "path": "/reference/react/use-client" + }, + { + "title": "'use server'", + "path": "/reference/react/use-server" + } + ] + }, { "hasSectionHeader": true, "sectionHeader": "react-dom@18.2.0" From a30e1f954dce2a67985caa8b2376a1839dbfa6cf Mon Sep 17 00:00:00 2001 From: Brandon Dail Date: Mon, 10 Jul 2023 03:26:39 -0500 Subject: [PATCH 3/4] update link to fault tolerance blog post (#6142) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit this is the canonical link now 🫡 --- src/content/reference/react/Component.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/reference/react/Component.md b/src/content/reference/react/Component.md index 006ade5d8..569bf19f2 100644 --- a/src/content/reference/react/Component.md +++ b/src/content/reference/react/Component.md @@ -1393,7 +1393,7 @@ Then you can wrap a part of your component tree with it: If `Profile` or its child component throws an error, `ErrorBoundary` will "catch" that error, display a fallback UI with the error message you've provided, and send a production error report to your error reporting service. -You don't need to wrap every component into a separate error boundary. When you think about the [granularity of error boundaries,](https://aweary.dev/fault-tolerance-react/) consider where it makes sense to display an error message. For example, in a messaging app, it makes sense to place an error boundary around the list of conversations. It also makes sense to place one around every individual message. However, it wouldn't make sense to place a boundary around every avatar. +You don't need to wrap every component into a separate error boundary. When you think about the [granularity of error boundaries,](https://www.brandondail.com/posts/fault-tolerance-react) consider where it makes sense to display an error message. For example, in a messaging app, it makes sense to place an error boundary around the list of conversations. It also makes sense to place one around every individual message. However, it wouldn't make sense to place a boundary around every avatar. From b9eea4da28db66591ae5935898f98acdf009a0ad Mon Sep 17 00:00:00 2001 From: Ricky Date: Fri, 21 Jul 2023 19:24:36 -0400 Subject: [PATCH 4/4] Fix lint warnings (#6174) --- .eslintrc | 3 +- src/components/Layout/HomeContent.js | 32 +++++++------------ src/components/Layout/Sidebar/SidebarLink.tsx | 1 + .../Layout/SidebarNav/SidebarNav.tsx | 1 - src/components/Layout/TopNav/TopNav.tsx | 3 +- src/components/Layout/getRouteMeta.tsx | 2 +- src/components/MDX/BlogCard.tsx | 1 + src/components/MDX/MDXComponents.tsx | 3 +- src/components/MDX/Sandpack/CustomPreset.tsx | 1 - src/components/MDX/Sandpack/NavigationBar.tsx | 8 +++-- src/components/MDX/Sandpack/Preview.tsx | 1 - src/components/MDX/TeamMember.tsx | 1 - src/components/Search.tsx | 3 +- src/hooks/usePendingRoute.ts | 2 +- 14 files changed, 27 insertions(+), 35 deletions(-) diff --git a/.eslintrc b/.eslintrc index 147e54607..f617dea26 100644 --- a/.eslintrc +++ b/.eslintrc @@ -5,7 +5,8 @@ "plugins": ["@typescript-eslint"], "rules": { "no-unused-vars": "off", - "@typescript-eslint/no-unused-vars": "warn" + "@typescript-eslint/no-unused-vars": ["error", { "varsIgnorePattern": "^_" }], + "react-hooks/exhaustive-deps": "error" }, "env": { "node": true, diff --git a/src/components/Layout/HomeContent.js b/src/components/Layout/HomeContent.js index ba3ff6ef1..e1b1b440d 100644 --- a/src/components/Layout/HomeContent.js +++ b/src/components/Layout/HomeContent.js @@ -8,12 +8,10 @@ import { useState, useContext, useId, - Fragment, Suspense, useEffect, useRef, useTransition, - useReducer, } from 'react'; import cn from 'classnames'; import NextLink from 'next/link'; @@ -26,7 +24,6 @@ import {IconSearch} from 'components/Icon/IconSearch'; import {Logo} from 'components/Logo'; import Link from 'components/MDX/Link'; import CodeBlock from 'components/MDX/CodeBlock'; -import {IconNavArrow} from 'components/Icon/IconNavArrow'; import {ExternalLink} from 'components/ExternalLink'; import sidebarBlog from '../../sidebarBlog.json'; @@ -67,14 +64,6 @@ function Para({children}) { ); } -function Left({children}) { - return ( -
- {children} -
- ); -} - function Center({children}) { return (
@@ -90,19 +79,23 @@ function FullBleed({children}) { } function CurrentTime() { - const msPerMinute = 60 * 1000; - const date = new Date(); - let nextMinute = Math.floor(+date / msPerMinute + 1) * msPerMinute; - + const [date, setDate] = useState(new Date()); const currentTime = date.toLocaleTimeString([], { hour: 'numeric', minute: 'numeric', }); - let [, forceUpdate] = useReducer((n) => n + 1, 0); useEffect(() => { - const timeout = setTimeout(forceUpdate, nextMinute - Date.now()); + const msPerMinute = 60 * 1000; + let nextMinute = Math.floor(+date / msPerMinute + 1) * msPerMinute; + + const timeout = setTimeout(() => { + if (Date.now() > nextMinute) { + setDate(new Date()); + } + }, nextMinute - Date.now()); return () => clearTimeout(timeout); }, [date]); + return {currentTime}; } @@ -831,7 +824,7 @@ function ExampleLayout({ .filter((s) => s !== null); setOverlayStyles(nextOverlayStyles); } - }, [activeArea]); + }, [activeArea, hoverTopOffset]); return (
@@ -1211,7 +1204,7 @@ function useNestedScrollLock(ref) { window.removeEventListener('scroll', handleScroll); clearInterval(interval); }; - }, []); + }, [ref]); } function ExamplePanel({ @@ -1220,7 +1213,6 @@ function ExamplePanel({ noShadow, height, contentMarginTop, - activeArea, }) { return (
0 ? breadcrumbs : [routeTree], diff --git a/src/components/MDX/BlogCard.tsx b/src/components/MDX/BlogCard.tsx index ba610b111..1a16013a2 100644 --- a/src/components/MDX/BlogCard.tsx +++ b/src/components/MDX/BlogCard.tsx @@ -18,6 +18,7 @@ function BlogCard({title, badge, date, icon, url, children}: BlogCardProps) { return (
diff --git a/src/components/MDX/MDXComponents.tsx b/src/components/MDX/MDXComponents.tsx index ba531c9f0..8ddf371c9 100644 --- a/src/components/MDX/MDXComponents.tsx +++ b/src/components/MDX/MDXComponents.tsx @@ -369,7 +369,8 @@ function YouTubeIframe(props: any) { } function Image(props: any) { - return ; + const {alt, ...rest} = props; + return {alt}; } export const MDXComponents = { diff --git a/src/components/MDX/Sandpack/CustomPreset.tsx b/src/components/MDX/Sandpack/CustomPreset.tsx index 6e0862564..46e0ca255 100644 --- a/src/components/MDX/Sandpack/CustomPreset.tsx +++ b/src/components/MDX/Sandpack/CustomPreset.tsx @@ -54,7 +54,6 @@ export const CustomPreset = memo(function CustomPreset({ const SandboxShell = memo(function SandboxShell({ showDevTools, - onDevToolsLoad, devToolsLoaded, providedFiles, lintErrors, diff --git a/src/components/MDX/Sandpack/NavigationBar.tsx b/src/components/MDX/Sandpack/NavigationBar.tsx index 8c884a5d8..94e2eb4b3 100644 --- a/src/components/MDX/Sandpack/NavigationBar.tsx +++ b/src/components/MDX/Sandpack/NavigationBar.tsx @@ -90,15 +90,17 @@ export function NavigationBar({providedFiles}: {providedFiles: Array}) { } else { return; } - }, [isMultiFile]); + + // Note: in a real useEvent, onContainerResize would be omitted. + }, [isMultiFile, onContainerResize]); const handleReset = () => { /** * resetAllFiles must come first, otherwise - * the previous content will appears for a second + * the previous content will appear for a second * when the iframe loads. * - * Plus, it should only prompts if there's any file changes + * Plus, it should only prompt if there's any file changes */ if ( sandpack.editorState === 'dirty' && diff --git a/src/components/MDX/Sandpack/Preview.tsx b/src/components/MDX/Sandpack/Preview.tsx index 2e140360c..6b7a6d183 100644 --- a/src/components/MDX/Sandpack/Preview.tsx +++ b/src/components/MDX/Sandpack/Preview.tsx @@ -49,7 +49,6 @@ export function Preview({ errorScreenRegisteredRef, openInCSBRegisteredRef, loadingScreenRegisteredRef, - status, } = sandpack; if ( diff --git a/src/components/MDX/TeamMember.tsx b/src/components/MDX/TeamMember.tsx index 887e9f691..0db067e10 100644 --- a/src/components/MDX/TeamMember.tsx +++ b/src/components/MDX/TeamMember.tsx @@ -7,7 +7,6 @@ import Image from 'next/image'; import {IconTwitter} from '../Icon/IconTwitter'; import {IconGitHub} from '../Icon/IconGitHub'; import {ExternalLink} from '../ExternalLink'; -import {IconNewPage} from 'components/Icon/IconNewPage'; import {H3} from './Heading'; import {IconLink} from 'components/Icon/IconLink'; diff --git a/src/components/Search.tsx b/src/components/Search.tsx index 2a9743ec3..8bc47297a 100644 --- a/src/components/Search.tsx +++ b/src/components/Search.tsx @@ -5,11 +5,10 @@ import Head from 'next/head'; import Link from 'next/link'; import Router from 'next/router'; -import {lazy, useCallback, useEffect} from 'react'; +import {lazy, useEffect} from 'react'; import * as React from 'react'; import {createPortal} from 'react-dom'; import {siteConfig} from 'siteConfig'; -import cn from 'classnames'; export interface SearchProps { appId?: string; diff --git a/src/hooks/usePendingRoute.ts b/src/hooks/usePendingRoute.ts index 73ff0b8af..229a36e64 100644 --- a/src/hooks/usePendingRoute.ts +++ b/src/hooks/usePendingRoute.ts @@ -33,7 +33,7 @@ const usePendingRoute = () => { events.off('routeChangeComplete', handleRouteChangeComplete); clearTimeout(routeTransitionTimer); }; - }, []); + }, [events]); return pendingRoute; };