Skip to content

Commit

Permalink
Improve errors caused by client hook called in a server component (#4…
Browse files Browse the repository at this point in the history
…4538)

Improve errors caused by client hook called in a server component.

Before
![image](https://user-images.githubusercontent.com/25056922/210399932-02d1071c-0d06-4260-8f1d-7da436e8bca9.png)


After
![image](https://user-images.githubusercontent.com/25056922/210399817-f949fb14-2f81-4309-bfcb-27af38ea1329.png)

Fixes NEXT-311

## Bug

- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md)

## Feature

- [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [ ] Related issues linked using `fixes #number`
- [ ] [e2e](https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md)

## Documentation / Examples

- [ ] Make sure the linting passes by running `pnpm build && pnpm lint`
- [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
  • Loading branch information
hanneslund committed Jan 4, 2023
1 parent 90b86be commit d89bdb2
Show file tree
Hide file tree
Showing 5 changed files with 314 additions and 106 deletions.
4 changes: 4 additions & 0 deletions errors/manifest.json
Expand Up @@ -777,6 +777,10 @@
{
"title": "context-in-server-component",
"path": "/errors/context-in-server-component.md"
},
{
"title": "react-client-hook-in-server-component",
"path": "/errors/react-client-hook-in-server-component.md"
}
]
}
Expand Down
40 changes: 40 additions & 0 deletions errors/react-client-hook-in-server-component.md
@@ -0,0 +1,40 @@
# React client hook in Server Component

#### Why This Error Occurred

You are using a React client hook in a Server Component.

#### Possible Ways to Fix It

Mark the component using the hook as a Client Component by adding `'use client'` at the top of the file.

##### Before

```jsx
import { useEffect } from 'react'

export default function Page() {
useEffect(() => {
console.log('in useEffect')
})
return <p>Hello world</p>
}
```

##### After

```jsx
'use client'
import { useEffect } from 'react'

export default function Page() {
useEffect(() => {
console.log('in useEffect')
})
return <p>Hello world</p>
}
```

### Useful Links

[Server and Client Components](https://beta.nextjs.org/docs/rendering/server-and-client-components)
41 changes: 35 additions & 6 deletions packages/next/src/lib/format-server-error.ts
@@ -1,12 +1,41 @@
const invalidServerComponentReactHooks = [
'useDeferredValue',
'useEffect',
'useImperativeHandle',
'useInsertionEffect',
'useLayoutEffect',
'useReducer',
'useRef',
'useState',
'useSyncExternalStore',
'useTransition',
]

function setMessage(error: Error, message: string): void {
error.message = message
if (error.stack) {
const lines = error.stack.split('\n')
lines[0] = message
error.stack = lines.join('\n')
}
}

export function formatServerError(error: Error): void {
if (error.message.includes('createContext is not a function')) {
const message =
setMessage(
error,
'createContext only works in Client Components. Add the "use client" directive at the top of the file to use it. Read more: https://nextjs.org/docs/messages/context-in-server-component'
error.message = message
if (error.stack) {
const lines = error.stack.split('\n')
lines[0] = message
error.stack = lines.join('\n')
)
return
}

for (const clientHook of invalidServerComponentReactHooks) {
if (error.message.includes(`${clientHook} is not a function`)) {
setMessage(
error,
`${clientHook} only works in Client Components. Add the "use client" directive at the top of the file to use it. Read more: https://nextjs.org/docs/messages/react-client-hook-in-server-component`
)
return
}
}
}
5 changes: 5 additions & 0 deletions packages/next/src/server/app-render.tsx
Expand Up @@ -47,6 +47,7 @@ import {
} from '../client/components/app-router-headers'
import type { StaticGenerationStore } from '../client/components/static-generation-async-storage'
import { DefaultHead } from '../client/components/head'
import { formatServerError } from '../lib/format-server-error'

const isEdgeRuntime = process.env.NEXT_RUNTIME === 'edge'

Expand Down Expand Up @@ -227,6 +228,10 @@ function createErrorHandler(
return err.digest
}

// Format server errors in development to add more helpful error messages
if (process.env.NODE_ENV !== 'production') {
formatServerError(err)
}
// Used for debugging error source
// console.error(_source, err)
console.error(err)
Expand Down

0 comments on commit d89bdb2

Please sign in to comment.