diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000000..4e68d966d7f --- /dev/null +++ b/LICENSE @@ -0,0 +1 @@ +MIT License - Copyright (c) 2026 Ian Alloway diff --git a/src/content/learn/extracting-state-logic-into-a-reducer.md b/src/content/learn/extracting-state-logic-into-a-reducer.md index 5c08c012390..5205617404a 100644 --- a/src/content/learn/extracting-state-logic-into-a-reducer.md +++ b/src/content/learn/extracting-state-logic-into-a-reducer.md @@ -183,11 +183,11 @@ Each of its event handlers calls `setTasks` in order to update the state. As thi Reducers are a different way to handle state. You can migrate from `useState` to `useReducer` in three steps: -1. **Move** from setting state to dispatching actions. +1. **Move** from setting state to dispatching Actions. 2. **Write** a reducer function. 3. **Use** the reducer from your component. -### Step 1: Move from setting state to dispatching actions {/*step-1-move-from-setting-state-to-dispatching-actions*/} +### Step 1: Move from setting state to dispatching Actions {/*step-1-move-from-setting-state-to-dispatching-actions*/} Your event handlers currently specify _what to do_ by setting state: @@ -226,7 +226,7 @@ Remove all the state setting logic. What you are left with are three event handl - `handleChangeTask(task)` is called when the user toggles a task or presses "Save". - `handleDeleteTask(taskId)` is called when the user presses "Delete". -Managing state with reducers is slightly different from directly setting state. Instead of telling React "what to do" by setting state, you specify "what the user just did" by dispatching "actions" from your event handlers. (The state update logic will live elsewhere!) So instead of "setting `tasks`" via an event handler, you're dispatching an "added/changed/deleted a task" action. This is more descriptive of the user's intent. +Managing state with reducers is slightly different from directly setting state. Instead of telling React "what to do" by setting state, you specify "what the user just did" by dispatching "Actions" from your event handlers. (The state update logic will live elsewhere!) So instead of "setting `tasks`" via an event handler, you're dispatching an "added/changed/deleted a task" action. This is more descriptive of the user's intent. ```js function handleAddTask(text) { @@ -862,7 +862,7 @@ li { -Component logic can be easier to read when you separate concerns like this. Now the event handlers only specify _what happened_ by dispatching actions, and the reducer function determines _how the state updates_ in response to them. +Component logic can be easier to read when you separate concerns like this. Now the event handlers only specify _what happened_ by dispatching Actions, and the reducer function determines _how the state updates_ in response to them. ## Comparing `useState` and `useReducer` {/*comparing-usestate-and-usereducer*/} diff --git a/src/content/learn/managing-state.md b/src/content/learn/managing-state.md index ef7b76e04c2..10db94f6c39 100644 --- a/src/content/learn/managing-state.md +++ b/src/content/learn/managing-state.md @@ -502,7 +502,7 @@ Read **[Preserving and Resetting State](/learn/preserving-and-resetting-state)** ## Extracting state logic into a reducer {/*extracting-state-logic-into-a-reducer*/} -Components with many state updates spread across many event handlers can get overwhelming. For these cases, you can consolidate all the state update logic outside your component in a single function, called "reducer". Your event handlers become concise because they only specify the user "actions". At the bottom of the file, the reducer function specifies how the state should update in response to each action! +Components with many state updates spread across many event handlers can get overwhelming. For these cases, you can consolidate all the state update logic outside your component in a single function, called "reducer". Your event handlers become concise because they only specify the user "Actions". At the bottom of the file, the reducer function specifies how the state should update in response to each action! diff --git a/src/content/learn/typescript.md b/src/content/learn/typescript.md index 2e6f7edc53e..c749663ee29 100644 --- a/src/content/learn/typescript.md +++ b/src/content/learn/typescript.md @@ -286,7 +286,7 @@ export default App = AppTSX; 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: +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 its existence and throw an error when not present: ```js {5, 16-20} import { createContext, useContext, useState, useMemo } from 'react'; diff --git a/src/content/reference/rsc/use-client.md b/src/content/reference/rsc/use-client.md index 4c6051977ef..813693230ba 100644 --- a/src/content/reference/rsc/use-client.md +++ b/src/content/reference/rsc/use-client.md @@ -376,4 +376,96 @@ These libraries may rely on component Hooks or client APIs. Third-party componen If these libraries have been updated to be compatible with React Server Components, then they will already include `'use client'` markers of their own, allowing you to use them directly from your Server Components. If a library hasn't been updated, or if a component needs props like event handlers that can only be specified on the client, you may need to add your own Client Component file in between the third-party Client Component and your Server Component where you'd like to use it. -[TODO]: <> (Troubleshooting - need use-cases) +## Troubleshooting {/*troubleshooting*/} + +### "Functions cannot be passed directly to Client Components" error {/*functions-cannot-be-passed-directly*/} + +This error occurs when you try to pass a function from a Server Component to a Client Component. + +```js +// Server Component +import ClientButton from './ClientButton'; + +export default function Page() { + function handleClick() { + console.log('clicked'); + } + // This will error! + return ; +} +``` + +Functions are not serializable, so they cannot be passed from server to client. To fix this, either move the function to the Client Component, or use a [Server Function](/reference/rsc/server-functions) with `'use server'`. + +```js +// Solution 1: Move the handler to the Client Component +'use client'; + +export default function ClientButton() { + function handleClick() { + console.log('clicked'); + } + return ; +} +``` + +```js +// Solution 2: Use a Server Function +'use server'; + +export async function handleClick() { + console.log('clicked on server'); +} +``` + +### Component uses hooks but doesn't have `'use client'` {/*component-uses-hooks-without-use-client*/} + +If you're using hooks like `useState`, `useEffect`, or `useContext` in a component, you need to mark it as a Client Component. + +```js +// This will error - hooks require 'use client' +import { useState } from 'react'; + +export default function Counter() { + const [count, setCount] = useState(0); + return ; +} +``` + +Add `'use client'` at the top of the file: + +```js +'use client'; + +import { useState } from 'react'; + +export default function Counter() { + const [count, setCount] = useState(0); + return ; +} +``` + +### Third-party library doesn't work in Server Components {/*third-party-library-server-component*/} + +Many third-party libraries were written before Server Components existed and may use client-only features. If you get an error when importing a library in a Server Component, create a wrapper Client Component: + +```js +// components/ChartWrapper.js +'use client'; + +import { Chart } from 'third-party-chart-library'; + +export default function ChartWrapper(props) { + return ; +} +``` + +Then import the wrapper in your Server Component: + +```js +// page.js (Server Component) +import ChartWrapper from './components/ChartWrapper'; + +export default function Page() { + return ; +} diff --git a/src/content/reference/rsc/use-server.md b/src/content/reference/rsc/use-server.md index 359637aa7dc..cb4f90a9e81 100644 --- a/src/content/reference/rsc/use-server.md +++ b/src/content/reference/rsc/use-server.md @@ -215,3 +215,97 @@ export default async function incrementLike() { ``` To read a Server Function return value, you'll need to `await` the promise returned. + +## Troubleshooting {/*troubleshooting*/} + +### "Server Functions cannot be called during render" error {/*server-functions-cannot-be-called-during-render*/} + +Server Functions should only be called in response to user actions (like form submissions or button clicks), not during component rendering. + +```js +// This will error - calling during render +function BadComponent() { + const data = await serverFunction(); // Don't do this! + return
{data}
; +} + +// Correct - call in response to user action +function GoodComponent() { + const [data, setData] = useState(null); + + const handleClick = async () => { + const result = await serverFunction(); + setData(result); + }; + + return ; +} +``` + +For data fetching during render, use a Server Component instead of a Server Function. + +### "Arguments must be serializable" error {/*arguments-must-be-serializable*/} + +Server Functions can only receive serializable arguments. You cannot pass functions, class instances, or React elements. + +```js +// This will error - functions aren't serializable +async function saveData(callback) { + 'use server'; + // ... +} + +// Correct - pass serializable data +async function saveData(userId, formData) { + 'use server'; + // ... +} +``` + +### Server Function not updating the UI {/*server-function-not-updating-ui*/} + +If your Server Function runs but the UI doesn't update, make sure you're calling it inside a Transition when outside a form: + +```js +import { useTransition } from 'react'; + +function MyComponent() { + const [isPending, startTransition] = useTransition(); + + const handleClick = () => { + // Wrap in startTransition for proper UI updates + startTransition(async () => { + await myServerFunction(); + }); + }; + + return ; +} +``` + +### "use server" directive not working {/*use-server-directive-not-working*/} + +The `'use server'` directive must be: +1. At the very beginning of the function body (not before the function) +2. Written with single or double quotes (not backticks) +3. Used only in async functions + +```js +// Wrong - directive before function +'use server'; +async function myFunction() { + // ... +} + +// Correct - directive inside function body +async function myFunction() { + 'use server'; + // ... +} + +// Also correct - at top of file to mark all exports +'use server'; + +export async function functionA() { /* ... */ } +export async function functionB() { /* ... */ } +```