Доступ к данным из getServerSideProps в пользовательском документе Next.js

#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, но мне интересно, есть ли более простой способ.

Есть ли способ структурировать это так, чтобы мои данные были более легко доступны в _document.js, передано из getServerSideProps()?

Комментарии:

1. Next.js пользовательский _document не поддерживает методы извлечения данных , такие как getStaticProps или getServerSideProps . Вместо этого вам следует перенести эту логику в свой обычай _app .

Ответ №1:

Вот простой способ получить getServerSideProps() данные вашей страницы в свой _document.js , прежде чем приложение будет загружено.

Итак, как я уже упоминал, вы можете сделать некоторые забавные вещи с предварительно созданным html-кодом, который используется для использования данных getServerSideProps (). Это халтурное решение и очень ненадежное. Однако в моем случае я просто добавляю имя класса, не являющееся следствием, в свой документ (с защитой от сбоев на интерфейсе), так что даже если это не удастся, все будет в порядке.

Будьте очень осторожны, если вы решите использовать это.

Вот как это работает:

У меня есть 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'
}
 

На данный момент я могу добавить это значение в качестве имени класса в свой 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
 

Поскольку эти данные кажутся несколько доступными между ними, я надеюсь, что есть более официальный способ сделать это?