diff --git a/package.json b/package.json index 5ff6adbab..472ef79c9 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "next-remote-watch": "^1.0.0", "parse-numeric-range": "^1.2.0", "react": "^0.0.0-experimental-16d053d59-20230506", - "react-collapsed": "npm:@gaearon/react-collapsed@3.1.0-forked.1", + "react-collapsed": "4.0.4", "react-dom": "^0.0.0-experimental-16d053d59-20230506", "remark-frontmatter": "^4.0.1", "remark-gfm": "^3.0.1" diff --git a/src/components/Layout/Sidebar/SidebarRouteTree.tsx b/src/components/Layout/Sidebar/SidebarRouteTree.tsx index 9a0dd23f5..a9fa575b5 100644 --- a/src/components/Layout/Sidebar/SidebarRouteTree.tsx +++ b/src/components/Layout/Sidebar/SidebarRouteTree.tsx @@ -7,7 +7,7 @@ import {useRef, useLayoutEffect, Fragment} from 'react'; import cn from 'classnames'; import {useRouter} from 'next/router'; import {SidebarLink} from './SidebarLink'; -import useCollapse from 'react-collapsed'; +import {useCollapse} from 'react-collapsed'; import usePendingRoute from 'hooks/usePendingRoute'; import type {RouteItem} from 'components/Layout/getRouteMeta'; diff --git a/src/components/Seo.tsx b/src/components/Seo.tsx index d0dcaab75..79f19f87c 100644 --- a/src/components/Seo.tsx +++ b/src/components/Seo.tsx @@ -22,6 +22,7 @@ const deployedTranslations = [ 'zh-hans', 'es', 'fr', + 'ja', // We'll add more languages when they have enough content. // Please DO NOT edit this list without a discussion in the reactjs/react.dev repo. // It must be the same between all translations. diff --git a/src/content/blog/2022/06/15/react-labs-what-we-have-been-working-on-june-2022.md b/src/content/blog/2022/06/15/react-labs-what-we-have-been-working-on-june-2022.md index 0d9e8e260..938b75c4b 100644 --- a/src/content/blog/2022/06/15/react-labs-what-we-have-been-working-on-june-2022.md +++ b/src/content/blog/2022/06/15/react-labs-what-we-have-been-working-on-june-2022.md @@ -8,7 +8,7 @@ June 15, 2022 by [Andrew Clark](https://twitter.com/acdlite), [Dan Abramov](http -[React 18](https://reactjs.org/blog/2022/03/29/react-v18) was years in the making, and with it brought valuable lessons for the React team. Its release was the result of many years of research and exploring many paths. Some of those paths were successful; many more were dead-ends that led to new insights. One lesson we’ve learned is that it’s frustrating for the community to wait for new features without having insight into these paths that we’re exploring. +[React 18](https://react.dev/blog/2022/03/29/react-v18) was years in the making, and with it brought valuable lessons for the React team. Its release was the result of many years of research and exploring many paths. Some of those paths were successful; many more were dead-ends that led to new insights. One lesson we’ve learned is that it’s frustrating for the community to wait for new features without having insight into these paths that we’re exploring. diff --git a/src/content/community/conferences.md b/src/content/community/conferences.md index 6e1780a7d..1c164a098 100644 --- a/src/content/community/conferences.md +++ b/src/content/community/conferences.md @@ -55,7 +55,6 @@ October 27th 2023. In-person in Verona, Italy and online (hybrid event) [Website](https://2023.reactjsday.it/) - [Twitter](https://twitter.com/reactjsday) - [Facebook](https://www.facebook.com/GrUSP/) - [YouTube](https://www.youtube.com/c/grusp) - ### React Summit US 2023 {/*react-summit-us-2023*/} November 13 & 15, 2023. In-person in New York, US + remote first interactivity (hybrid event) @@ -66,6 +65,11 @@ December 8 & 12, 2023. In-person in Berlin, Germany + remote first interactivity [Website](https://reactday.berlin) - [Twitter](https://twitter.com/reactdayberlin) - [Facebook](https://www.facebook.com/reactdayberlin/) - [Videos](https://portal.gitnation.org/events/react-day-berlin-2023) +### App.js Conf 2024 {/*appjs-conf-2024*/} +May 22 - 24, 2024. In-person in Kraków, Poland + remote + +[Website](https://appjs.co) - [Twitter](https://twitter.com/appjsconf) + ### Render(ATL) 2024 🍑 {/*renderatl-2024-*/} June 12 - June 14, 2024. Atlanta, GA, USA diff --git a/src/content/learn/referencing-values-with-refs.md b/src/content/learn/referencing-values-with-refs.md index f41b060bb..1fd17a7d8 100644 --- a/src/content/learn/referencing-values-with-refs.md +++ b/src/content/learn/referencing-values-with-refs.md @@ -284,7 +284,11 @@ Anda juga tidak perlu khawatir tentang [menghindari mutasi](/learn/updating-obje ## *Refs* dan *DOM* {/*refs-and-the-dom*/} +<<<<<<< HEAD Anda dapat memberikan nilai apa pun kepada *ref*. Namun, penggunaan *ref* yang paling umum adalah untuk mengakses sebuah elemen *DOM*. Misalnya, hal ini berguna jika Anda ingin memberi fokus pada sebuah input secara programatik. Ketika Anda mengoper sebuah *ref* ke dalam atribut `ref` di *JSX*, seperti `
`, React akan menempatkan elemen *DOM* yang sesuai ke dalam `myRef.current`. Anda dapat membaca lebih lanjut tentang hal ini di [Memanipulasi DOM dengan Refs](/learn/manipulating-the-dom-with-refs). +======= +You can point a ref to any value. However, the most common use case for a ref is to access a DOM element. For example, this is handy if you want to focus an input programmatically. When you pass a ref to a `ref` attribute in JSX, like `
`, React will put the corresponding DOM element into `myRef.current`. Once the element is removed from the DOM, React will update `myRef.current` to be `null`. You can read more about this in [Manipulating the DOM with Refs.](/learn/manipulating-the-dom-with-refs) +>>>>>>> e85b71de88a20cda9588f51f01d4a70e5cbe1cb4 diff --git a/src/content/learn/rendering-lists.md b/src/content/learn/rendering-lists.md index e22e17c3b..009d945f5 100644 --- a/src/content/learn/rendering-lists.md +++ b/src/content/learn/rendering-lists.md @@ -1088,7 +1088,11 @@ export const recipes = [{ #### Daftar dengan pemisah {/*list-with-a-separator*/} +<<<<<<< HEAD Pada contoh ini, terdapat sebuah *haiku* terkenal dari Katsushika Hokusai yang setiap barisnya dibungkus `

`. Anda diminta untuk menambahkan `


` di antara setiap baris sebagai pemisah. Hasil akhirnya akan menyerupai ini: +======= +This example renders a famous haiku by Tachibana Hokushi, with each line wrapped in a `

` tag. Your job is to insert an `


` separator between each paragraph. Your resulting structure should look like this: +>>>>>>> e85b71de88a20cda9588f51f01d4a70e5cbe1cb4 ```js
diff --git a/src/content/learn/start-a-new-react-project.md b/src/content/learn/start-a-new-react-project.md index f2fbcaeee..4c378a66c 100644 --- a/src/content/learn/start-a-new-react-project.md +++ b/src/content/learn/start-a-new-react-project.md @@ -21,10 +21,16 @@ Jika Anda ingin membuat aplikasi baru atau situs web baru dengan menggunakan Rea **[Next.js](https://nextjs.org/) adalah *full-stack framework* React.** *framework* ini serbaguna dan memudahkan Anda untuk membuat aplikasi React dalam berbagai ukuran--mulai dari halaman blog statis hingga aplikasi dinamis yang kompleks. Untuk membuat proyek Next.js, jalankan di terminal Anda: -npx create-next-app +npx create-next-app@latest +<<<<<<< HEAD Jika Anda baru menggunakan Next.js, baca [panduan Next.js.](https://nextjs.org/learn/foundations/about-nextjs) +======= +If you're new to Next.js, check out the [Next.js tutorial.](https://nextjs.org/learn/foundations/about-nextjs) + +Next.js is maintained by [Vercel](https://vercel.com/). You can [deploy a Next.js app](https://nextjs.org/docs/app/building-your-application/deploying) to any Node.js or serverless hosting, or to your own server. Next.js also supports a [static export](https://nextjs.org/docs/pages/building-your-application/deploying/static-exports) which doesn't require a server. +>>>>>>> e85b71de88a20cda9588f51f01d4a70e5cbe1cb4 Next.js dikelola oleh [Vercel](https://vercel.com/). Anda dapat [memasang aplikasi Next.js](https://nextjs.org/docs/deployment) ke semua ekosistem berbasis Node.js atau *serverless hosting* atau pada *server* Anda sendiri. [Aplikasi statis Next.js](https://nextjs.org/docs/advanced-features/static-html-export) dapat dipasang pada layanan *hosting* statis apa pun. ### Remix {/*remix*/} @@ -88,6 +94,7 @@ Setiap hari, fitur-fitur ini semakin dekat untuk menjadi teknologi yang siap dig ### *App Router* Next.js {/*nextjs-app-router*/} +<<<<<<< HEAD **[*App Router* Next.js](https://beta.nextjs.org/docs/getting-started) adalah sebuah desain baru dari Next.js API yang bertujuan untuk memenuhi visi tim React mengenai arsitektur aplikasi *full-stack*.** Hal ini dapat membantu Anda untuk mengambil data pada komponen asinkron yang berjalan pada *server* atau saat waktu *build*. Next.js dikelola oleh [Vercel](https://vercel.com/). Anda dapat [memasang aplikasi Next.js](https://nextjs.org/docs/deployment) ke semua ekosistem berbasis Node.js atau *serverless hosting* atau pada *server* Anda sendiri. Next.js juga mendukung [ekspor halaman statis](https://beta.nextjs.org/docs/configuring/static-export) yang tidak membutuhkan *server*. @@ -96,6 +103,11 @@ Next.js dikelola oleh [Vercel](https://vercel.com/). Anda dapat [memasang aplika *App Router* Next.js **saat ini dalam fase *beta* dan tidak direkomendasikan untuk implementasi di tingkat *production*** (per Bulan Maret 2023). Untuk mencoba bereksperimen dengan *App Router* dalam proyek Next.js yang ada, [ikuti panduan migrasi bertahap ini](https://beta.nextjs.org/docs/upgrade-guide#migrating-from-pages-to-app). +======= +**[Next.js's App Router](https://nextjs.org/docs) is a redesign of the Next.js APIs aiming to fulfill the React team’s full-stack architecture vision.** It lets you fetch data in asynchronous components that run on the server or even during the build. + +Next.js is maintained by [Vercel](https://vercel.com/). You can [deploy a Next.js app](https://nextjs.org/docs/app/building-your-application/deploying) to any Node.js or serverless hosting, or to your own server. Next.js also supports [static export](https://nextjs.org/docs/app/building-your-application/deploying/static-exports) which doesn't require a server. +>>>>>>> e85b71de88a20cda9588f51f01d4a70e5cbe1cb4 diff --git a/src/content/learn/tutorial-tic-tac-toe.md b/src/content/learn/tutorial-tic-tac-toe.md index 805773cfe..849a18c2e 100644 --- a/src/content/learn/tutorial-tic-tac-toe.md +++ b/src/content/learn/tutorial-tic-tac-toe.md @@ -2073,7 +2073,17 @@ export default function Game() { } ``` +<<<<<<< HEAD Anda dapat melihat tampilan kode Anda di bawah ini. Perhatikan bahwa Anda akan melihat error pada konsol *developer tools* yang bertuliskan: ``Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of `Game`.`` Anda akan memperbaiki error ini di bagian selanjutnya. +======= +You can see what your code should look like below. Note that you should see an error in the developer tools console that says: + + +Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of `Game`. + + +You'll fix this error in the next section. +>>>>>>> e85b71de88a20cda9588f51f01d4a70e5cbe1cb4 diff --git a/src/content/learn/typescript.md b/src/content/learn/typescript.md index 56fa5be5b..727974b1c 100644 --- a/src/content/learn/typescript.md +++ b/src/content/learn/typescript.md @@ -284,7 +284,7 @@ export default App = AppTSX; -This technique works when you have an default value which makes sense - but there are occasionally cases when you do not, and in those cases `null` can feel reasonable as a default value. However, to allow the type-system to understand your code, you need to explicitly set `ContextShape | null` on the `createContext`. +This technique works when you have a default value which makes sense - but there are occasionally cases when you do not, and in those cases `null` can feel reasonable as a default value. However, to allow the type-system to understand your code, you need to explicitly set `ContextShape | null` on the `createContext`. This causes the issue that you need to eliminate the `| null` in the type for context consumers. Our recommendation is to have the hook do a runtime check for it's existence and throw an error when not present: @@ -460,4 +460,4 @@ We recommend the following resources: - [React TypeScript Cheatsheet](https://react-typescript-cheatsheet.netlify.app/) is a community-maintained cheatsheet for using TypeScript with React, covering a lot of useful edge cases and providing more breadth than this document. - - [TypeScript Community Discord](https://discord.com/invite/typescript) is a great place to ask questions and get help with TypeScript and React issues. \ No newline at end of file + - [TypeScript Community Discord](https://discord.com/invite/typescript) is a great place to ask questions and get help with TypeScript and React issues. diff --git a/src/content/reference/react-dom/server/renderToPipeableStream.md b/src/content/reference/react-dom/server/renderToPipeableStream.md index bbfca9b3a..aa94acd4d 100644 --- a/src/content/reference/react-dom/server/renderToPipeableStream.md +++ b/src/content/reference/react-dom/server/renderToPipeableStream.md @@ -286,8 +286,14 @@ Pada contoh di atas, React dapat memulai *streaming* halaman lebih awal. Hanya ` **Hanya sumber data yang mendukung Suspense yang akan mengaktifkan komponen Suspense.** Di antaranya: +<<<<<<< HEAD - *Data fetching* dengan *framework* yang mendukung *Suspense* seperti [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) dan [Next.js](https://nextjs.org/docs/advanced-features/react-18) - Pemuatan kode komponen secara *lazy-loading* dengan [`lazy`](/reference/react/lazy) +======= +- Data fetching with Suspense-enabled frameworks like [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) and [Next.js](https://nextjs.org/docs/getting-started/react-essentials) +- Lazy-loading component code with [`lazy`](/reference/react/lazy) +- Reading the value of a Promise with [`use`](/reference/react/use) +>>>>>>> e85b71de88a20cda9588f51f01d4a70e5cbe1cb4 *Suspense* **tidak** dapat medeteksi *data fetching* jika dilakukan dalam *Effect* atau *event handler.* diff --git a/src/content/reference/react-dom/server/renderToReadableStream.md b/src/content/reference/react-dom/server/renderToReadableStream.md index ad2fe775c..8454822d6 100644 --- a/src/content/reference/react-dom/server/renderToReadableStream.md +++ b/src/content/reference/react-dom/server/renderToReadableStream.md @@ -285,8 +285,14 @@ Dalam contoh ini, React dapat memulai *streaming* halaman lebih awal. Hanya `Pro **Hanya sumber data yang mengaktifkan *Suspense* yang akan mengaktifkan komponen Suspense.** Sumber tersebut meliputi: +<<<<<<< HEAD - Pengambilan data dengan *framework* yang mendukung *Suspense* seperti [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) dan [Next.js](https://nextjs.org/docs/getting-started/react-essentials) - Kode komponen pemuatan lambat dengan [`lazy`](/reference/react/lazy) +======= +- Data fetching with Suspense-enabled frameworks like [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) and [Next.js](https://nextjs.org/docs/getting-started/react-essentials) +- Lazy-loading component code with [`lazy`](/reference/react/lazy) +- Reading the value of a Promise with [`use`](/reference/react/use) +>>>>>>> e85b71de88a20cda9588f51f01d4a70e5cbe1cb4 *Suspense* **tidak** mendeteksi saat data diambil di dalam *Effect* atau *event handler*. diff --git a/src/content/reference/react/Profiler.md b/src/content/reference/react/Profiler.md index 6b75ef882..47d65ccd3 100644 --- a/src/content/reference/react/Profiler.md +++ b/src/content/reference/react/Profiler.md @@ -55,12 +55,21 @@ function onRender(id, phase, actualDuration, baseDuration, startTime, commitTime #### Parameters {/*onrender-parameters*/} +<<<<<<< HEAD * `id`: String `id` prop dari pohon `` yang baru saja di-*commit*. Ini memungkinkan Anda mengidentifikasi bagian mana dari pohon yang di-*commit* jika Anda menggunakan beberapa *profiler*. * `phase`: `"mount"`, `"update"` atau `"nested-update"`. Hal ini memungkinkan Anda mengetahui, apakah pohon baru saja dipasang untuk pertama kali, atau di-*render* ulang karena ada perubahan pada *props*, status, atau *hooks*. * `actualDuration`: Jumlah milidetik yang dihabiskan untuk me-*render* `` dan turunannya untuk pembaruan terkini. Ini mengindikasikan seberapa baik *sub*-pohon menggunakan memoisasi (contohnya [`memo`](/reference/react/memo) dan [`useMemo`](/reference/react/useMemo)). Idealnya, nilai ini akan berkurang secara signifikan setelah pemasangan awal, karena banyak keturunan yang hanya perlu me-*render* ulang jika *props* tertentu mereka berubah. * `baseDuration`: Jumlah milidetik yang memperkirakan berapa lama waktu yang dibutuhkan untuk me-*render* ulang seluruh *sub*-pohon `` tanpa pengoptimalan apa pun. Dihitung dengan menjumlahkan durasi *render* terbaru dari setiap komponen dalam pohon. Nilai ini memperkirakan biaya kasus terburuk dari *rendering* (misalnya, pemasangan awal atau pohon tanpa memoisasi). Bandingkan `actualDuration` dengan nilai ini untuk melihat apakah memoisasi berfungsi. * `startTime`: Stempel waktu numerik untuk mengetahui kapan React mulai me-*render* pembaruan terkini. * `endTime`: Stempel waktu numerik untuk mengetahui kapan React melakukan pembaruan terkini. Nilai ini dibagi di antara semua *profiler* dalam sebuah *commit*, sehingga memungkinkan untuk dikelompokkan jika diinginkan. +======= +* `id`: The string `id` prop of the `` tree that has just committed. This lets you identify which part of the tree was committed if you are using multiple profilers. +* `phase`: `"mount"`, `"update"` or `"nested-update"`. This lets you know whether the tree has just been mounted for the first time or re-rendered due to a change in props, state, or hooks. +* `actualDuration`: The number of milliseconds spent rendering the `` and its descendants for the current update. This indicates how well the subtree makes use of memoization (e.g. [`memo`](/reference/react/memo) and [`useMemo`](/reference/react/useMemo)). Ideally this value should decrease significantly after the initial mount as many of the descendants will only need to re-render if their specific props change. +* `baseDuration`: The number of milliseconds estimating how much time it would take to re-render the entire `` subtree without any optimizations. It is calculated by summing up the most recent render durations of each component in the tree. This value estimates a worst-case cost of rendering (e.g. the initial mount or a tree with no memoization). Compare `actualDuration` against it to see if memoization is working. +* `startTime`: A numeric timestamp for when React began rendering the current update. +* `commitTime`: A numeric timestamp for when React committed the current update. This value is shared between all profilers in a commit, enabling them to be grouped if desirable. +>>>>>>> e85b71de88a20cda9588f51f01d4a70e5cbe1cb4 --- diff --git a/src/content/reference/react/Suspense.md b/src/content/reference/react/Suspense.md index dd9312055..d94003783 100644 --- a/src/content/reference/react/Suspense.md +++ b/src/content/reference/react/Suspense.md @@ -254,6 +254,7 @@ async function getAlbums() { - Data fetching with Suspense-enabled frameworks like [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) and [Next.js](https://nextjs.org/docs/getting-started/react-essentials) - Lazy-loading component code with [`lazy`](/reference/react/lazy) +- Reading the value of a Promise with [`use`](/reference/react/use) Suspense **does not** detect when data is fetched inside an Effect or event handler. diff --git a/src/content/reference/react/cache.md b/src/content/reference/react/cache.md index 72fa4bd36..7f9afdb99 100644 --- a/src/content/reference/react/cache.md +++ b/src/content/reference/react/cache.md @@ -414,7 +414,7 @@ See prior mentioned pitfalls If none of the above apply, it may be a problem with how React checks if something exists in cache. -If your arguments are not [primatives](https://developer.mozilla.org/en-US/docs/Glossary/Primitive) (ex. objects, functions, arrays), ensure you're passing the same object reference. +If your arguments are not [primitives](https://developer.mozilla.org/en-US/docs/Glossary/Primitive) (ex. objects, functions, arrays), ensure you're passing the same object reference. When calling a memoized function, React will look up the input arguments to see if a result is already cached. React will use shallow equality of the arguments to determine if there is a cache hit. diff --git a/src/content/reference/react/experimental_taintObjectReference.md b/src/content/reference/react/experimental_taintObjectReference.md new file mode 100644 index 000000000..e3fd35a48 --- /dev/null +++ b/src/content/reference/react/experimental_taintObjectReference.md @@ -0,0 +1,153 @@ +--- +title: experimental_taintObjectReference +--- + + + +**This API is experimental and is not available in a stable version of React yet.** + +You can try it by upgrading React packages to the most recent experimental version: + +- `react@experimental` +- `react-dom@experimental` +- `eslint-plugin-react-hooks@experimental` + +Experimental versions of React may contain bugs. Don't use them in production. + +This API is only available inside React Server Components. + + + + + + +`taintObjectReference` lets you prevent a specific object instance from being passed to a Client Component like a `user` object. + +```js +experimental_taintObjectReference(message, object); +``` + +To prevent passing a key, hash or token, see [`taintUniqueValue`](/reference/react/experimental_taintUniqueValue). + + + + + +--- + +## Reference {/*reference*/} + +### `taintObjectReference(message, object)` {/*taintobjectreference*/} + +Call `taintObjectReference` with an object to register it with React as something that should not be allowed to be passed to the Client as is: + +```js +import {experimental_taintObjectReference} from 'react'; + +experimental_taintObjectReference( + 'Do not pass ALL environment variables to the client.', + process.env +); +``` + +[See more examples below.](#usage) + +#### Parameters {/*parameters*/} + +* `message`: The message you want to display if the object gets passed to a Client Component. This message will be displayed as a part of the Error that will be thrown if the object gets passed to a Client Component. + +* `object`: The object to be tainted. Functions and class instances can be passed to `taintObjectReference` as `object`. Functions and classes are already blocked from being passed to Client Components but the React's default error message will be replaced by what you defined in `message`. When a specific instance of a Typed Array is passed to `taintObjectReference` as `object`, any other copies of the Typed Array will not be tainted. + +#### Returns {/*returns*/} + +`experimental_taintObjectReference` returns `undefined`. + +#### Caveats {/*caveats*/} + +- Recreating or cloning a tainted object creates a new untained object which main contain sensetive data. For example, if you have a tainted `user` object, `const userInfo = {name: user.name, ssn: user.ssn}` or `{...user}` will create new objects which are not tainted. `taintObjectReference` only protects against simple mistakes when the object is passed through to a Client Component unchanged. + + + +**Do not rely on just tainting for security.** Tainting an object doesn't prevent leaking of every possible derived value. For example, the clone of a tainted object will create a new untained object. Using data from a tainted object (e.g. `{secret: taintedObj.secret}`) will create a new value or object that is not tainted. Tainting is a layer of protection; a secure app will have multiple layers of protection, well designed APIs, and isolation patterns. + + + +--- + +## Usage {/*usage*/} + +### Prevent user data from unintentionally reaching the client {/*prevent-user-data-from-unintentionally-reaching-the-client*/} + +A Client Component should never accept objects that carry sensitive data. Ideally, the data fetching functions should not expose data that the current user should not have access to. Sometimes mistakes happen during refactoring. To protect against this mistakes happening down the line we can "taint" the user object in our data API. + +```js +import {experimental_taintObjectReference} from 'react'; + +export async function getUser(id) { + const user = await db`SELECT * FROM users WHERE id = ${id}`; + experimental_taintObjectReference( + 'Do not pass the entire user object to the client. ' + + 'Instead, pick off the specific properties you need for this use case.', + user, + ); + return user; +} +``` + +Now whenever anyone tries to pass this object to a Client Component, an error will be thrown with the passed in error message instead. + + + +#### Protecting against leaks in data fetching {/*protecting-against-leaks-in-data-fetching*/} + +If you're running a Server Components environment that has access to sensitive data, you have to be careful not to pass objects straight through: + +```js +// api.js +export async function getUser(id) { + const user = await db`SELECT * FROM users WHERE id = ${id}`; + return user; +} +``` + +```js +import { getUser } from 'api.js'; +import { InfoCard } from 'components.js'; + +export async function Profile(props) { + const user = await getUser(props.userId); + // DO NOT DO THIS + return ; +} +``` + +```js +// components.js +"use client"; + +export async function InfoCard({ user }) { + return
{user.name}
; +} +``` + +Ideally, the `getUser` should not expose data that the current user should not have access to. To prevent passing the `user` object to a Client Component down the line we can "taint" the user object: + + +```js +// api.js +import {experimental_taintObjectReference} from 'react'; + +export async function getUser(id) { + const user = await db`SELECT * FROM users WHERE id = ${id}`; + experimental_taintObjectReference( + 'Do not pass the entire user object to the client. ' + + 'Instead, pick off the specific properties you need for this use case.', + user, + ); + return user; +} +``` + +Now if anyone tries to pass the `user` object to a Client Component, an error will be thrown with the passed in error message. + +
diff --git a/src/content/reference/react/experimental_taintUniqueValue.md b/src/content/reference/react/experimental_taintUniqueValue.md new file mode 100644 index 000000000..a67eebf77 --- /dev/null +++ b/src/content/reference/react/experimental_taintUniqueValue.md @@ -0,0 +1,198 @@ +--- +title: experimental_taintUniqueValue +--- + + + +**This API is experimental and is not available in a stable version of React yet.** + +You can try it by upgrading React packages to the most recent experimental version: + +- `react@experimental` +- `react-dom@experimental` +- `eslint-plugin-react-hooks@experimental` + +Experimental versions of React may contain bugs. Don't use them in production. + +This API is only available inside [React Server Components](/reference/react/use-client). + + + + + + +`taintUniqueValue` lets you prevent unique values from being passed to Client Components like passwords, keys, or tokens. + +```js +taintUniqueValue(errMessage, lifetime, value) +``` + +To prevent passing an object containing sensitive data, see [`taintObjectReference`](/reference/react/experimental_taintObjectReference). + + + + + +--- + +## Reference {/*reference*/} + +### `taintUniqueValue(message, lifetime, value)` {/*taintuniquevalue*/} + +Call `taintUniqueValue` with a password, token, key or hash to register it with React as something that should not be allowed to be passed to the Client as is: + +```js +import {experimental_taintUniqueValue} from 'react'; + +experimental_taintUniqueValue( + 'Do not pass secret keys to the client.', + process, + process.env.SECRET_KEY +); +``` + +[See more examples below.](#usage) + +#### Parameters {/*parameters*/} + +* `message`: The message you want to display if `value` is passed to a Client Component. This message will be displayed as a part of the Error that will be thrown if `value` is passed to a Client Component. + +* `lifetime`: Any object that indicates how long `value` should be tainted. `value` will be blocked from being sent to any Client Component while this object still exists. For example, passing `globalThis` blocks the value for the lifetime of an app. `lifetime` is typically an object whose properties contains `value`. + +* `value`: A string, bigint or TypedArray. `value` must be a unique sequence of characters or bytes with high entropy such as a cryptographic token, private key, hash, or a long password. `value` will be blocked from being sent to any Client Component. + +#### Returns {/*returns*/} + +`experimental_taintUniqueValue` returns `undefined`. + +#### Caveats {/*caveats*/} + +- Deriving new values from tainted values can compromise tainting protection. New values created by uppercasing tainted values, concatenating tainted string values into a larger string, converting tainted values to base64, substringing tainted values, and other similar transformations are not tainted unless you explicity call `taintUniqueValue` on these newly created values. + +--- + +## Usage {/*usage*/} + +### Prevent a token from being passed to Client Components {/*prevent-a-token-from-being-passed-to-client-components*/} + +To ensure that sensitive information such as passwords, session tokens, or other unique values do not inadvertently get passed to Client Components, the `taintUniqueValue` function provides a layer of protection. When a value is tainted, any attempt to pass it to a Client Component will result in an error. + +The `lifetime` argument defines the duration for which the value remains tainted. For values that should remain tainted indefinitely, objects like [`globalThis`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis) or `process` can serve as the `lifetime` argument. These objects have a lifespan that spans the entire duration of your app's execution. + +```js +import {experimental_taintUniqueValue} from 'react'; + +experimental_taintUniqueValue( + 'Do not pass a user password to the client.', + globalThis, + process.env.SECRET_KEY +); +``` + +If the tainted value's lifespan is tied to a object, the `lifetime` should be the object that encapsulates the value. This ensures the tainted value remains protected for the lifetime of the encapsulating object. + +```js +import {experimental_taintUniqueValue} from 'react'; + +export async function getUser(id) { + const user = await db`SELECT * FROM users WHERE id = ${id}`; + experimental_taintUniqueValue( + 'Do not pass a user session token to the client.', + user, + user.session.token + ); + return user; +} +``` + +In this example, the `user` object serves as the `lifetime` argument. If this object gets stored in a global cache or is accessible by another request, the session token remains tainted. + + + +**Do not rely solely on tainting for security.** Tainting a value doesn't block every possible derived value. For example, creating a new value by upper casing a tainted string will not taint the new value. + + +```js +import {experimental_taintUniqueValue} from 'react'; + +const password = 'correct horse battery staple'; + +experimental_taintUniqueValue( + 'Do not pass the password to the client.', + globalThis, + password +); + +const uppercasePassword = password.toUpperCase() // `uppercasePassword` is not tainted +``` + +In this example, the constant `password` is tainted. Then `password` is used to create a new value `uppercasePassword` by calling the `toUpperCase` method on `password`. The newly created `uppercasePassword` is not tainted. + +Other similar ways of deriving new values from tainted values like concatenating it into a larger string, converting it to base64, or returning a substring create untained values. + +Tainting only protects against simple mistakes like explictly passing secret values to the client. Mistakes in calling the `taintUniqueValue` like using a global store outside of React, without the corresponding lifetime object, can cause the tainted value to become untainted. Tainting is a layer of protection; a secure app will have multiple layers of protection, well designed APIs, and isolation patterns. + + + + + +#### Using `server-only` and `taintUniqueValue` to prevent leaking secrets {/*using-server-only-and-taintuniquevalue-to-prevent-leaking-secrets*/} + +If you're running a Server Components environment that has access to private keys or passwords such as database passwords, you have to be careful not to pass that to a Client Component. + +```js +export async function Dashboard(props) { + // DO NOT DO THIS + return ; +} +``` + +```js +"use client"; + +import {useEffect} from '...' + +export async function Overview({ password }) { + useEffect(() => { + const headers = { Authorization: password }; + fetch(url, { headers }).then(...); + }, [password]); + ... +} +``` + +This example would leak the secret API token to the client. If this API token can be used to access data this particular user shouldn't have access to, it could lead to a data breach. + +[comment]: <> (TODO: Link to `server-only` docs once they are written) + +Ideally, secrets like this are abstracted into a single helper file that can only be imported by trusted data utilities on the server. The helper can even be tagged with [`server-only`](https://www.npmjs.com/package/server-only) to ensure that this file isn't imported on the client. + +```js +import "server-only"; + +export function fetchAPI(url) { + const headers = { Authorization: process.env.API_PASSWORD }; + return fetch(url, { headers }); +} +``` + +Sometimes mistakes happen during refactoring and not all of your colleagues might know about this. +To protect against this mistakes happening down the line we can "taint" the actual password: + +```js +import "server-only"; +import {experimental_taintUniqueValue} from 'react'; + +experimental_taintUniqueValue( + 'Do not pass the API token password to the client. ' + + 'Instead do all fetches on the server.' + process, + process.env.API_PASSWORD +); +``` + +Now whenever anyone tries to pass this password to a Client Component, or send the password to a Client Component with a Server Action, a error will be thrown with message you defined when you called `taintUniqueValue`. + + + +--- diff --git a/src/content/reference/react/useDeferredValue.md b/src/content/reference/react/useDeferredValue.md index ae0dcc5b0..1552f4f08 100644 --- a/src/content/reference/react/useDeferredValue.md +++ b/src/content/reference/react/useDeferredValue.md @@ -82,10 +82,18 @@ Selama pembaruan, nilai yang ditangguhkan akan "te +<<<<<<< HEAD Contoh ini menganggap Anda menggunakan salah satu sumber data yang menggunakan Suspense: - Pengambilan data yang menggunakan Suspense dengan framework seperti [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) dan [Next.js](https://nextjs.org/docs/getting-started/react-essentials) - Kode komponen pemuatan lambat dengan [`lazy`](/reference/react/lazy) +======= +This example assumes you use a Suspense-enabled data source: + +- Data fetching with Suspense-enabled frameworks like [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) and [Next.js](https://nextjs.org/docs/getting-started/react-essentials) +- Lazy-loading component code with [`lazy`](/reference/react/lazy) +- Reading the value of a Promise with [`use`](/reference/react/use) +>>>>>>> e85b71de88a20cda9588f51f01d4a70e5cbe1cb4 [Pelajari lebih lanjut tentang Suspense dan batasannya.](/reference/react/Suspense) diff --git a/src/content/reference/react/useMemo.md b/src/content/reference/react/useMemo.md index 073696d5a..a53c2fe30 100644 --- a/src/content/reference/react/useMemo.md +++ b/src/content/reference/react/useMemo.md @@ -645,7 +645,13 @@ Membungkus *node* JSX secara manual ke dalam `useMemo` tidaklah mudah. Misalnya, Pada contoh ini, komponen `List` **dilambatkan secara artifisial** sehingga Anda dapat melihat apa yang terjadi ketika komponen React yang anda *render* benar-benar lambat. Coba ganti *tab* dan ubah temanya. +<<<<<<< HEAD Mengganti *tab* terasa lambat karena `List` yang diperlambat dipaksa untuk *render* ulang. Hal ini wajar karena `tab` berubah, sehingga Anda perlu menampilkan pilihan baru untuk pengguna di layar. +======= +Switching the tabs feels slow because it forces the slowed down `List` to re-render. That's expected because the `tab` has changed, and so you need to reflect the user's new choice on the screen. + +Next, try toggling the theme. **Thanks to `useMemo` together with [`memo`](/reference/react/memo), it’s fast despite the artificial slowdown!** The `List` skipped re-rendering because the `visibleTodos` array has not changed since the last render. The `visibleTodos` array has not changed because both `todos` and `tab` (which you pass as dependencies to `useMemo`) haven't changed since the last render. +>>>>>>> e85b71de88a20cda9588f51f01d4a70e5cbe1cb4 Selanjutnya, coba ubah temanya. **Berkat `useMemo` dan [`memo`](/reference/react/memo), mengubah tema menjadi cepat walaupun dilambatkan secara artifisial!** Pe-*render*-an ulang pada `List` dilewati karena senarai (*array*) `visibleItems` tidak berubah semenjak *render* terakhir. Senarai (*array*) `visibleItems` tidak berubah karena `todos` dan `tabs` (yang Anda berikan sebagai `dependency` ke `useMemo`) tidak berubah semenjak *render* terakhir. diff --git a/src/content/reference/react/useSyncExternalStore.md b/src/content/reference/react/useSyncExternalStore.md index a5108db3b..9bce79691 100644 --- a/src/content/reference/react/useSyncExternalStore.md +++ b/src/content/reference/react/useSyncExternalStore.md @@ -57,6 +57,26 @@ function TodosApp() { * Jika fungsi `subscribe` yang berbeda diberikan saat *render* ulang, React akan berlangganan ulang ke tempat penyimpanan menggunakan fungsi `subscribe` yang baru. Anda bisa menghindari ini dengan mendeklarasi `subscribe` di luar komponen. +* If the store is mutated during a [non-blocking transition update](/reference/react/useTransition), React will fall back to performing that update as blocking. Specifically, React will call `getSnapshot` a second time just before applying changes to the DOM. If it returns a different value than when it was called originally, React will restart the transition update from scratch, this time applying it as a blocking update, to ensure that every component on screen is reflecting the same version of the store. + +* It's not recommended to _suspend_ a render based on a store value returned by `useSyncExternalStore`. The reason is that mutations to the external store cannot be [marked as non-blocking transition updates](/reference/react/useTransition), so they will trigger the nearest [`Suspense` fallback](/reference/react/Suspense), replacing already-rendered content on screen with a loading spinner, which typically makes a poor UX. + + For example, the following are discouraged: + + ```js + const LazyProductDetailPage = lazy(() => import('./ProductDetailPage.js')); + + function ShoppingApp() { + const selectedProductId = useSyncExternalStore(...); + + // ❌ Calling `use` with a Promise dependent on `selectedProductId` + const data = use(fetchItem(selectedProductId)) + + // ❌ Conditionally rendering a lazy component based on `selectedProductId` + return selectedProductId != null ? : ; + } + ``` + --- ## Penggunaan {/*usage*/} @@ -426,4 +446,4 @@ function ChatIndicator({ userId }) { // ... } -``` \ No newline at end of file +``` diff --git a/src/sidebarReference.json b/src/sidebarReference.json index 151d4d10e..727790333 100644 --- a/src/sidebarReference.json +++ b/src/sidebarReference.json @@ -127,6 +127,16 @@ { "title": "startTransition", "path": "/reference/react/startTransition" + }, + { + "title": "experimental_taintObjectReference", + "path": "/reference/react/experimental_taintObjectReference", + "canary": true + }, + { + "title": "experimental_taintUniqueValue", + "path": "/reference/react/experimental_taintUniqueValue", + "canary": true } ] }, diff --git a/yarn.lock b/yarn.lock index 5819fa4fa..8a3183cd5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4797,11 +4797,6 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= - periscopic@^3.0.0: version "3.0.4" resolved "https://registry.yarnpkg.com/periscopic/-/periscopic-3.0.4.tgz#b3fbed0d1bc844976b977173ca2cd4a0ef4fa8d1" @@ -5284,13 +5279,6 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -raf@^3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" - integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== - dependencies: - performance-now "^2.1.0" - range-parser@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" @@ -5306,12 +5294,11 @@ raw-body@2.5.1: iconv-lite "0.4.24" unpipe "1.0.0" -"react-collapsed@npm:@gaearon/react-collapsed@3.1.0-forked.1": - version "3.1.0-forked.1" - resolved "https://registry.yarnpkg.com/@gaearon/react-collapsed/-/react-collapsed-3.1.0-forked.1.tgz#b287b81fc2af2971d7d7b523dc40b6cf116822ac" - integrity sha512-QkW55Upl4eeOtnDMOxasafDtDwaF+DpYKvHq8KZoNz9P477iUH8Ik1YFYuqtI7UA8mHm1/z66LD678dZCXwEEg== +react-collapsed@4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/react-collapsed/-/react-collapsed-4.0.4.tgz#4c6bce3a15286d43e95b6730ad70ec387a54caa9" + integrity sha512-8avvmnQxDYTgGZYVP9+3Z7doomxVEBoCkukpTmUHEIrAYvELZ5jNNfYCt/hCpHB6GmQbzZoDmnDupjsnQVgcCQ== dependencies: - raf "^3.4.1" tiny-warning "^1.0.3" react-devtools-inline@4.4.0: