在 Gatsby 中使用 wrapRootElement 钩子进行状态管理

由于Gatsby为我们处理路线,这使得我们没有地方用Redux商店或提供商包装我们的应用程序。在这篇文章中,我们将学习一个聪明的技巧来解决这个问题。

为简单起见,所有示例都将使用React的上下文API](https://andsky.com/tech/tutorials/react-context-api)进行状态管理,以节省设置样板的时间,但所有内容仍然适用于其他状态管理方法。如果您需要温习一下与提供者的合作,您可以查看This Introo to the useContext hook.

安装

我更喜欢从一个非常基本的主题开始,但最后我们只需要两页的内容,这样我们就可以看到我们的状态正在全球范围内应用。因为我们将使用一些样式,所以我将使用node-sassgatsby-plugin-sass添加对Sass的支持,因为我不是一只动物。

1$ gatsby new stateful-gatsby https://github.com/gatsbyjs/gatsby-starter-defaultCopyInstall
2$ cd stateful-gatsby
3$ yarn add node-sass gatsby-plugin-sass -D

使用Provider包装

第一步是为我们的上下文提供程序设置一个简单的‘isDark’状态和一个逆转其当前状态的方法。我们将把传入的任何东西作为道具,并将其包装在我们的新myConext.Provider中。

 1[label provider.js]
 2import React, { useState } from 'react';
 3
 4export const myContext = React.createContext();
 5
 6const Provider = props => {
 7  const [isDark, setTheme] = useState(false);
 8
 9  return (
10    <myContext.Provider value={{
11      isDark,
12      changeTheme: () => setTheme(!isDark)
13    }}>
14      {props.children}
15    </myContext.Provider>
16  )
17};

就在它下面,我们将导出一个函数,该函数将把传递给它的任何内容包装在我们的新提供程序中。

1export default ({ element }) => (
2  <Provider>
3    {element}
4  </Provider>
5);

现在我们有了一种管理状态的方法,Gatsby为我们提供了一个名为wrapRootElement‘的小钩子,您可以在[docs](https://www.gatsbyjs.org/docs/browser-apis/#wrapRootElement).中查看它这个钩子获取我们站点的大部分内容,并将其作为道具传递给我们提供给它的函数,就像我们刚刚从Provider.js`中导出的函数一样,为我们提供了完美的小空间来包装其中的所有内容。

gatsby-browser.jsgatsby-ssr.js都可以访问这个钩子,建议用我们的提供者将它们都包装起来,这就是为什么我们在Provider.js中定义了包装器函数。

1import Provider from './provider';
2
3export const wrapRootElement = Provider;

Style

以下是我们简单的主题风格:

 1[label src/global.sass]
 2.colorTheme 
 3    height: 100vh
 4    transition: .3s ease-in-out
 5
 6.darkTheme 
 7    @extend .colorTheme
 8    background-color: #1A202C
 9    color: #fff
10    a
11        color: yellow
12
13.lightTheme 
14    @extend .colorTheme
15    background-color: #fff
16    color: #000

应用主题

要访问我们的状态,我们唯一需要做的就是将每个组件包装在一个myConext.Consumer中,并在Conext上访问我们的全局状态,React.Fragment只是让我们添加多个元素。

对于我们的背景颜色,我们可以有条件地将我们的类设置为我们的状态,如果您有多个主题,您可以将提供者的主题设置为带有我们的类名的字符串。

 1[label layout.js]
 2import { myContext } from '../../provider';
 3import '../global.sass';
 4
 5 return (
 6    <myContext.Consumer>
 7      {context => (
 8        <React.Fragment>
 9          <div className={context.isDark ? 'darkTheme' : 'lightTheme'}>
10            {/* ... */}
11          </div>
12        </React.Fragment>
13      )}
14    </myContext.Consumer>
15  )

我们还可以访问setTheme,因为我们将其传递给了changeTheme方法。让我们添加一个按钮来反转isDark

 1[label src/pages/index.js]
 2import { myContext } from '../../provider';
 3
 4const IndexPage = () => (
 5  <Layout>
 6    <myContext.Consumer>
 7      {context => (
 8        <React.Fragment>
 9          <SEO title="Home" />
10          <h1>{context.isDark ? "Dark Theme" : "Light Theme"}</h1>
11
12          <button onClick={() => context.changeTheme()}>{context.isDark ? "Light" : "Dark"}</button>
13
14          <Link to="/page-2/">Go to page 2</Link>
15        </React.Fragment>
16      )}
17    </myContext.Consumer>
18  </Layout>
19);

现在,如果您将基本相同的配置添加到page-2.js,您应该能够更改状态并在它们之间移动,而它们之间的状态保持一致。

 1[label page-2.js]
 2import { myContext } from '../../provider';
 3
 4const SecondPage = () => (
 5  <Layout>
 6    <myContext.Consumer>
 7      {context => (
 8        <React.Fragment>
 9          <SEO title="Page two" />
10          <h1>{context.isDark ? "Dark Theme" : "Light Theme"}</h1>
11          <p>Welcome to page 2</p>
12
13          <button onClick={() => context.changeTheme()}>{context.isDark ? "Light" : "Dark"}</button>
14
15          <Link to="/">Go back to the homepage</Link>
16        </React.Fragment>
17      )}
18    </myContext.Consumer>
19  </Layout>
20);

镜像说明

哈扎!事情真的就这么简单,只需更改3个小文件,然后将所有内容包装在一个消费者中,您就可以开始了。

结论

对我来说,这不是一种非常明显的做事方式,所以我希望这有助于理解如何使用`wrapRootElement‘挂钩对您有利。

为了方便起见,我用这个设置创建了一个回购,您可以查看here.

Published At
Categories with 技术
Tagged with
comments powered by Disqus