模式介绍
Provider 模式是一种常见的设计模式,它的作用是将某个对象注入到所有使用它的对象中去。在 React 中,我们可以使用 Context API 来实现 Provider 模式。
使用场景
Provider 模式在 React 中的使用场景非常多,比如:
- 全局状态管理
- 主题切换
- 多语言切换
- 等等
简单使用
现在介绍一下简单的使用方法。 主要用于父组件向下传递数据。
项目结构
App.jsx
作为父组件, C1.jsx
作为子组件, C2.jsx
作为孙子组件。
App.jsx
import React from 'react'
import { createContext } from 'react'
import C1 from './components/C1/index.jsx'
export const UserContextProvider = createContext(undefined)
function App() {
const user = {
name: '小明',
age: 18,
sex: '男',
}
return (
<>
<UserContextProvider.Provider value={user}>
<C1 />
</UserContextProvider.Provider>
</>
)
}
export default App
C1.jsx
import React from 'react'
import { useContext } from 'react'
import { UserContextProvider } from '../../App.jsx'
import C2 from './C2/index.jsx'
const C1 = () => {
const user = useContext(UserContextProvider)
return (
<div style={{ height: '150px', backgroundColor: 'pink' }}>
c1组件
<div>age: {user.age}</div>
<C2 />
</div>
)
}
export default C1
C2.jsx
import React from 'react'
import { useContext } from 'react'
import { UserContextProvider } from '../../../App.jsx'
const C2 = () => {
const user = useContext(UserContextProvider)
return (
<div style={{ height: '50px', marginTop: "50px", backgroundColor: 'green' }}>
c2组件
<div>name: {user.name}</div>
</div>
)
}
export default C2
效果
封装
可以看出如果要在子组件中使用user
数据, 需要在子组件中引入UserContextProvider
并使用useContext
方法, 这样会导致代码冗余, 为了解决这个问题, 我们可以封装一个useUser
方法, 用于获取user
数据。
封装一个hooks, 用于获取user
数据。
useUser.js
import { useContext } from 'react'
import { UserContextProvider } from '../App.jsx'
const useUser = () => {
const user = useContext(UserContextProvider)
return user
}
export default useUser
C1.jsx
import React from 'react'
import useUser from '../../hooks/useUser.js'
import C2 from './C2/index.jsx'
const C1 = () => {
const user = useUser()
return (
<div style={{ height: '150px', backgroundColor: 'pink' }}>
c1组件
<div>age: {user.age}</div>
<C2 />
</div>
)
}
export default C1
进阶
改造App.jsx, 使组件变得稍微复杂一点。
App.jsx
import React from 'react'
import { createContext } from 'react'
import C1 from './components/C1/index.jsx'
export const UserProvider = createContext(undefined)
function App() {
const user = {
name: '小明',
age: 18,
sex: '男',
}
return (
<>
<UserProvider.Provider value={user}>
<h3>用户信息</h3>
<p>姓名:{user.name}</p>
<p>年龄:{user.age * 0.5}</p>
<p>性别:{user.sex}</p>
<hr />
<C1 />
</UserProvider.Provider>
</>
)
}
export default App
如图, 我们在App.jsx
的页面显示了user的全部数据, 但是年龄显示的是user.age * 0.5
。
虽然还是不太复杂, 但是当有很多逻辑代码的时候, 我们可以进一步封装。
UserProviderComponent.jsx
把App.jsx
中的逻辑代码封装到UserProviderComponent.jsx
中。
import React, { createContext } from 'react'
export const UserProvider = createContext(undefined)
const UserProviderComponent = ({children}) => {
const user = {
name: '小明',
age: 18,
sex: '男',
}
return (
<>
<UserProvider.Provider value={user}>
<h3>用户信息</h3>
<p>姓名:{user.name}</p>
<p>年龄:{user.age * 0.5}</p>
<p>性别:{user.sex}</p>
<hr />
{children}
</UserProvider.Provider>
</>
)
}
export default UserProviderComponent
其中{children}
是App.jsx
中的<C1 />
组件。
props.children
, 用于获取组件的子节点。
App.jsx
import React from 'react'
import UserProviderComponent from './components/UserProviderComponent'
import C1 from './components/C1/index.jsx'
function App() {
return (
<>
<UserProviderComponent>
<C1 />
</UserProviderComponent>
</>
)
}
export default App
useUser.js
由于UserProvider
原先从App.jsx
中引入, 现在UserProvider
从UserProviderComponent.jsx
中引入, 所以需要修改useUser.js
中的引入路径。
import { useContext } from 'react'
import { UserProvider } from '../components/UserProviderComponent.jsx'
const useUser = () => {
return useContext(UserProvider)
}
export default useUser