从自定义文档 Next.js 中的 getServerSideProps 访问数据

时间:2021-07-22 15:51:06

标签: next.js

我正在使用 getServerSideProps 获取事件的详细信息。我的理解是,这些数据将在服务器上获取,然后会发生某种预渲染,然后将上下文对象传递到 _document.js。我知道 _document.js 将在每个请求上呈现,但如果存在该服务器数据,我想向我的 html 添加一个类名(以防止在执行此客户端时闪烁)。

这是我的 getServerSideProps 函数:

export async function getServerSideProps(context) {
    const shortId  = context.params.short_id
    const event = await GETrequest({ endpoint: API_PUBLIC_EVENT({ shortId }) })

    return {
        props: {
            event,
            shortId
        } // will be passed to the page component as props
    }
}

当我在 _document.js 中时,我能够看到我的 getServerSideProps 的结果可用,就像我在 getInitialProps 中记录上下文时看到的那样:

class MyDocument extends Document {
static async getInitialProps(ctx) {
    const initialProps = await Document.getInitialProps(ctx)
    console.log(initialProps)
    // can see html that has been processed using my data
}
...

我可以想到在那个 initialProps.html 中获取我的 className 的一些hacky 解决方案,但我想知道是否有更简单的方法。

有没有一种方法可以构建它,以便我的数据在 _document.js 中更容易使用,从 getServerSideProps() 传递过来?

1 个答案:

答案 0 :(得分:0)

这是一种在应用加载之前将您网页的 getServerSideProps() 数据导入您的 _document.js 的黑客方法。

所以,正如我所提到的,您可以使用使用 getServerSideProps() 数据的预渲染 html 做一些时髦的事情。这是一个hacky解决方案并且非常不可靠。然而,在我的情况下,我只是在我的文档中添加一个无关紧要的 className(前端带有故障安全功能),因此即使失败也一切正常。

如果您决定使用它,请务必谨慎。

这是它的工作原理:

我在页面上有一个 getServerSideProps()。在这个函数中,我获取一个事件并将其传递到我的页面组件中。在这个组件中,我添加了一个我知道将被呈现的空 div,并附加了我需要的信息。此信息来自我的 getServerSideProps()。

<div data-meta-event-theme={event?.theme?.preset}></div>
// let's say theme preset = light

然后在我的 _document.js 中,在 getInitialProps(ctx) 函数中,我首先检查这是否是服务器请求。如果不是,则跳过(否则构建将失败)。然后我检查请求是否来自适当的路由。我的路线结构类似于 /j/[shortId],它是唯一使用 /j/ 的页面。所以在触发任何逻辑之前,我会检查以确保我们在正确的页面上。

const isInviteScreen = ctx.req.url.indexOf('/j/') > -1

如果我们在正确的页面上,那么我们可以从我们的 html 解析值,然后用它做我们想做的事。

if (isInviteScreen) {
    const html = initialProps.html

    // whatever you set your data to in the html
    // make sure to keep the opening quote "
    const key = 'data-meta-event-theme="'
    
    // you don't have to touch this
    const begin = html.indexOf(key)
    const sub = html.substring(begin + key.length)
    const closingQuote = sub.indexOf('"')
    const value = sub.substring(0, closingQuote)

    console.log(value)
    // logs 'light'
}

此时,我可以将该值作为 className 附加到我的 html 中,瞧!不再闪烁。

这是一个更完整的_document.js

 class MyDocument extends Document {
    static async getInitialProps(ctx) {
        const initialProps = await Document.getInitialProps(ctx)

        // Make sure this operation is on the server and has a request
        if (ctx.req) {
            
            // Make sure we're only doing this for the correct route
            const isInviteScreen = ctx.req.url.indexOf('/j/') > -1
            if (isInviteScreen) {
                const html = initialProps.html

                // Your HTML key here, make sure to keep quote
                const key = 'data-meta-event-theme="'

                // Don't have to touch this
                const begin = html.indexOf(key)
                const sub = html.substring(begin + key.length)
                const closingQuote = sub.indexOf('"')

                // Value that was sent from the page with getServerSideProps()
                const value = sub.substring(0, closingQuote)

                return {
                    ...initialProps,
                    preference: { theme: value }
                }
            }
        }
        
        // Default return just in case
        return {
            ...initialProps,
            preference: { theme: 'light }
        }
    }

    render() {
        const theme = this.props.preference.theme
        return (
            <Html data-theme={theme} className={theme} lang="en">
                <Head />
                <body>
                    <Main />
                    <NextScript />
                </body>
            </Html>
        )
    }
}

export default MyDocument

由于这些数据在两者之间似乎有些可访问,我希望有更正式的方法来做到这一点?

相关问题