在Gatsby中使用wrapRootElementHook进行状态管理
由于 Gatsby 为我们处理我们的路线,这让我们无法使用 Redux 存储或提供程序 来包装我们的应用程序。 在本文中,我们将学习一个巧妙的技巧来解决这个问题。
为简单起见,所有示例都将使用 React 的 Context API 进行状态管理,以节省设置样板的时间,但一切仍然适用于其他状态管理方法。 如果您需要重新了解与提供程序的合作,您可以查看 this intro to the useContext hook。
安装
我更喜欢从一个非常基本的主题开始,但最后我们只需要两页的东西,这样我们就可以看到我们的状态正在全球范围内应用。 由于我们将使用一些样式,因此我将使用 node-sass
和 gatsby-plugin-sass
添加 Sass 支持,因为我不是动物。
$ gatsby new stateful-gatsby https://github.com/gatsbyjs/gatsby-starter-defaultCopyInstall $ cd stateful-gatsby $ yarn add node-sass gatsby-plugin-sass -D
用提供者包装
第一步是使用简单的 isDark
状态和反转其当前状态的方法设置我们的上下文提供程序。 我们将传入的任何内容作为道具并将其包装在我们新的 myContext.Provider
中。
提供者.js
import React, { useState } from 'react'; export const myContext = React.createContext(); const Provider = props => { const [isDark, setTheme] = useState(false); return ( <myContext.Provider value={{ isDark, changeTheme: () => setTheme(!isDark) }}> {props.children} </myContext.Provider> ) };
在它的下方,我们将导出一个函数,该函数将在我们的新提供者中包装传递给它的任何内容。
export default ({ element }) => ( <Provider> {element} </Provider> );
现在我们有了管理状态的方法,Gatsby 为我们提供了一个简洁的小钩子,称为 wrapRootElement
,您可以在 文档 中查看它。 这个钩子占用了我们网站的大部分内容,并将其作为道具传递给我们提供给它的函数,就像我们刚刚从 Provider.js
导出的函数一样,为我们提供了完美的小空间来包装所有内容。
gatsby-browser.js
和 gatsby-ssr.js
都可以访问这个钩子,建议用我们的提供者包装它们,这就是我们在 provider.js
中定义包装函数的原因。
import Provider from './provider'; export const wrapRootElement = Provider;
造型
以下是我们简单的主题样式:
src/global.sass
.colorTheme height: 100vh transition: .3s ease-in-out .darkTheme @extend .colorTheme background-color: #1A202C color: #fff a color: yellow .lightTheme @extend .colorTheme background-color: #fff color: #000
应用主题
要访问我们的状态,我们唯一需要做的就是将每个组件包装在 myContext.Consumer
中并在 context
、React.Fragment 上访问我们的全局状态只是为了让我们添加多个元素。
对于我们的背景颜色,我们可以有条件地将我们的类设置为我们的状态,如果您有多个主题,您可以将提供者的主题设置为带有我们类名的字符串。
布局.js
import { myContext } from '../../provider'; import '../global.sass'; return ( <myContext.Consumer> {context => ( <React.Fragment> <div className={context.isDark ? 'darkTheme' : 'lightTheme'}> {/* ... */} </div> </React.Fragment> )} </myContext.Consumer> )
我们还可以访问 setTheme
因为我们将它传递给了 changeTheme
方法。 让我们添加一个按钮来反转 isDark
。
src/pages/index.js
import { myContext } from '../../provider'; const IndexPage = () => ( <Layout> <myContext.Consumer> {context => ( <React.Fragment> <SEO title="Home" /> <h1>{context.isDark ? "Dark Theme" : "Light Theme"}</h1> <button onClick={() => context.changeTheme()}>{context.isDark ? "Light" : "Dark"}</button> <Link to="/page-2/">Go to page 2</Link> </React.Fragment> )} </myContext.Consumer> </Layout> );
现在,如果您向 page-2.js
添加基本相同的配置,您应该能够更改状态并在它们之间移动,并且它们之间的状态保持一致。
page-2.js
import { myContext } from '../../provider'; const SecondPage = () => ( <Layout> <myContext.Consumer> {context => ( <React.Fragment> <SEO title="Page two" /> <h1>{context.isDark ? "Dark Theme" : "Light Theme"}</h1> <p>Welcome to page 2</p> <button onClick={() => context.changeTheme()}>{context.isDark ? "Light" : "Dark"}</button> <Link to="/">Go back to the homepage</Link> </React.Fragment> )} </myContext.Consumer> </Layout> );
胡扎! 它真的就这么简单,3 个小文件更改并将所有内容包装在一个消费者中,你就可以开始了。
结论
对我来说,这不是一种非常明显的做事方式,所以我希望这有助于理解如何使用 wrapRootElement
钩子来发挥你的优势。
为了方便起见,我创建了一个使用此设置作为启动器的存储库,您可以 在此处查看 。