Next.js中如何将页面Hook返回数据传递给带getLayout的持久化布局
实现方案
你遇到的核心问题是useFetchDataCustom是页面组件内部的运行时Hook,返回的数据无法直接被组件外部的静态getLayout属性访问,下面提供两种可保留getLayout函数的实现方案:
方案1:调整页面渲染逻辑,最小程度兼容原有代码
不需要修改现有getLayout的通用调用逻辑,只需要给需要传参的页面加个标记即可,其他不需要传自定义数据的页面完全不受影响。
步骤1:修改布局组件,支持接收额外参数
// /components/SiteLayout.js // 组件新增接收data参数 const SiteLayout = ({ children, data }) => <div>{/* 此处可直接使用data */}</div> // getLayout新增第二个参数layoutProps,接收要传给布局的属性 export const getLayout = (page, layoutProps = {}) => <SiteLayout {...layoutProps}>{page}</SiteLayout> export default SiteLayout
// /components/AccountSettingsLayout.js import { getLayout as getSiteLayout } from './SiteLayout' // 组件新增接收data参数 const AccountSettingsLayout = ({ children, data }) => <div>{/* 此处可直接使用data */}</div> // getLayout新增第二个参数layoutProps,拆分后传给两层布局 export const getLayout = (page, layoutProps = {}) => getSiteLayout(<AccountSettingsLayout {...layoutProps}>{page}</AccountSettingsLayout>, layoutProps) export default AccountSettingsLayout
步骤2:修改页面组件,内部调用getLayout传参
// /pages/account-settings/[id].js import { getLayout } from '../../components/AccountSettingsLayout' const AccountSettingsBasicInformation = ({customProp}) => { const data = useFetchDataCustom() // 组件内部直接调用getLayout,传入获取到的data return getLayout( <div> 页面原有内容 </div>, { data } ) } // 保留原有getLayout静态属性,不影响全局复用 AccountSettingsBasicInformation.getLayout = getLayout // 新增标记,告诉_app当前页面已经内部处理了布局包裹 AccountSettingsBasicInformation.hasInternalLayout = true export default AccountSettingsBasicInformation
步骤3:修改_app.js兼容内部布局标记
//_app.js class MyApp extends App { render() { const { Component, pageProps } = this.props const getLayout = Component.getLayout || ((page) => page) // 如果组件已经内部处理布局,直接返回组件即可,避免重复包裹 if (Component.hasInternalLayout) { return <Component {...pageProps} /> } return getLayout(<Component {...pageProps} />) } } export default MyApp
方案2:React Context全局共享数据,完全不改动原有逻辑
如果不想改动现有getLayout和_app.js的逻辑,可以用Context做数据透传,解耦性更强,后续新增要共享的参数也不需要修改布局定义。
步骤1:新建全局Context
// /contexts/LayoutDataContext.js import { createContext, useContext } from 'react' const LayoutDataContext = createContext(null) export const LayoutDataProvider = LayoutDataContext.Provider // 布局组件用这个Hook拿数据 export const useLayoutData = () => useContext(LayoutDataContext)
步骤2:页面组件用Provider包裹内容
// /pages/account-settings/[id].js import { getLayout } from '../../components/AccountSettingsLayout' import { LayoutDataProvider } from '../../contexts/LayoutDataContext' const AccountSettingsBasicInformation = ({customProp}) => { const data = useFetchDataCustom() return ( <LayoutDataProvider value={data}> <div>页面原有内容</div> </LayoutDataProvider> ) } AccountSettingsBasicInformation.getLayout = getLayout // 不需要加其他标记,原有逻辑完全保留 export default AccountSettingsBasicInformation
步骤3:布局组件用Hook拿数据
// /components/SiteLayout.js import { useLayoutData } from '../contexts/LayoutDataContext' const SiteLayout = ({ children }) => { const data = useLayoutData() return <div>{/* 此处直接使用data */}</div> } export const getLayout = page => <SiteLayout>{page}</SiteLayout> export default SiteLayout
AccountSettingsLayout组件同理,直接调用useLayoutData即可拿到数据,不需要修改原有getLayout的定义。
内容的提问来源于stack exchange,提问作者tzhang
相关产品推荐
相关产品推荐

