使用 HOC 在 Next.js getInitialProps 中访问使用的 React.Context

Posted

技术标签:

【中文标题】使用 HOC 在 Next.js getInitialProps 中访问使用的 React.Context【英文标题】:Accessing consumed React.Context in Next.js getInitialProps using HOC 【发布时间】:2019-07-09 14:17:13 【问题描述】:

我正在尝试使用一个简单的服务来抽象我的 API 调用,该服务提供了一个非常简单的方法,它只是一个 HTTP 调用。我将此实现存储在 React Context 中,并在我的 _app.js 中使用它的提供程序,以便 API 全局可用,但我在实际使用页面中的上下文时遇到了问题。

pages/_app.js

import React from 'react'
import App,  Container  from 'next/app'

import ApiProvider from '../Providers/ApiProvider';

import getConfig from 'next/config'
const  serverRuntimeConfig, publicRuntimeConfig  = getConfig()

export default class Webshop extends App 

    static async getInitialProps( Component, router, ctx ) 
        let pageProps = 

        if (Component.getInitialProps) 
            pageProps = await Component.getInitialProps(ctx)
        

        return  pageProps 
    

    render () 
        const  Component, pageProps  = this.props

        return (
            <Container>
                <ApiProvider endpoint=publicRuntimeConfig.api_endpoint>
                    <Component ...pageProps />
                </ApiProvider>
            </Container>
        );
    

服务/Api.js

import fetch from 'unfetch'

function Api (config)

    const apiUrl = config.endpoint;

    async function request (url) 
        return fetch(apiUrl + '/' + url);
    ;

    this.decode = async function (code) 
        const res = request('/decode?code=' + code);
        const json = await res.json();
        return json;
    

    return this;


export default Api;

Providers/ApiProvider.js

import React,  Component  from 'react';
import Api from '../Services/Api';

const defaultStore = null;

class ApiProvider extends React.Component

    state = 
        api: null
    ;

    constructor (props) 
        super(props);

        this.state.api = new Api( endpoint: props.endpoint );
    

    render () 
        return (
            <ApiContext.Provider value=this.state.api>
                this.props.children
            </ApiContext.Provider>
        );
    


export const ApiContext = React.createContext(defaultStore);
export default ApiProvider;
export const ApiConsumer = ApiContext.Consumer;
export function withApi(Component) 
    return function withApiHoc(props) 
        return (
            <ApiConsumer> context => <Component ...props api=context /> </ApiConsumer>
        )
    
;

pages/code.js

import React,  Component  from 'react';
import Link from 'next/link';
import  withApi  from '../Providers/ApiProvider';

class Code extends React.Component

    static async getInitialProps ( query, ctx ) 
        const decodedResponse = this.props.api.decode(query.code); // Cannot read property 'api' of undefined

        return 
            code: query.code,
            decoded: decodedResponse
        ;
    

    render () 
        return (
            <div>
                [...]
            </div>
        );
    


let hocCode = withApi(Code);
hocCode.getInitialProps = Code.getInitialProps;
export default hocCode;

问题是我无法访问使用的上下文。我可以在我的getInitialProps 中直接调用fetch,但是我想通过使用一个也接受可配置 URL 的小函数来抽象它。

我做错了什么?

【问题讨论】:

我在尝试使用 Context.Consumer API 访问功能组件中的上下文时遇到了类似的问题。您是否尝试过使用static contextType = ApiContext 将其转换为类组件。不是一个解决方案,但它会帮助你找出这是否是问题 探测的是 getInitialProps 是一个静态方法,因此你不能从它里面访问类实例属性 @ShubhamKhatri 哦,对了……这太明显了。那我怎么能做这样的事情呢?我需要将它作为我的 HOC 的 getInitialProps 的参数传递,对吗?这也是我尝试过的,但是我无法以编程方式访问 React.Context 数据,如果我用消费者上下文包装我的组件,getInitialProps 将不会被正确调用。 【参考方案1】:

您无法以 static 方法 getInitialProps 访问您的提供者的实例,它在 React 树生成之前被调用(当您的提供者可用时)。

我建议您将 API 的 Singelton 保存在 API 模块中,并通过常规导入在 getInitialProps 方法中使用它。

或者,您可以将其注入到 _app getInitialProps 内的 componentPage 中,类似这样:

// _app.jsx
import api from './path/to/your/api.js';

export default class Webshop extends App 
    static async getInitialProps( Component, router, ctx ) 
        let pageProps = 
        ctx.api = api;

        if (Component.getInitialProps) 
            pageProps = await Component.getInitialProps(ctx)
        

        return  pageProps 
    

    render () 
        const  Component, pageProps  = this.props

        return (
            <Container>
                <Component ...pageProps />
            </Container>
        );
    


// PageComponent.jsx

import React,  Component  from 'react';

class Code extends React.Component

    static async getInitialProps ( query, ctx ) 
        const decodedResponse = ctx.api.decode(query.code); // Cannot read property 'api' of undefined

        return 
            code: query.code,
            decoded: decodedResponse
        ;
    

    render () 
        return (
            <div>
                [...]
            </div>
        );
    


export default Code;

这对你有意义吗?

【讨论】:

所以你建议不要使用 React.Context,而是使用单例方法? 是的,singelton 可以注入到我建议的所有页面组件中。 这样我什至可以只在我需要的地方准确地导入它,而不是全局注入它,对吧?

以上是关于使用 HOC 在 Next.js getInitialProps 中访问使用的 React.Context的主要内容,如果未能解决你的问题,请参考以下文章

在 Next.js 中创建一个 HOC(高阶组件)进行身份验证

在 Next.js 中创建一个 HOC(高阶组件)进行身份验证

我无法在 Next.js 中引用图像

我无法在 Next.js 中引用图像

如何使用 next.js 获取客户端 cookie?

使用 Typescript 使用 getInitialProps 将 prop 传递到 Next.js 中的每个页面