diff --git a/src/components/DocsFooter.tsx b/src/components/DocsFooter.tsx index 2fdbb046..5f2330e7 100644 --- a/src/components/DocsFooter.tsx +++ b/src/components/DocsFooter.tsx @@ -27,7 +27,7 @@ export const DocsPageFooter = memo( <> {prevRoute?.path || nextRoute?.path ? ( <> -
+
{prevRoute?.path ? ( - - +
+ {type} - {title} - + + {title} + +
); } diff --git a/src/components/ErrorDecoderContext.tsx b/src/components/ErrorDecoderContext.tsx new file mode 100644 index 00000000..080969ef --- /dev/null +++ b/src/components/ErrorDecoderContext.tsx @@ -0,0 +1,23 @@ +// Error Decoder requires reading pregenerated error message from getStaticProps, +// but MDX component doesn't support props. So we use React Context to populate +// the value without prop-drilling. +// TODO: Replace with React.cache + React.use when migrating to Next.js App Router + +import {createContext, useContext} from 'react'; + +const notInErrorDecoderContext = Symbol('not in error decoder context'); + +export const ErrorDecoderContext = createContext< + | {errorMessage: string | null; errorCode: string | null} + | typeof notInErrorDecoderContext +>(notInErrorDecoderContext); + +export const useErrorDecoderParams = () => { + const params = useContext(ErrorDecoderContext); + + if (params === notInErrorDecoderContext) { + throw new Error('useErrorDecoder must be used in error decoder pages only'); + } + + return params; +}; diff --git a/src/components/Layout/Feedback.tsx b/src/components/Layout/Feedback.tsx index 8639ce12..0bdbc6b7 100644 --- a/src/components/Layout/Feedback.tsx +++ b/src/components/Layout/Feedback.tsx @@ -4,6 +4,7 @@ import {useState} from 'react'; import {useRouter} from 'next/router'; +import cn from 'classnames'; export function Feedback({onSubmit = () => {}}: {onSubmit?: () => void}) { const {asPath} = useRouter(); @@ -60,7 +61,11 @@ function sendGAEvent(isPositive: boolean) { function SendFeedback({onSubmit}: {onSubmit: () => void}) { const [isSubmitted, setIsSubmitted] = useState(false); return ( -
+

{isSubmitted ? 'Thank you for your feedback!' : 'Is this page useful?'}

diff --git a/src/components/MDX/ErrorDecoder.tsx b/src/components/MDX/ErrorDecoder.tsx new file mode 100644 index 00000000..daa71328 --- /dev/null +++ b/src/components/MDX/ErrorDecoder.tsx @@ -0,0 +1,107 @@ +import {useEffect, useState} from 'react'; +import {useErrorDecoderParams} from '../ErrorDecoderContext'; +import cn from 'classnames'; + +function replaceArgs( + msg: string, + argList: Array, + replacer = '[missing argument]' +): string { + let argIdx = 0; + return msg.replace(/%s/g, function () { + const arg = argList[argIdx++]; + // arg can be an empty string: ?args[0]=&args[1]=count + return arg === undefined || arg === '' ? replacer : arg; + }); +} + +/** + * Sindre Sorhus + * Released under MIT license + * https://github.com/sindresorhus/linkify-urls/blob/edd75a64a9c36d7025f102f666ddbb6cf0afa7cd/index.js#L4C25-L4C137 + * + * The regex is used to extract URL from the string for linkify. + */ +const urlRegex = + /((? { + if (i % 2 === 1) { + return ( + + {message} + + ); + } + return message; + }); +} + +// `?args[]=foo&args[]=bar` +// or `// ?args[0]=foo&args[1]=bar` +function parseQueryString(search: string): Array { + const rawQueryString = search.substring(1); + if (!rawQueryString) { + return []; + } + + const args: Array = []; + + const queries = rawQueryString.split('&'); + for (let i = 0; i < queries.length; i++) { + const query = decodeURIComponent(queries[i]); + if (query.startsWith('args[')) { + args.push(query.slice(query.indexOf(']=') + 2)); + } + } + + return args; +} + +export default function ErrorDecoder() { + const {errorMessage} = useErrorDecoderParams(); + /** error messages that contain %s require reading location.search */ + const hasParams = errorMessage?.includes('%s'); + const [message, setMessage] = useState(() => + errorMessage ? urlify(errorMessage) : null + ); + + const [isReady, setIsReady] = useState(errorMessage == null || !hasParams); + + useEffect(() => { + if (errorMessage == null || !hasParams) { + return; + } + + setMessage( + urlify( + replaceArgs( + errorMessage, + parseQueryString(window.location.search), + '[missing argument]' + ) + ) + ); + setIsReady(true); + }, [hasParams, errorMessage]); + + return ( + + {message} + + ); +} diff --git a/src/components/MDX/MDXComponents.tsx b/src/components/MDX/MDXComponents.tsx index a0547e93..67e78502 100644 --- a/src/components/MDX/MDXComponents.tsx +++ b/src/components/MDX/MDXComponents.tsx @@ -31,6 +31,8 @@ import {TocContext} from './TocContext'; import type {Toc, TocItem} from './TocContext'; import {TeamMember} from './TeamMember'; +import ErrorDecoder from './ErrorDecoder'; + function CodeStep({children, step}: {children: any; step: number}) { return ( >>>>>> 303ecae3dd4c7b570cf18e0115b94188f6aad5a1 +======= +### React Paris 2024 {/*react-paris-2024*/} +March 22, 2024. In-person in Paris, France + Remote (hybrid) + +[Website](https://react.paris/) - [Twitter](https://twitter.com/BeJS_) - [LinkedIn](https://www.linkedin.com/events/7150816372074192900/comments/) + +>>>>>>> 6bfde58c109ec86fd6c5767421404cb679ffba9a ### App.js Conf 2024 {/*appjs-conf-2024*/} May 22 - 24, 2024. In-person in Kraków, Poland + remote @@ -79,6 +87,11 @@ June 12 - June 14, 2024. Atlanta, GA, USA [Website](https://renderatl.com) - [Discord](https://www.renderatl.com/discord) - [Twitter](https://twitter.com/renderATL) - [Instagram](https://www.instagram.com/renderatl/) - [Facebook](https://www.facebook.com/renderatl/) - [LinkedIn](https://www.linkedin.com/company/renderatl) - [Podcast](https://www.renderatl.com/culture-and-code#/) +### React Nexus 2024 {/*react-nexus-2024*/} +July 04 & 05, 2024. Bangalore, India (In-person event) + +[Website](https://reactnexus.com/) - [Twitter](https://twitter.com/ReactNexus) - [Linkedin](https://www.linkedin.com/company/react-nexus) - [YouTube](https://www.youtube.com/reactify_in) + ### React India 2024 {/*react-india-2024*/} October 17 - 19, 2024. In-person in Goa, India (hybrid event) + Oct 15 2024 - remote day @@ -109,7 +122,7 @@ October 20 & 23, 2023. In-person in London, UK + remote first interactivity (hyb ### React Brussels 2023 {/*react-brussels-2023*/} October 13th 2023. In-person in Brussels, Belgium + Remote (hybrid) -[Website](https://www.react.brussels/) - [Twitter](https://twitter.com/BrusselsReact) +[Website](https://www.react.brussels/) - [Twitter](https://twitter.com/BrusselsReact) - [Videos](https://www.youtube.com/playlist?list=PL53Z0yyYnpWh85KeMomUoVz8_brrmh_aC) ### React India 2023 {/*react-india-2023*/} October 5 - 7, 2023. In-person in Goa, India (hybrid event) + Oct 3 2023 - remote day diff --git a/src/content/community/meetups.md b/src/content/community/meetups.md index 000eeb43..a12a5349 100644 --- a/src/content/community/meetups.md +++ b/src/content/community/meetups.md @@ -72,7 +72,8 @@ Do you have a local React.js meetup? Add it here! (Please keep the list alphabet ## England (UK) {/*england-uk*/} * [Manchester](https://www.meetup.com/Manchester-React-User-Group/) * [React.JS Girls London](https://www.meetup.com/ReactJS-Girls-London/) -* [React London : Bring Your Own Project](https://www.meetup.com/React-London-Bring-Your-Own-Project/) +* [React Advanced London](https://guild.host/react-advanced-london) +* [React Native London](https://guild.host/RNLDN) ## France {/*france*/} * [Nantes](https://www.meetup.com/React-Nantes/) @@ -86,7 +87,7 @@ Do you have a local React.js meetup? Add it here! (Please keep the list alphabet * [Karlsruhe](https://www.meetup.com/react_ka/) * [Kiel](https://www.meetup.com/Kiel-React-Native-Meetup/) * [Munich](https://www.meetup.com/ReactJS-Meetup-Munich/) -* [React Berlin](https://www.meetup.com/React-Open-Source/) +* [React Berlin](https://guild.host/react-berlin) ## Greece {/*greece*/} * [Athens](https://www.meetup.com/React-To-React-Athens-MeetUp/) @@ -108,7 +109,7 @@ Do you have a local React.js meetup? Add it here! (Please keep the list alphabet * [Indonesia](https://www.meetup.com/reactindonesia/) ## Ireland {/*ireland*/} -* [Dublin](https://www.meetup.com/ReactJS-Dublin/) +* [Dublin](https://guild.host/reactjs-dublin) ## Israel {/*israel*/} * [Tel Aviv](https://www.meetup.com/ReactJS-Israel/) @@ -124,7 +125,7 @@ Do you have a local React.js meetup? Add it here! (Please keep the list alphabet * [Penang](https://www.facebook.com/groups/reactpenang/) ## Netherlands {/*netherlands*/} -* [Amsterdam](https://www.meetup.com/React-Amsterdam/) +* [Amsterdam](https://guild.host/react-amsterdam) ## New Zealand {/*new-zealand*/} * [Wellington](https://www.meetup.com/React-Wellington/) @@ -201,6 +202,7 @@ Do you have a local React.js meetup? Add it here! (Please keep the list alphabet * [New York, NY - React Ladies](https://www.meetup.com/React-Ladies/) * [New York, NY - React Native](https://www.meetup.com/React-Native-NYC/) * [New York, NY - useReactNYC](https://www.meetup.com/useReactNYC/) +* [New York, NY - React.NYC](https://guild.host/react-nyc) * [Omaha, NE - ReactJS/React Native](https://www.meetup.com/omaha-react-meetup-group/) * [Palo Alto, CA - React Native](https://www.meetup.com/React-Native-Silicon-Valley/) * [Philadelphia, PA - ReactJS](https://www.meetup.com/Reactadelphia/) diff --git a/src/content/community/versioning-policy.md b/src/content/community/versioning-policy.md index eb38f8c2..fad926c5 100644 --- a/src/content/community/versioning-policy.md +++ b/src/content/community/versioning-policy.md @@ -80,7 +80,7 @@ This section will be most relevant to developers who work on frameworks, librari Each of React's release channels is designed for a distinct use case: - [**Latest**](#latest-channel) is for stable, semver React releases. It's what you get when you install React from npm. This is the channel you're already using today. **User-facing applications that consume React directly use this channel.** -- [**Canary**](#canary-channel) tracks the main branch of the React source code repository. Think of these as release candidates for the next semver release. **[Frameworks or other curated setups may choose to use this channel with a pinned version of React.](/blog/2023/05/03/react-canaries) You can also Canaries for integration testing between React and third party projects.** +- [**Canary**](#canary-channel) tracks the main branch of the React source code repository. Think of these as release candidates for the next semver release. **[Frameworks or other curated setups may choose to use this channel with a pinned version of React.](/blog/2023/05/03/react-canaries) You can also use Canaries for integration testing between React and third party projects.** - [**Experimental**](#experimental-channel) includes experimental APIs and features that aren't available in the stable releases. These also track the main branch, but with additional feature flags turned on. Use this to try out upcoming features before they are released. All releases are published to npm, but only Latest uses semantic versioning. Prereleases (those in the Canary and Experimental channels) have versions generated from a hash of their contents and the commit date, e.g. `18.3.0-canary-388686f29-20230503` for Canary and `0.0.0-experimental-388686f29-20230503` for Experimental. diff --git a/src/content/errors/377.md b/src/content/errors/377.md new file mode 100644 index 00000000..6269e9d0 --- /dev/null +++ b/src/content/errors/377.md @@ -0,0 +1,13 @@ + + +In the minified production build of React, we avoid sending down full error messages in order to reduce the number of bytes sent over the wire. + + + +We highly recommend using the development build locally when debugging your app since it tracks additional debug info and provides helpful warnings about potential problems in your apps, but if you encounter an exception while using the production build, this page will reassemble the original error message. + +The full text of the error you just encountered is: + + + +This error occurs when you pass a BigInt value from a Server Component to a Client Component. diff --git a/src/content/errors/generic.md b/src/content/errors/generic.md new file mode 100644 index 00000000..27c3ca52 --- /dev/null +++ b/src/content/errors/generic.md @@ -0,0 +1,11 @@ + + +In the minified production build of React, we avoid sending down full error messages in order to reduce the number of bytes sent over the wire. + + + +We highly recommend using the development build locally when debugging your app since it tracks additional debug info and provides helpful warnings about potential problems in your apps, but if you encounter an exception while using the production build, this page will reassemble the original error message. + +The full text of the error you just encountered is: + + diff --git a/src/content/errors/index.md b/src/content/errors/index.md new file mode 100644 index 00000000..25746d25 --- /dev/null +++ b/src/content/errors/index.md @@ -0,0 +1,10 @@ + + +In the minified production build of React, we avoid sending down full error messages in order to reduce the number of bytes sent over the wire. + + + + +We highly recommend using the development build locally when debugging your app since it tracks additional debug info and provides helpful warnings about potential problems in your apps, but if you encounter an exception while using the production build, the error message will include just a link to the docs for the error. + +For an example, see: [https://react.dev/errors/149](/errors/421). diff --git a/src/content/learn/updating-arrays-in-state.md b/src/content/learn/updating-arrays-in-state.md index ea861931..61e4f4e2 100644 --- a/src/content/learn/updating-arrays-in-state.md +++ b/src/content/learn/updating-arrays-in-state.md @@ -409,7 +409,6 @@ For example: ```js import { useState } from 'react'; -let nextId = 3; const initialList = [ { id: 0, title: 'Big Bellies' }, { id: 1, title: 'Lunar Landscape' }, diff --git a/src/content/reference/react-dom/components/form.md b/src/content/reference/react-dom/components/form.md index 7a4b26b3..4b7d5d8a 100644 --- a/src/content/reference/react-dom/components/form.md +++ b/src/content/reference/react-dom/components/form.md @@ -5,7 +5,7 @@ canary: true -React's extensions to `
` are currently only available in React's canary and experimental channels. In stable releases of React `` works only as a [built-in browser HTML component](https://react.dev/reference/react-dom/components#all-html-components). Learn more about [React's release channels here](/community/versioning-policy#all-release-channels). +React's extensions to `` are currently only available in React's canary and experimental channels. In stable releases of React, `` works only as a [built-in browser HTML component](https://react.dev/reference/react-dom/components#all-html-components). Learn more about [React's release channels here](/community/versioning-policy#all-release-channels). @@ -118,7 +118,7 @@ function AddToCart({productId}) { } ``` -In lieu of using hidden form fields to provide data to the ``'s action, you can call the `bind` method to supply it with extra arguments. This will bind a new argument (`productId`) to the function in addition to the `formData` that is passed as a argument to the function. +In lieu of using hidden form fields to provide data to the ``'s action, you can call the `bind` method to supply it with extra arguments. This will bind a new argument (`productId`) to the function in addition to the `formData` that is passed as an argument to the function. ```jsx [[1, 8, "bind"], [2,8, "productId"], [2,4, "productId"], [3,4, "formData"]] import { updateCart } from './lib.js'; @@ -278,7 +278,7 @@ export async function deliverMessage(message) { ### Handling form submission errors {/*handling-form-submission-errors*/} -In some cases the function called by a ``'s `action` prop throw an error. You can handle these errors by wrapping `` in an Error Boundary. If the function called by a ``'s `action` prop throws an error, the fallback for the error boundary will be displayed. +In some cases the function called by a ``'s `action` prop throws an error. You can handle these errors by wrapping `` in an Error Boundary. If the function called by a ``'s `action` prop throws an error, the fallback for the error boundary will be displayed. diff --git a/src/content/reference/react-dom/components/input.md b/src/content/reference/react-dom/components/input.md index f5685aa6..36452de9 100644 --- a/src/content/reference/react-dom/components/input.md +++ b/src/content/reference/react-dom/components/input.md @@ -34,7 +34,7 @@ To display an input, render the [built-in browser ``](https://developer.m -React's extensions to the `formAction` prop are currently only available in React's Canary and experimental channels. In stable releases of React `formAction` works only as a [built-in browser HTML component](https://react.dev/reference/react-dom/components#all-html-components). Learn more about [React's release channels here](/community/versioning-policy#all-release-channels). +React's extensions to the `formAction` prop are currently only available in React's Canary and experimental channels. In stable releases of React, `formAction` works only as a [built-in browser HTML component](https://react.dev/reference/react-dom/components#all-html-components). Learn more about [React's release channels here](/community/versioning-policy#all-release-channels). [`formAction`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#formaction): A string or function. Overrides the parent `` for `type="submit"` and `type="image"`. When a URL is passed to `action` the form will behave like a standard HTML form. When a function is passed to `formAction` the function will handle the form submission. See [``](/reference/react-dom/components/form#props). diff --git a/src/content/reference/react-dom/hooks/useFormState.md b/src/content/reference/react-dom/hooks/useFormState.md index bcd43170..8384fcbd 100644 --- a/src/content/reference/react-dom/hooks/useFormState.md +++ b/src/content/reference/react-dom/hooks/useFormState.md @@ -104,7 +104,7 @@ function MyComponent() { When the form is submitted, the action function that you provided will be called. Its return value will become the new current state of the form. -The action that you provide will also receive a new first argument, namely the current state of the form. The first time the form is submitted, this will be the initial state you provided, while with subsequent submissions, it will be the return value from the last time the action was called. The rest of the arguments are the same as if `useFormState` had not been used +The action that you provide will also receive a new first argument, namely the current state of the form. The first time the form is submitted, this will be the initial state you provided, while with subsequent submissions, it will be the return value from the last time the action was called. The rest of the arguments are the same as if `useFormState` had not been used. ```js [[3, 1, "action"], [1, 1, "currentState"]] function action(currentState, formData) { diff --git a/src/content/reference/react/experimental_taintObjectReference.md b/src/content/reference/react/experimental_taintObjectReference.md index 335f659c..b5b9e513 100644 --- a/src/content/reference/react/experimental_taintObjectReference.md +++ b/src/content/reference/react/experimental_taintObjectReference.md @@ -64,11 +64,11 @@ experimental_taintObjectReference( #### Caveats {/*caveats*/} -- Recreating or cloning a tainted object creates a new untained object which may contain sensitive 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. +- Recreating or cloning a tainted object creates a new untainted object which may contain sensitive 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. +**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 untainted 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. diff --git a/src/content/reference/react/forwardRef.md b/src/content/reference/react/forwardRef.md index e1e96a20..04d69287 100644 --- a/src/content/reference/react/forwardRef.md +++ b/src/content/reference/react/forwardRef.md @@ -42,7 +42,7 @@ const MyInput = forwardRef(function MyInput(props, ref) { #### Caveats {/*caveats*/} -* In Strict Mode, React will **call your render function twice** in order to [help you find accidental impurities.](#my-initializer-or-updater-function-runs-twice) This is development-only behavior and does not affect production. If your render function is pure (as it should be), this should not affect the logic of your component. The result from one of the calls will be ignored. +* In Strict Mode, React will **call your render function twice** in order to [help you find accidental impurities.](/reference/react/useState#my-initializer-or-updater-function-runs-twice) This is development-only behavior and does not affect production. If your render function is pure (as it should be), this should not affect the logic of your component. The result from one of the calls will be ignored. --- diff --git a/src/content/reference/react/useContext.md b/src/content/reference/react/useContext.md index bba445d0..ce06e703 100644 --- a/src/content/reference/react/useContext.md +++ b/src/content/reference/react/useContext.md @@ -457,7 +457,7 @@ function LoginForm() { const {setCurrentUser} = useContext(CurrentUserContext); const [firstName, setFirstName] = useState(''); const [lastName, setLastName] = useState(''); - const canLogin = firstName !== '' && lastName !== ''; + const canLogin = firstName.trim() !== '' && lastName.trim() !== ''; return ( <>