五、实现 React 上下文
在前面的章节中,我们学习了最基本的挂钩,如状态挂钩、减速机挂钩和效果挂钩。我们使用这些挂钩开发了一个小型博客应用。我们在开发博客应用的过程中注意到,我们需要将user
状态从App
组件传递到UserBar
组件,从UserBar
组件传递到Login
、Register
和Logout
组件。为了避免像这样传递状态,我们现在将学习 React 上下文和上下文挂钩。
我们将从学习什么是 React 上下文,以及什么是提供者和消费者开始。然后,我们将使用上下文挂钩作为上下文使用者,并讨论何时应该使用上下文。最后,我们将通过上下文实现主题和全局状态。
本章将介绍以下主题:
- 引入 React 上下文作为传递道具的替代方案
- 通过上下文实现主题
- 对全局状态使用上下文
技术要求
应该已经安装了 Node.js 的最新版本(v11.12.0 或更高版本)。Node.js 的npm
包管理器也需要安装。
本章的代码可以在 GitHub 存储库中找到:hhttps://github.com/PacktPublishing/Learn-React-Hooks/tree/master/Chapter05
请查看以下视频以查看代码的运行情况:
Please note that it is highly recommended that you write the code on your own. Do not simply run the code examples that have been provided. It is important that you write the code yourself in order for you to be able to learn and understand properly. However, if you run into any issues, you can always refer to the code example.
现在,让我们从这一章开始。
引入 React 上下文
在前面的章节中,我们将user
状态和dispatch
函数从App
组件传递到UserBar
组件;然后从UserBar
组件到Logout
、Login
和Register
组件。React context 通过允许我们在组件之间共享值,而不必通过道具显式地传递道具,为这种在多个组件级别上传递道具的麻烦方式提供了解决方案。正如我们将要看到的,React 上下文非常适合在整个应用中共享值。
首先,我们将仔细研究传递道具的问题。然后,我们将引入 React 上下文作为问题的解决方案。
传递道具
在深入学习 React context 之前,让我们回顾一下我们在前面几章中实现的内容,以便了解 context 解决的问题:
- 在
src/App.js
中,我们定义了user
状态和dispatch
功能:
const [ state, dispatch ] = useReducer(appReducer, { user: '', posts: defaultPosts })
const { user, posts } = state
- 然后,我们将
user
状态和dispatch
函数传递给UserBar
组件(和CreatePost
组件):
return (
<div style={{ padding: 8 }}>
<UserBar user={user} dispatch={dispatch} />
<br />
{user && <CreatePost user={user} posts={posts} dispatch={dispatch} />}
<br />
<hr />
<PostList posts={posts} />
</div>
)
- 在
src/user/UserBar.js
组件中,我们将user
状态作为道具,然后将其传递给Logout
组件。我们还将dispatch
功能作为道具,并将其传递给Logout
、Login
和Register
组件:
export default function UserBar ({ user, dispatch }) {
if (user) {
return <Logout user={user} dispatch={dispatch} />
} else {
return (
<React.Fragment>
<Login dispatch={dispatch} />
<Register dispatch={dispatch} />
</React.Fragment>
)
}
}
- 最后,我们在
Logout
、Login
和Register
组件中使用了dispatch
和user
道具。
React 上下文允许我们跳过步骤 2 和 3,直接从步骤 1 跳到步骤 4。正如你们所想象的,对于更大的应用,上下文变得更加有用,因为我们可能需要在多个层次上传递道具。
引入 React 上下文
React 上下文用于跨 React 组件树共享值。通常,我们希望共享全局值,例如user
状态和dispatch
函数、我们应用的主题或所选语言。
React 上下文由两部分组成:
- 提供程序,提供(设置)值
- 消费者,消费(使用)价值
我们首先将使用一个简单的示例来了解上下文是如何工作的,在下一节中,我们将在我们的博客应用中实现它们。我们使用create-react-app
工具创建了一个新项目。在我们的简单示例中,我们将定义一个主题上下文,其中包含应用的主颜色。
定义上下文
首先,我们必须定义上下文。自从引入挂钩以来,这种工作方式没有改变。
我们只需使用React.createContext(defaultValue)
函数创建一个新的上下文对象。我们将默认值设置为{ primaryColor: 'deepskyblue' }
,因此当没有定义提供者时,我们的默认原色将为'deepskyblue'
。
在src/App.js
中,在App
功能之前添加以下定义:
export const ThemeContext = React.createContext({ primaryColor: 'deepskyblue' })
Note how we are exporting ThemeContext
here, because we are going to need to import it for the consumer.
这就是用 React 定义上下文所需要做的一切。现在我们只需要定义消费者。
定义消费者
现在,我们必须在Header
组件中定义消费者。目前,我们将以传统方式进行此操作,并在接下来的步骤中使用挂钩来定义使用者:
- 创建一个新的
src/Header.js
文件 - 首先,我们需要从
App.js
文件中导入ThemeContext
:
import React from 'react'
import { ThemeContext } from './App'
- 现在,我们可以定义我们的组件,其中我们使用
ThemeContext.Consumer
组件和render
函数作为children
属性,以利用上下文值:
const Header = ({ text }) => (
<ThemeContext.Consumer>
{theme => (
- 在
render
函数中,我们现在可以利用上下文值来设置Header
组件的color
样式:
<h1 style={{ color: theme.primaryColor }}>{text}</h1>
)}
</ThemeContext.Consumer>
)
export default Header
- 现在我们还需要导入
src/App.js
中的Header
组件,增加以下import
语句:
import Header from './Header'
- 然后,我们将当前的
App
功能替换为以下代码:
const App = () => (
<Header text="Hello World" />
)
export default App
使用这样的上下文是可行的,但是,正如我们在第一章中所学到的,以这种方式使用带有render
功能道具的组件会弄乱我们的 UI 树,使我们的应用更难调试和维护。
使用挂钩
使用上下文的更好方法是使用useContext
挂钩!通过这种方式,我们可以像使用任何其他值一样使用上下文值,就像使用useState
挂钩一样:
- 编辑
src/Header.js
。首先,我们从 React 导入useContext
挂钩,从src/App.js
导入ThemeContext
对象:
import React, { useContext } from 'react'
import { ThemeContext } from './App'
- 然后,我们创建
Header
组件,现在我们在其中定义useContext
挂钩:
const Header = ({ text }) => {
const theme = useContext(ThemeContext)
- 我们的其他组件将与以前相同,只是现在我们可以简单地返回我们的
Header
组件,而无需为消费者使用额外的组件:
return <h1 style={{ color: theme.primaryColor }}>{text}</h1>
}
export default Header
正如我们所看到的,使用挂钩可以使上下文使用者代码更加简洁。此外,它将更易于阅读、维护和调试。
我们可以看到标题现在有颜色deepskyblue
:
A simple app with a Context Hook!
正如我们所看到的,我们的主题上下文成功地为标题提供了主题。
定义提供者
当没有定义提供程序时,上下文使用传递给React.createContext
的默认值。这对于调试未嵌入到应用中的组件非常有用。例如,我们可以将单个组件作为独立组件进行调试。在应用中,我们通常希望使用提供者为上下文提供值,我们现在要定义上下文。
编辑src/App.js
,在我们的App
函数中,我们简单地用<ThemeContext.Provider>
组件包装Header
组件,其中我们将coral
传递为primaryColor
:
const App = () => (
<ThemeContext.Provider value={{ primaryColor: 'coral' }}>
<Header text="Hello World" />
</ThemeContext.Provider>
)
export default App
我们现在可以看到我们的标题颜色从deepskyblue
变为coral
:
Our provider changed the color of the header
如果我们想更改上下文的值,我们可以简单地调整传递给Provider
组件的value
属性。
Please note that the default value of a context is not used when we define a provider without passing the value
prop to it! If we define a provider without a value
prop, then the value of the context will be undefined
.
现在我们已经为上下文定义了一个提供程序,让我们继续定义多个嵌套的提供程序。
嵌套提供程序
使用 React context,还可以为同一上下文定义多个提供者。使用这种技术,我们可以覆盖应用某些部分中的上下文值。让我们考虑前面的例子,并向它添加第二个标题:
- 编辑
src/App.js
,并添加第二个Header
组件:
const App = () => (
<ThemeContext.Provider value={{ primaryColor: 'coral' }}>
<Header text="Hello World" />
<Header text="This is a test" />
</ThemeContext.Provider>
)
export default App
- 现在,用不同的
primaryColor
定义第二个Provider
组件:
const App = () => (
<ThemeContext.Provider value={{ primaryColor: 'coral' }}>
<Header text="Hello World" />
<ThemeContext.Provider value={{ primaryColor: 'deepskyblue' }}> <Header text="This is a test" />
</ThemeContext.Provider>
</ThemeContext.Provider>
)
export default App
如果在浏览器中打开应用,则第二个标题的颜色与第一个标题的颜色不同:
Overriding context values with nested providers
正如我们所看到的,我们可以通过定义提供者来覆盖 React 上下文值。提供程序也可以嵌套,因此覆盖组件树中较高的其他提供程序的值。
示例代码
小主题上下文示例的示例代码可以在Chapter05/chapter5_1
文件夹中找到。
只需运行npm install
即可安装所有依赖项,npm start
即可启动应用;然后在浏览器中访问http://localhost:3000
(如果它没有自动打开)。
上下文的替代
但是,我们应该小心,不要太频繁地使用 React 上下文,因为这会使重用组件更加困难。我们应该只在需要访问位于不同嵌套级别的许多组件中的数据时使用上下文。此外,我们需要确保只对不经常变化的数据使用上下文。频繁更改上下文的值可能会导致整个组件树重新渲染,从而导致性能问题。这就是为什么,对于频繁变化的值,我们应该使用状态管理解决方案,例如 Redux 或 MobX。
如果我们只想避免传递道具,我们可以传递渲染组件而不是数据。例如,假设我们有一个Page
组件,它呈现Header
组件,它呈现Profile
组件,然后它呈现Avatar
组件。我们将一个headerSize
道具传递给Page
组件,这是Header
组件中需要的,也是Avatar
组件中需要的。我们可以执行以下操作,而不是通过多个关卡传递道具:
function Page ({ headerSize }) {
const profile = (
<Profile>
<Avatar size={headerSize} />
</Profile>
)
return <Header size={headerSize} profile={profile} />
}
现在,只有Page
组件需要知道headerSize
道具,无需在树中进一步传递。在这种情况下,上下文是不必要的。
这种模式被称为控制反转,它可以使您的代码比传递道具或使用上下文更简洁。但是,我们也不应该总是使用这种模式,因为它使更高级别的组件更加复杂。
实施主题
在一个小示例中学习了如何实现主题之后,我们现在将使用 React 上下文和挂钩在我们的博客应用中实现主题。
定义上下文
首先,我们必须定义上下文。在我们的博客应用中,我们将为上下文创建一个单独的文件,而不是在src/App.js
文件中定义它。有一个单独的上下文文件可以使以后更容易维护它们。此外,我们总是知道从何处导入上下文,因为从文件名可以清楚地看到。
让我们开始定义一个主题上下文:
- 创建一个新的
src/contexts.js
文件。 - 然后,我们导入
React
:
import React from 'react'
- 接下来,我们定义
ThemeContext
。与前面的小示例一样,我们将默认值primaryColor
设置为deepskyblue
。此外,我们将secondaryColor
设置为coral
:
export const ThemeContext = React.createContext({
primaryColor: 'deepskyblue',
secondaryColor: 'coral'
})
现在我们已经定义了上下文,我们可以继续定义上下文挂钩。
定义上下文挂钩
在定义了上下文之后,我们将使用上下文挂钩来定义我们的消费者。我们首先为头部创建一个新组件,然后为现有的Post
组件定义一个上下文挂钩。
创建标题组件
首先,我们创建一个新的Header
组件,它将在我们应用的primaryColor
中显示React Hooks Blog
。
现在让我们创建Header
组件:
- 创建一个新的
src/Header.js
文件。 - 在这个文件中,我们导入了
React
和useContext
挂钩:
import React, { useContext } from 'react'
- 接下来,我们从之前创建的
src/contexts.js
文件导入ThemeContext
:
import { ThemeContext } from `'./contexts'
- 然后,我们定义
Header
组件和上下文挂钩。我们没有将上下文值存储在theme
变量中,而是使用解构直接提取primaryColor
值:
const Header = ({ text }) => {
const { primaryColor } = useContext(ThemeContext)
- 最后,我们返回
h1
元素,就像我们之前在小示例中所做的一样,export
返回Header
组件:
return <h1 style={{ color: primaryColor }}>{text}</h1>
}
export default Header
现在我们的Header
组件已经定义,我们可以使用它了。
使用 Header 组件
创建Header
组件后,我们将在App
组件中使用它,如下所示:
- 编辑
src/App.js
,导入Header
组件:
import Header from './Header'
- 然后,在
UserBar
组件之前渲染Header
组件:
return (
<div style={{ padding: 8 }}>
<Header text="React Hooks Blog" />
<UserBar user={user} dispatch={dispatch} />
You might want to refactor the React Hooks Blog
value into a prop that is passed to the App
component (app config), because we are already using it three times in this component.
现在,我们的Header
组件将在应用中呈现,我们可以继续在 Post 组件中实现上下文挂钩。
实现 Post 组件的上下文挂钩
接下来,我们想用第二种颜色显示Post
标题。为此,我们需要为Post
组件定义一个上下文挂钩,如下所示:
- 编辑
src/post/Post.js
,调整import
语句导入useContext
挂钩:
import React, { useContext } from 'react'
- 接下来,我们导入
ThemeContext
:
import { ThemeContext } from '../contexts'
- 然后,我们在
Post
组件中定义一个上下文挂钩,通过解构从主题中获取secondaryColor
值:
export default function Post ({ title, content, author }) {
const { secondaryColor } = useContext(ThemeContext)
- 最后,我们使用
secondaryColor
值来设置h3
元素的样式:
return (
<div>
<h3 style={{ color: secondaryColor }}>{title}</h3>
如果我们现在查看我们的应用,我们可以看到这两种颜色都在ThemeContext
中正确使用:
Our ThemeContext in action
正如我们所看到的,我们的应用现在使用主标题的主颜色,以及文章标题的次颜色。
定义提供者
现在,当没有定义提供者时,我们的上下文挂钩使用上下文指定的默认值。为了能够更改该值,我们需要定义一个提供者。
让我们开始定义提供者:
- 编辑
src/App.js
,导入ThemeContext
:
import { ThemeContext } from './contexts'
- 用
ThemeContext.Provider
组件包装整个应用,提供我们之前设置为默认值的相同主题:
return (
<ThemeContext.Provider value={{ primaryColor: 'deepskyblue', secondaryColor: 'coral' }}> <div style={{ padding: 8 }}>
<Header text="React Hooks Blog" />
...
<PostList posts={posts} />
</div>
</ThemeContext.Provider>
)
我们的应用看起来应该和以前完全一样,但现在我们使用的是提供商提供的价值!
动态更改主题
现在我们已经定义了一个提供者,我们可以使用它来动态地更改主题。我们将使用定义当前主题的状态挂钩,而不是将静态值传递给提供者。然后,我们将实现一个改变主题的组件。
与上下文提供程序一起使用状态挂钩
首先,我们将定义一个新的状态挂钩,我们将使用它来设置上下文提供程序的值。
让我们定义一个状态挂钩,并在上下文提供程序中使用它:
- 编辑
src/App.js
,导入useState
挂钩:
import React, { useReducer, useEffect, useState } from 'react'
- 在
App
组件的开头定义一个新的状态挂钩;在这里,我们将默认值设置为默认主题:
export default function App () {
const [ theme, setTheme ] = useState({
primaryColor: 'deepskyblue',
secondaryColor: 'coral'
})
- 然后,我们将
theme
值传递给ThemeContext.Provider
组件:
return (
<ThemeContext.Provider value={theme}>
我们的应用看起来仍然和以前一样,但是我们现在已经准备好动态地改变我们的主题了!
实现 ChangeTheme 组件
主题特性的最后一部分是一个组件,可以通过使用前面定义的状态挂钩来动态更改主题。状态挂钩将重新呈现App
组件,这将改变传递给ThemeContext.Provider
的值,而ThemeContext.Provider
将重新呈现使用ThemeContext
上下文挂钩的所有组件。
让我们开始实现ChangeTheme
组件:
- 创建一个新的
src/ChangeTheme.js
文件。 - 一如既往,我们必须先导入
React
,然后才能定义组件:
import React from 'react'
- 为了以后能够轻松地添加新主题,我们将创建一个常量
THEMES
数组,而不是手动复制和粘贴不同主题的代码。这将使我们的代码更加简洁,更易于阅读:
const THEMES = [
{ primaryColor: 'deepskyblue', secondaryColor: 'coral' },
{ primaryColor: 'orchid', secondaryColor: 'mediumseagreen' }
]
It is a good idea to give constant values that are hardcoded a special name, such as writing the whole variable name in caps. Later on, it might make sense to put all these configurable hardcoded values in a separate src/config.js
file.
- 接下来,我们定义一个组件来呈现单个
theme
:
function ThemeItem ({ theme, active, onClick }) {
- 在这里,我们渲染一个链接,并通过显示主颜色和次颜色显示主题的小预览:
return (
<span onClick={onClick} style={{ cursor: 'pointer', paddingLeft: 8, fontWeight: active ? 'bold' : 'normal' }}>
<span style={{ color: theme.primaryColor }}>Primary</span> / <span style={{ color: theme.secondaryColor }}>Secondary</span>
</span>
)
}
Here, we set the cursor to pointer
, in order to make the element appear clickable. We could also use an <a>
element; however, this is not recommended if we do not have a valid link target, such as a separate page.
- 然后,我们定义
ChangeTheme
组件,它接受theme
和setTheme
道具:
export default function ChangeTheme ({ theme, setTheme }) {
- 接下来,我们定义一个函数来检查主题对象是否是当前活动的主题:
function isActive (t) {
return t.primaryColor === theme.primaryColor && t.secondaryColor === theme.secondaryColor
}
- 现在,我们使用
.map
函数呈现所有可用主题,点击时调用setTheme
函数:
return (
<div>
Change theme:
{THEMES.map((t, i) =>
<ThemeItem key={'theme-' + i} theme={t} active={isActive(t)} onClick={() => setTheme(t)} />
)}
</div>
)
}
- 最后,我们可以导入并呈现
ChangeTheme
组件,在src/App.js
中的Header
组件之后:
import ChangeTheme from './ChangeTheme'
// ...
return (
<ThemeContext.Provider value={theme}>
<div style={{ padding: 8 }}>
<Header text="React Hooks Blog" />
<ChangeTheme theme={theme} setTheme={setTheme} />
<br />
正如我们所看到的,我们现在有了一种改变应用主题的方法:
Our app after changing the theme, using Context Hooks in combination with a State Hook
现在,我们有了一个通过挂钩消费的上下文,它也可以通过挂钩更改!
示例代码
在Chapter05/chapter5_2
文件夹中可以找到我们博客应用中主题功能的示例代码。
只需运行npm install
即可安装所有依赖项,npm start
即可启动应用;然后在浏览器中访问http://localhost:3000
(如果它没有自动打开)。
对全局状态使用上下文
在学习了如何使用 React context 在我们的博客应用中实现主题后,我们现在将使用一个上下文来避免手动传递我们的全局应用状态的state
和dispatch
道具。
定义状态上下文
我们首先在src/contexts.js
文件中定义上下文。
在src/contexts.js
中定义StateContext
,用于存储state
值和dispatch
函数:
export const StateContext = React.createContext({
state: {},
dispatch: () => {}
})
我们将state
值初始化为空对象,dispatch
函数初始化为空函数,在未定义提供程序时使用。
定义上下文提供程序
现在,我们将在src/App.js
文件中定义上下文提供程序,它将从现有的 Reducer 挂钩中获取值。
现在让我们为全局状态定义上下文提供程序:
- 在
src/App.js
中,通过调整已有import
语句导入StateContext
:
import { ThemeContext, StateContext } from './contexts'
- 然后,通过从
App
函数返回,我们定义了一个新的上下文提供程序:
return (
<StateContext.Provider value={{ state, dispatch }}>
<ThemeContext.Provider value={theme}>
...
</ThemeContext.Provider>
</StateContext.Provider>
)
现在,我们的上下文提供者为应用的其余部分提供了state
对象和dispatch
函数,我们可以继续使用上下文值。
使用 StateContext
现在我们已经定义了上下文和提供者,我们可以在各种组件中使用state
对象和dispatch
函数。
我们首先移除在src/App.js
中手动传递给组件的道具。删除以下以粗体标记的代码段:
<div style={{ padding: 8 }}>
<Header text="React Hooks Blog" />
<ChangeTheme theme={theme} setTheme={setTheme} />
<br />
<UserBar user={user} dispatch={dispatch} />
<br />
{user && <CreatePost user={user} posts={posts} dispatch={dispatch} />}
<br />
<hr />
<PostList posts={posts} />
</div>
当我们使用上下文时,就不再需要手动传递道具了。我们现在可以继续重构组件了。
重构用户组件
首先,我们重构用户组件,然后再转到 post 组件。
现在让我们重构与用户相关的组件:
- 编辑
src/user/UserBar.js
,同时移除那里的道具(应该移除粗体标记的代码),因为我们不再需要手动传递它们:
export default function UserBar ({ user, dispatch }) {
if (user) {
return <Logout user={user} dispatch={dispatch} />
} else {
return (
<React.Fragment>
<Login dispatch={dispatch} />
<Register dispatch={dispatch} />
</React.Fragment>
)
}
}
- 然后,我们导入
useContext
挂钩和src/user/UserBar.js
中的StateContext
,以便能够判断用户是否登录:
import React, { useContext } from 'react'
import { StateContext } from '../contexts'
- 现在,我们可以使用上下文挂钩从我们的
state
对象获取user
状态:
export default function UserBar () {
const { state } = useContext(StateContext)
const { user } = state
- 同样,我们在
src/user/Login.js
中导入useContext
和StateContext
:
import React, { useState, useContext } from 'react'
import { StateContext } from '../contexts'
- 然后,我们移除
dispatch
道具,并使用上下文挂钩:
export default function Login () {
const { dispatch } = useContext(StateContext)
- 我们在
src/user/Register.js
组件中重复相同的过程:
import React, { useState, useContext } from 'react'
import { StateContext } from '../contexts' export default function Register () { const { dispatch } = useContext(StateContext)
- 在
src/user/Logout.js
组件中,我们也这样做,但也从state
对象获取user
状态:
import React, { useContext } from 'react'
import { StateContext } from '../contexts' export default function Logout () { const { state, dispatch } = useContext(StateContext)
const { user } = state
我们的用户相关组件现在使用上下文而不是道具。让我们继续重构与 post 相关的组件。
重构 post 组件
现在,剩下要做的就是重构 post 组件;然后,我们的整个应用将使用全局状态的 React 上下文:
- 我们从
src/post/PostList.js
组件开始,在这里我们导入useContext
和StateContext
,移除道具,并使用上下文挂钩:
import React, { useContext } from 'react'
import { StateContext } from '../contexts'
import Post from './Post'
export default function PostList () {
const { state } = useContext(StateContext)
const { posts } = state
- 我们对
CreatePost
组件也做了同样的操作,这是我们需要重构的最后一个组件:
import React, { useState, useContext } from 'react'
import { StateContext } from '../contexts'
export default function CreatePost () {
const { state, dispatch } = useContext(StateContext)
const { user } = state
我们的应用的工作方式与以前相同,但现在我们使用全局状态的上下文,这使我们的代码更干净,并且避免了传递道具!
示例代码
我们博客应用中全局状态上下文的示例代码可以在Chapter05/chapter5_3
文件夹中找到。
只需运行npm install
即可安装所有依赖项,npm start
即可启动应用;然后在浏览器中访问http://localhost:3000
(如果它没有自动打开)。
总结
在本章中,我们首先了解了 React 上下文,它可以替代在多个级别的 React 组件上传递道具。然后,我们了解了上下文提供者和消费者,以及通过挂钩定义消费者的新方法。接下来,我们学习了什么时候使用上下文没有意义,什么时候应该使用控制反转。然后,通过在博客应用中实现主题,我们使用了我们在实践中学到的知识。最后,我们在博客应用中使用了全局状态的 React 上下文。
在下一章中,我们将学习如何使用 React 和 hook 从服务器请求数据。然后,我们将学习React.memo
以防止不必要的组件重新渲染,并在需要组件时对延迟加载组件作出 React。
问题
为了回顾我们在本章学到的知识,请尝试回答以下问题:
- 上下文可以避免哪些问题?
- 上下文由哪两部分组成?
- 为了使用上下文,是否需要定义这两个部分?
- 使用挂钩而不是传统的上下文消费者的优势是什么?
- 什么是上下文的替代方案,我们应该在什么时候使用它?
- 我们如何实现动态变化的上下文?
- 什么时候为状态使用上下文有意义?
进一步阅读
如果您对我们在本章中探讨的概念的更多信息感兴趣,请阅读以下阅读材料:
- 关于 React 上下文的正式文件:https://reactjs.org/docs/context.html
- 关于组合与继承的更多信息:https://reactjs.org/docs/composition-vs-inheritance.html
- HTML 颜色代码列表(如果要定义新主题):https://www.rapidtables.com/web/color/html-color-codes.html.
版权属于:月萌API www.moonapi.com,转载请注明出处