# react-useContext
**Repository Path**: cai-lunduo/react-use-context
## Basic Information
- **Project Name**: react-useContext
- **Description**: 包含useContext的实际场景用法和context的使用笔记
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2021-05-30
- **Last Updated**: 2023-02-17
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
### useContext(补充)
我们通过`createContext`创建一个Context之后,在子组件可以通过`useContext`拿到我们在祖先组件创建的`Context`,但是前提是我们需要对子组件用`Context.Provider`标签进行包裹,然后通过`value`属性传值。
这里需要注意` const states = useContext(Context)`只能在子组件内部调用,不可在顶级元素使用。
例子如下:
```react
import {createContext, useContext} from 'react'
const Context = createContext()
function Child() {
const states = useContext(Context)
return (
{states.map(state => {
return (
{state}
)
})}
)
}
function App() {
return (
);
}
export default App;
```
在这里我们通过`Context.Provider`传输了一个数字数组到子代组件,子代组件通过`const states = useContext(Context)`获取到父组件传过来的数据并进行展示。
### 优化
假如我们要给多个子组件提供同一份数据,那么我可以将提供数据的父组件给抽离成一个单独的负责**提供数据**的组件:
```react
import {createContext, useState} from 'react'
export const Context = createContext()
export function CountContextProvider({children}) {
let [count, setCount] = useState(10)
const countObj = {
count,
add() {
setCount(++count)
},
minus() {
setCount(--count)
}
}
return (
{children}
)
}
```
子组件:
```react
import {useContext} from 'react'
import {Context, CountContextProvider} from './CountContextProvider'
function CountChild() {
const {count, add, minus} = useContext(Context)
return (
{count}
)
}
let r = () => {
return (
)
}
export default r
```
## 14. Context
Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。
因为典型的React应用数据是通过Props属性自上而下(由父及子)进行传递的,但这种做法对于某些类型的属性而言是极其繁琐的(例如:地区偏好,UI 主题),这些属性是应用程序中许多组件都需要的。Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props。
总体步骤
```shell
1.creareContext
2.MyContext.Provider包裹通过value传值
3.在需要该值的组件static contextType = ThemeContext;接收context
```
### 14.1 使用Context之前
**何时使用 Context**
Context的目的是共享那些应该在全局共享的数据,例如当前用户的Token数据,或者首选语言。举个例子,在下面的代码中,我们通过一个 “theme” 属性手动调整一个按钮组件的样式:
```react
class App extends React.Component {
render() {
return ;
}
}
function Toolbar(props) {
// Toolbar 组件接受一个额外的“theme”属性,然后传递给 ThemedButton 组件。
// 如果应用中每一个单独的按钮都需要知道 theme 的值,这会是件很麻烦的事,
// 因为必须将这个值层层传递所有组件。
return (
);
}
class ThemedButton extends React.Component {
render() {
return ;
}
}
```
使用 context, 我们可以避免通过中间元素传递 props:
```react
// Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。
// 为当前的 theme 创建一个 context(“light”为默认值)。
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
// 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
// 无论多深,任何组件都能读取这个值。
// 在这个例子中,我们将 “dark” 作为当前的值传递下去。
return (
);
}
}
// 中间的组件再也不必指明往下传递 theme 了。
function Toolbar() {
return (
);
}
class ThemedButton extends React.Component {
// 指定 contextType 读取当前的 theme context。
// React 会往上找到最近的 theme Provider,然后使用它的值。
// 在这个例子中,当前的 theme 值为 “dark”。
static contextType = ThemeContext;
render() {
return ;
}
}
```
**使用Context之前应该有什么考虑?**
使用Context之前我们必须清楚,Context会使得组件的复用性变差,它主要应用场景在于*很多*不同层级的组件需要访问同样一些的数据,请谨慎使用。
如果只是为了避免层层传递一些属性,`组件组合`有时候是比Context更好的解决方案:
如:
```react
// ... 渲染出 ...
// ... 渲染出 ...
// ... 渲染出 ...
```
当我们发现user只在Avatar使用到但其他上层组件都要层层传递的时候,代码会变得非常冗余。解决方案除了用Context之外,我们还可以将该Avatar组件自身传递下去,这样中间组件无需知道 `user` 或者 `avatarSize` 等 props:
```react
function Page(props) {
const user = props.user;
const userLink = (
);
return ;
}
// 现在,我们有这样的组件:
// ... 渲染出 ...
// ... 渲染出 ...
// ... 渲染出 ...
{props.userLink}
```
这种对组件的*控制反转*减少了在你的应用中要传递的 props 数量,这在很多场景下会使得你的代码更加干净,使你对根组件有更多的把控。但是,这并不适用于每一个场景:这种将逻辑提升到组件树的更高层次来处理,会使得这些高层组件变得更复杂,并且会强行将低层组件适应这样的形式,这可能不会是你想要的。
而且你的组件并不限制于接收单个子组件。你可能会传递多个子组件,甚至会为这些子组件(children)封装多个单独的“接口(slots)
### 14.2 Context用法
Context可以有三种用法
#### 14.2.1 Consumer(函数组件)
就是通过Consumer包裹需要拿到值的元素,这样子那个元素就能拿到上下文的值了
```react
import React from 'react'
function Child(props) {
return (
Child: {props.foo}
)
}
const MyContext = React.createContext()
const { Provider, Consumer } = MyContext;
export default function ContextTest() {
return (
)
}
```
Provider设置值value,然后Consumer就可以拿到值,Consumer内部组件可以通过Props.children格式为带参函数的形式拿到value,然后让对应组件渲染结果。
#### 14.2.2 Hook取值
Hook是一种很方便的存在,它让我们不用通过Consumer就可以很简便的取到上级传给我们的值,但是有版本限制,必须
```react
import React, {useContext} from 'react'
const MyContext = React.createContext()
const { Provider, Consumer } = MyContext;
function Child2() {
const value = useContext(MyContext)
return (
Child2: {value.foo}
)
}
export default function ContextTest() {
return (
)
}
```
#### 14.2.3 Class取值(类组件)
```react
import React from 'react'
const MyContext = React.createContext()
const { Provider, Consumer } = MyContext;
class Child3 extends React.Component {
static contextType = MyContext
render() {
return (
Child3: {this.context.foo}
)
}
}
export default function ContextTest() {
return (
)
}
```
### 14.3 总结
总共有三种方法取到Context的值,如下所示:
```react
import React, {useContext} from 'react'
const MyContext = React.createContext()
const { Provider, Consumer } = MyContext;
// Consumer取值
function Child(props) {
return (
Child: {props.foo}
)
}
// Hook取值
function Child2() {
const value = useContext(MyContext)
return (
Child2: {value.foo}
)
}
// 类的方式指定静态属性contextType,这样子上下文就会自动有个context
class Child3 extends React.Component {
static contextType = MyContext
render() {
return (
Child3: {this.context.foo}
)
}
}
export default function ContextTest() {
return (
{/* Consumer包裹元素 */}
{ value => }
)
}
```