Next.js 为客户端自定义挂钩并防止 "did not match server/client" 错误
Next.js custom hook for client side and prevent "did not match server/client" error
如何在Next.js中使用自定义钩子useCustomHook()
并在不使用useEffect()
的情况下防止did not match server/client
错误?例如使用 swr
时。
useData()
在客户端存储数据(context + localStorage)。
我正在尝试使用用户提供的一些数据创建一个静态站点。
function useCustomHook() {
const [{ data }, dispatch] = useData()
if (typeof window !== 'undefined') { // Client side
return { data: data }
}
return { data: undefined }
}
export default function Page() {
const { data } = useCustomHook()
if (!data) {
return <div>No data...</div>
}
return <div>Data {data.foobar}</div>
}
上下文提供者(<DataProvider>
@_app.js
)
import { useReducer, useContext, createContext } from 'react'
const DataStateContext = createContext()
const initialState = {
data: {},
}
const reducer = (state, action) => {
switch (action.type) {
default:
throw new Error(`Unknown action: ${action.type}`)
}
}
export const DataProvider = ({ children }) => {
const localState = initialState
// Get the data from localStorage
try {
localState.data = localStorage.getItem('data')
? JSON.parse(localStorage.getItem('data'))
: null
} catch (err) {}
const [state, dispatch] = useReducer(reducer, localState)
return (
<DataStateContext.Provider value={[state, dispatch]}>
{children}
</DataStateContext.Provider>
)
}
export const useData = () => useContext(DataStateContext)
当我重新加载页面时将导致 Warning: Text content did not match. Server: "No data..." Client: "Data "
。
但是当我使用 useSWR
时,我可以重新加载页面而不会出现错误。此外,请求仅在客户端完成,服务器端 data
只是未定义(服务器根本不发出请求)。这是如何运作的?我已经检查了 swr 的代码,但我不明白。
import useSWR from "swr";
const fetcher = (url) => fetch(url).then((res) => res.json());
export default function App() {
const { data, error } = useSWR(
"https://api.github.com/repos/vercel/swr",
fetcher
);
if (error) return "An error has occurred.";
if (!data) return "Loading...";
return (
<div>{data.description}</div>
);
}
您想在挂钩中结合 useEffect()
记住 return 值,这也将防止多次渲染。
function useCustomHook() {
const [{ data }, dispatch] = useData()
const [clientData, setClientData] = useState()
const hasWindow = typeof window !== 'undefined'
useEffect(() => {
if (hasWindow) {
setClientData(data)
}
}, [hasWindow, data])
const value = useMemo(
() => ({
data: clientData,
}),
[clientData]
)
return value
}
如何在Next.js中使用自定义钩子useCustomHook()
并在不使用useEffect()
的情况下防止did not match server/client
错误?例如使用 swr
时。
useData()
在客户端存储数据(context + localStorage)。
我正在尝试使用用户提供的一些数据创建一个静态站点。
function useCustomHook() {
const [{ data }, dispatch] = useData()
if (typeof window !== 'undefined') { // Client side
return { data: data }
}
return { data: undefined }
}
export default function Page() {
const { data } = useCustomHook()
if (!data) {
return <div>No data...</div>
}
return <div>Data {data.foobar}</div>
}
上下文提供者(<DataProvider>
@_app.js
)
import { useReducer, useContext, createContext } from 'react'
const DataStateContext = createContext()
const initialState = {
data: {},
}
const reducer = (state, action) => {
switch (action.type) {
default:
throw new Error(`Unknown action: ${action.type}`)
}
}
export const DataProvider = ({ children }) => {
const localState = initialState
// Get the data from localStorage
try {
localState.data = localStorage.getItem('data')
? JSON.parse(localStorage.getItem('data'))
: null
} catch (err) {}
const [state, dispatch] = useReducer(reducer, localState)
return (
<DataStateContext.Provider value={[state, dispatch]}>
{children}
</DataStateContext.Provider>
)
}
export const useData = () => useContext(DataStateContext)
当我重新加载页面时将导致 Warning: Text content did not match. Server: "No data..." Client: "Data "
。
但是当我使用 useSWR
时,我可以重新加载页面而不会出现错误。此外,请求仅在客户端完成,服务器端 data
只是未定义(服务器根本不发出请求)。这是如何运作的?我已经检查了 swr 的代码,但我不明白。
import useSWR from "swr";
const fetcher = (url) => fetch(url).then((res) => res.json());
export default function App() {
const { data, error } = useSWR(
"https://api.github.com/repos/vercel/swr",
fetcher
);
if (error) return "An error has occurred.";
if (!data) return "Loading...";
return (
<div>{data.description}</div>
);
}
您想在挂钩中结合 useEffect()
记住 return 值,这也将防止多次渲染。
function useCustomHook() {
const [{ data }, dispatch] = useData()
const [clientData, setClientData] = useState()
const hasWindow = typeof window !== 'undefined'
useEffect(() => {
if (hasWindow) {
setClientData(data)
}
}, [hasWindow, data])
const value = useMemo(
() => ({
data: clientData,
}),
[clientData]
)
return value
}