在我的Gatsby网站上,我希望有一个Youtube组件,该组件仅在由于GDPR导致用户接受Cookie的情况下才会显示。如果他们拒绝了cookie,我希望它能提供指导以重置其首选项,并且如果cookie接受状态未知(例如,他们使用的扩展程序阻止了cookie通知),我希望它说它不知道是否cookie是否允许,如果您没有看到横幅,请检查您的adblocker。
我已经通过react-redux设置了全局状态,该状态检查'acceptCookies'cookie和所有显示横幅的逻辑等(都使用react-cookie-consent完成),并将state.acceptCookies设置为true。这在各种页面上都有效,例如,我只希望在接受cookie的情况下显示联系表单。
这是我的组件(我将其包含在拥有YouTube视频的任何页面中,并将YouTube代码作为道具传递给它):
import React from "react"
import { useSelector } from "react-redux"
import { Alert } from "react-bootstrap"
import { Link } from "gatsby"
const YouTubeVideo = (props) => {
const acceptCookies = useSelector(state => state.acceptCookies)
return (
<>
{acceptCookies === '' &&
<Alert variant="warning">
<p>
We cannot show this YouTube video as cookies have not been accepted,
but are required for YouTube to work. To comply with GDPR law, the
video is not displayed until you have accepted cookies.
</p>
<p>
If you do not see the cookie banner, please try resetting your
preferences from the <Link to="/privacy-policy">Privacy Policy</Link> or
disabling any content blockers.
</p>
</Alert>
}
{acceptCookies === 'false' &&
<Alert variant="warning">
<p>
We cannot show this YouTube video as cookies have been declined, but
are required for YouTube to work. To comply with GDPR law, the video
is not displayed.
</p>
<p>
You may reset your preferences from
the <Link to="/privacy-policy">Privacy Policy</Link>
</p>
</Alert>
}
{acceptCookies === 'true' &&
<div className="embed-container">
<iframe src={`https://www.youtube.com/embed/${props.code}?rel=0`}
title={`YouTube Video ${props.code}`}
frameBorder="0"
allowFullScreen
>
</iframe>
</div>
}
</>
)
}
export default YouTubeVideo
我尝试了其他语法,例如使用if语句和多个渲染块,没有区别
我尝试将其重写为一个类并使用connect(),没什么不同
在我的一生中,我无法正常工作。它在开发模式下工作良好-初始状态显示“未接受Cookie”警报,下降显示“ Cookie已被拒绝”警报,接受显示视频,一切都很好。我刷新了,一切依旧。
但是,一旦我构建并部署了该网站,它的行为就会变得很奇怪。乍一看似乎不错-初始状态显示“未接受Cookie”警报,下降则显示“ Cookie已被拒绝”警报,接受则显示视频。然后刷新页面,然后在警告中显示视频的小版本! (无警告文本)。真的很奇怪就像遵循``未接受Cookie的逻辑''并开始发出警报一样,然后在编写段落之前,遇到了某种竞争状况并呈现了代码``视频已接受''cookie的一部分。我完全不了解它,我希望它能够呈现三件事之一,而不是设法将结果融合在一起。
我在这里想念什么?我完全困惑。我想我一定在预渲染中遇到了一些麻烦,但是无法解决这个问题。
编辑:头脑清楚,我认为至少应尝试将acceptCookies设置为静态“ true”,以查看问题出在我的渲染(在这种情况下仍无法正确显示)还是全局状态实现(在这种情况下会),并证明是后者。问题确实是我不知道如何在Gatsby中正确地执行cookie许可或全局状态(将其“缓存”为全局状态,以便在接受cookie时,输出将更新而不必刷新页面),并且只能讨论了有关香草React和Gatsby的教程的混搭。我可能最好改写它-在这一点上,我不记得我使用了哪个教程!
不过,这就是我所拥有的
gatsby-ssr.js和gatsby-browser.js都包含:
import wrapWithProvider from "./wrap-with-provider"
export const wrapRootElement = wrapWithProvider
wrap-with-provider.js:
import React from "react"
import { Provider } from "react-redux"
import createStore from "./src/state/createStore"
// eslint-disable-next-line react/display-name,react/prop-types
export default ({ element }) => {
// Instantiating store in `wrapRootElement` handler ensures:
// - there is fresh store for each SSR page
// - it will be called only once in browser, when React mounts
const store = createStore()
return <Provider store={store}>{element}</Provider>
}
src / state / createStore.js:
import { createStore as reduxCreateStore } from "redux"
import Cookies from 'js-cookie'
const reducer = (state, action) => {
if (action.type === `ACCEPT_COOKIES`) {
return Object.assign({}, state, {
acceptCookies: 'true',
})
}
if (action.type === `DECLINE_COOKIES`) {
return Object.assign({}, state, {
acceptCookies: 'false',
})
}
if (action.type === `RESET_COOKIES`) {
return Object.assign({}, state, {
acceptCookies: '',
})
}
return state
}
const initialState = { acceptCookies: Cookies.get('acceptCookies') || '' }
const createStore = () => reduxCreateStore(reducer, initialState)
export default createStore
cookie同意通知是layout.js的一部分,因此在需要时它会出现在每个页面上:
import CookieConsent from "react-cookie-consent"
const OurCookieConsent = ({ acceptCookies, accept, decline }) => (
<CookieConsent
location="bottom"
enableDeclineButton={true}
cookieName="acceptCookies"
onDecline={decline}
onAccept={accept}
>
<p>This website uses cookies for:</p>
<ul>
<li>REQUIRED: Saving your cookie preferences</li>
<li>OPTIONAL: The contact form on the "Contact Us" page - for spam
control (reCAPTCHA).</li>
<li>OPTIONAL: Embedded YouTube videos e.g. videos in our news pages
</li>
</ul>
<p>Declining consent will disable the features marked as OPTIONAL.</p>
<p>For more in-depth detail or to withdraw or re-establish your
consent at any time, please see the <Link to="/privacy-policy">Privacy Policy</Link>.</p>
</CookieConsent>
)
OurCookieConsent.propTypes = {
acceptCookies: PropTypes.string.isRequired,
accept: PropTypes.func.isRequired,
decline: PropTypes.func.isRequired,
}
const mapStateToProps = ({ acceptCookies }) => {
return { acceptCookies }
}
const mapDispatchToProps = dispatch => {
return {
accept: () => dispatch({ type: `ACCEPT_COOKIES` }),
decline: () => dispatch({ type: `DECLINE_COOKIES` })
}
}
const ConnectedCookieConsent = connect(mapStateToProps, mapDispatchToProps)(OurCookieConsent)
然后将ConnectedCookieConsent包含在布局中。
答案 0 :(得分:1)
我似乎已经开始工作了。这取决于Gatsby在生产站点上的工作效率-它通过Node.js预渲染了许多HTML,然后在托管站点加载和合并时,它只会更新其认为需要的内容。它没有意识到整个预渲染的“外部” div(由于SSR期间没有cookie,因此已渲染为“未知”状态),只需要在确定有某些更改的地方进行更新即可:内部内容。
因此,该组件需要“知道”以完全刷新自身,这仅在状态或道具发生变化时才会执行。最初的水合作用不会算作更改(因为gatsby的意见是应用程序不必关心它是在SSR还是在浏览器中),因此它被保留为“ limbo”。您必须触发状态更改,这显然可以使用useEffect()挂钩来完成,但就我个人而言,我发现最简单的方法是将其转换为类并使用componentDidMount()。然后需要在render函数中使用该状态,以便它知道“如果状态发生更改,那么我需要重新渲染自己”。如果您希望视频在接受cookie通知后立即显示(不刷新),则还需要componentDidUpdate()(并且如果状态不同则只需要更新状态,否则将导致无限循环,因此if语句在下面的代码)。
这非常复杂,我仍然不是100%理解,但这是我根据对这个问题的答案对情况的粗略了解:
Can you force a React component to rerender without calling setState?
和这个:
https://github.com/gatsbyjs/gatsby/issues/12413
因此,我的工作组成部分是:
import React from "react"
import { connect } from "react-redux"
import { Alert } from "react-bootstrap"
import { Link } from "gatsby"
function mapStateToProps(state) {
return { acceptCookies: state.acceptCookies }
}
class YouTubeVideo extends React.Component {
state = {
acceptCookies: 'undecided',
}
componentDidMount() {
this.setState({
acceptCookies: this.props.acceptCookies,
})
}
componentDidUpdate() {
if (this.state.acceptCookies !== this.props.acceptCookies) {
this.setState({
acceptCookies: this.props.acceptCookies,
})
}
}
render () {
const { acceptCookies } = this.state
if (acceptCookies === 'undecided') {
return (
<Alert variant="warning">
<p>
We cannot show this YouTube video as cookies have not been accepted,
but are required for YouTube to work. To comply with GDPR law, the
video is not displayed until you have accepted cookies.
</p>
<p>
If you do not see the cookie banner, please try resetting your
preferences from the <Link to="/privacy-policy">Privacy Policy</Link> or
disabling any content blockers.
</p>
</Alert>
)
}
if (acceptCookies === 'false') {
return(
<Alert variant="warning">
<p>
We cannot show this YouTube video as cookies have been declined, but
are required for YouTube to work. To comply with GDPR law, the video
is not displayed.
</p>
<p>
You may reset your preferences from
the <Link to="/privacy-policy">Privacy Policy</Link>
</p>
</Alert>
)
}
if (acceptCookies === 'true') {
return (
<div className="embed-container">
<iframe src={`https://www.youtube.com/embed/${this.props.code}?rel=0`}
title={`YouTube Video ${this.props.code}`}
frameBorder="0"
allowFullScreen
>
</iframe>
</div>
)
}
return (
<p><a href={`https://www.youtube.com/watch?v=${this.props.code}`}>YouTube Video</a></p>
)
}
}
export default connect(mapStateToProps)(YouTubeVideo)
所以回顾一下...
该组件具有其自己的本地状态,acceptCookies最初设置为“不确定”,这是SSR生成的内容(以及Google或禁用JS的人最终会看到的内容),也是在加载几毫秒后看到的内容如果您有Cookie,则直到JS工作人员加载并且页面水化为止。这确实会引起短暂的不需要的内容或FOUC,但我认为对此无能为力,而且仍然符合GDPR。它需要JavaScript来加载以检查Cookie,并知道它具有渲染YouTube嵌入的权限(以及Google向用户转储的大量其他未经请求的Cookie,因此需要进行整个练习)。一旦水化,就会调用componentDidMount()并将本地状态更新为当前的acceptCookies道具(稍后我们将介绍)。如果不同(即您已经接受Cookie),则该组件会看到状态更改并完全重新呈现自己。
在底部,您看到该组件是通过connect(mapStateToProps)导出的,该组件从redux获取全局状态(上下文),并将其通过mapStateToProps函数传递,该函数吐出acceptCookies属性,然后将该属性绑定回该组件。幸运的是,这发生在componentDidMount()之前,因此这就是它知道正确更新状态的方式。
当acceptCookies cookie更新时(通过我使用的单独的cookie通知机制),我们已经知道createStore.js中的'ACCEPT_COOKIES'reducer被触发,导致redux状态更新为acceptCookies ='true'。然后,connect函数更新组件的props以进行匹配(类似于之前的工作方式),但是由于我们现在在render函数中使用state而不是props,这不足以触发重新渲染-我们还需要componentDidUpdate ()获取道具更改并更新状态以进行匹配。
如果我做了一些愚蠢的事情,请纠正我,但这似乎已经解决了问题。我希望冗长的语气能帮助其他情况下的人。
我喜欢仅提供一个链接的后备功能(由于在GDPR下,您不对另一个网站放置的Cookie负责,只要它是外部链接,就好像您将其网站嵌入为iframe或类似的东西被认为是您的“一部分”),最后可能会更新我的错误/不确定的输出来执行此操作(并提供少量解释),而不是发出很大的脸部警报并拒绝用户有机会观看视频。
答案 1 :(得分:0)
我将这样编写YoutubeVideo
组件:
import React from "react"
import { useSelector } from "react-redux"
import { Alert } from "react-bootstrap"
import { Link } from "gatsby"
const YouTubeVideo = (props) => {
const acceptCookies = useSelector((state) => state.acceptCookies)
if (acceptCookies === "")
return (
<Alert variant="warning">
<p>
We cannot show this YouTube video as cookies have not been
accepted, but are required for YouTube to work. To comply
with GDPR law, the video is not displayed until you have
accepted cookies.
</p>
<p>
If you do not see the cookie banner, please try resetting
your preferences from the{" "}
<Link to="/privacy-policy">Privacy Policy</Link> or
disabling any content blockers.
</p>
</Alert>
)
if (acceptCookies === "false")
return (
<div className="embed-container">
<iframe
src={`https://www.youtube.com/embed/${props.code}?rel=0`}
title={`YouTube Video ${props.code}`}
frameBorder="0"
allowFullScreen
></iframe>
</div>
)
if (acceptCookies === "true")
return (
<div className="embed-container">
<iframe
src={`https://www.youtube.com/embed/${props.code}?rel=0`}
title={`YouTube Video ${props.code}`}
frameBorder="0"
allowFullScreen
></iframe>
</div>
)
// Maybe add a default ?
return (
<Alert variant="warning">
<p>
Not sure what's up here, but hey, better safe than sorry :)
</p>
</Alert>
)
}