使用_document.js在body上的nextjs客户类

时间:2018-04-02 10:03:58

标签: javascript reactjs nextjs

我正在使用此处的示例

https://github.com/zeit/next.js#custom-document

我想更改body标签上的custom_class

我试过在调用组件上传递道具但没有任何作用。我想添加一个黑暗的'基于某些条件的类,但我不知道如何改变它。

编辑:

我不确定这是可能的。在得到nextjs slack频道的帮助后,我被告知

"Pages get rendered client side with next/link
And body is not touched after server side rendering"

我将尝试将内容包装在我生成的另一个标记中并尝试更改它。

6 个答案:

答案 0 :(得分:3)

从当前 Next (10) 和 React (17) 版本开始,如果您想从页面更改 body 类,可以这样做:

// only example: maybe you'll want some logic before, 
// and maybe pass a variable to classList.add()
useEffect( () => { document.querySelector("body").classList.add("home") } );

请注意 useEffect 是一个 Hook,因此它只能用于现代函数组件,不能用于类组件。

可以使用 useEffect Hook 代替“旧的”componentDidMount LifeCycle。

https://reactjs.org/docs/hooks-effect.html https://reactjs.org/docs/hooks-overview.html https://reactjs.org/docs/components-and-props.html

答案 1 :(得分:1)

这并不是您要寻找的答案,但是我能够通过在组件中传递道具,然后在顶层元素上更新类,从而将所有内容包装到我的应用中,从而使功能得以实现。在下面 。这样,每个页面都可以拥有自己的唯一类,并以与我使用主体类相同的方式进行操作。

这是我传递道具的地方

<Layout page_title={meta_title} page_desc={meta_desc} main_class={'contact_page'}>

这是我在“布局”组件中使用它的位置:

<main className={props.main_class}>
    <Header page_title={props.page_title} page_desc={props.page_desc} />

      {props.children}

    <Footer />
</main>

答案 2 :(得分:1)

直接访问 Next js 上的 print 标记的唯一方法是通过 body 文件,但此文件在服务器端呈现,如 Documentation 中所述。

我建议的解决方法是直接从组件访问 _document.js 标记。示例:

body

答案 3 :(得分:0)

我发现的最干净的解决方案不是声明式的,但是效果很好:

import Head from "next/head"

class HeadElement extends React.Component {
    componentDidMount() {
        const {bodyClass} = this.props
        document.querySelector("body").classList.add(bodyClass || "light")
    }

    render() {
        return <Head>
            {/* Whatever other stuff you're using in Head */}
        </Head>
    }
}

export default HeadElement

使用此Head组件,您可以在页面中输入“深色”或“浅色”(以浅色/深色主题为例),作为bodyClass道具。

答案 4 :(得分:0)

针对我不需要很多独特主体类的情况提出的解决方案是创建一个名为BodyClass.js的组件并将该组件导入到我的Layout.js组件中。

BodyClass.js

def init_session(s):
  session = s
  clients = ['ecs', 'iam', 'eks', …]
  for c in clients:
    globals()[f'{c}_client'] = session.client(c)

Layout.js

import  { Component } from 'react';

class BodyClass extends Component {

    componentDidMount() {

        if (window.location.pathname == '/') {
            this.setBodyClass('home');
            
        } else if (window.location.pathname == '/locations') {
            this.setBodyClass('locations');
        } 
    }

    setBodyClass(className) {
        // remove other classes
        document.body.className ='';

        // assign new class
        document.body.classList.add(className);
    }

    render() { 
        return null;
    }

}
 
export default BodyClass;

答案 5 :(得分:-1)

使用 React Helmet 而不是 next/head。

React Helmet 允许您在 <body> 元素上指定属性,例如类、样式等。

要在此处设置您的 pages/_document.js 以使用 React Helment,您需要执行此操作。首先在您的布局组件之一中的某处添加带有 <Helmet><body className="hello"> 标记:

const title = "hello";
const bodyClass = "world";
return (
    <React.Fragment>
      <Helmet>
        <title>{ title }</title>
        <body className={ bodyClass } />
      </Helmet>
    </React.Fragment>
)

现在,配置您的 pages/_document.js 以从 Helmet 读取属性并将它们传递给 Next.js:

import Document, { Html, Head, Main, NextScript } from 'next/document';
import { Helmet } from 'react-helmet';

export default class MyDocument extends Document {
    static async getInitialProps(ctx) {
        const initialProps = await Document.getInitialProps(ctx);
        // see https://github.com/nfl/react-helmet#server-usage for more information
        // 'head' is used by 'renderPage().head', we cannot use it
        return { ...initialProps, helmet: Helmet.renderStatic() };
    }

    // should render on <html>
    get helmetHtmlAttrComponents() {
        return this.props.helmet.htmlAttributes.toComponent();
    }

    // should render on <body>
    get helmetBodyAttrComponents() {
        return this.props.helmet.bodyAttributes.toComponent();
    }

    // should render on <head>
    get helmetHeadComponents() {
        return Object.keys(this.props.helmet)
            .filter((el) => el !== 'htmlAttributes' && el !== 'bodyAttributes')
            .map((el) => this.props.helmet[el].toComponent());
    }

    render() {
        // if you don't like Helmet but you still want to set properties on body use this
        // const pageProps = _.get(this.props, '__NEXT_DATA__.props.pageProps');
        return (
            <Html {...this.helmetHtmlAttrComponents}>
                <Head>{this.helmetHeadComponents}</Head>
                <body {...this.helmetBodyAttrComponents}>
                    <Main />
                    <NextScript />
                </body>
            </Html>
        );
    }
}

注意:如果你不喜欢 Helmet 但你仍然想从传递到你的页面的 props 中设置属性,你可以从 this.props.__NEXT_DATA__.props.pageProps 中读取它们,但它对我来说感觉很糟糕而且不稳定 :)

    render() {
        const pageProps = _.get(this.props, '__NEXT_DATA__.props.pageProps');
        return (
            <Html>
                <Head/>
                <body className={ pageProps.bodyClass }>
                    <Main />
                    <NextScript />
                </body>
            </Html>
        );
    }