[英]How to make conditional overflow style in _document.js (Next.js)
[英]Next.js custom class on body using _document.js
我在这里使用示例
https://github.com/zeit/next.js#custom-document
我想更改 body 标签上的 custom_class
我试过在调用组件时传递道具,但没有任何效果。 我想根据某些条件添加一个“暗”类,但我不知道如何更改它。
编辑:
我不确定这是可能的。 从 nextjs 松弛频道获得帮助后,我被告知
"Pages get rendered client side with next/link
And body is not touched after server side rendering"
我将尝试将内容包装在我生成的另一个标签中,然后尝试更改它。
我发现的最干净的解决方案不是声明性的,但效果很好:
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 组件,您将传入“dark”或“light”(遵循浅色/深色主题的问题示例)作为页面中的 bodyClass 道具。
从当前 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
对于我不需要很多独特主体类的情况,我想出的解决方案是创建一个名为 BodyClass.js 的组件并将该组件导入到我的 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;
import Header from './Header';
import Footer from './Footer';
import BodyClass from '../BodyClass';
const Layout = ({ children }) => {
return (
<React.Fragment>
<BodyClass />
<Header />
{children}
<Footer />
</React.Fragment>
)
}
export default Layout;
直接访问 Next js 上的body
标签的唯一方法是通过_document.js
文件,但该文件在服务器端呈现,如文档中所述。
我建议的解决方法是直接从组件访问body
标记。 例子:
const handleClick = (e) => {
document.querySelector('body').classList.toggle('dark')
}
<div onClick={handleClick}>Toggle</div>
这并不完全是您正在寻找的答案,但我能够通过将一个 prop 传递给组件来在功能上完成同样的事情,该组件然后更新顶级元素上的一个类,该类包含我的应用程序中的所有内容并位于 . 这样,每个页面都可以拥有自己独特的类,并以与使用正文类相同的方式进行操作。
这是我通过道具的地方
<Layout page_title={meta_title} page_desc={meta_desc} main_class={'contact_page'}>
这是我在 Layout 组件中使用它的地方:
<main className={props.main_class}> <Header page_title={props.page_title} page_desc={props.page_desc} /> {props.children} <Footer /> </main>
直接,这是不可能的。 但是用另一种方式,您可以使用像 tailwind 这样的框架并将类直接插入到您的 css 中。
这是使用顺风的示例:
.dark { background-color: black; color: white; } body { @apply dark }
这是我的CSS 唯一解决方案:
(我首先查看了https://stackoverflow.com/a/66358460/729221 ,但感觉太复杂了,无法更改一些样式。)
(这使用了 Tailwind CSS,但不需要。)
索引.css:
body:has(#__next .set-bg-indigo-50-on-body) {
@apply bg-indigo-50;
}
布局.tsx:
//…
return (
<div className="set-bg-indigo-50-on-body">{children}</div>
)
这将起作用,只要该div
是<div id="__next">
的直接父级。 否则你需要更新 css :has
-rule。
使用React Helmet而不是 next/head。
React Helmet 允许您在<body>
元素上指定属性,例如类、样式等。
要设置您的pages/_document.js
以使用 React Helment,这是您需要做的。 首先在布局组件之一的某处添加带有<body className="hello">
<Helmet>
标记:
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
读取它们但是它对我来说感觉很hacky 并且不稳定 :)
render() {
const pageProps = _.get(this.props, '__NEXT_DATA__.props.pageProps');
return (
<Html>
<Head/>
<body className={ pageProps.bodyClass }>
<Main />
<NextScript />
</body>
</Html>
);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.