模式介绍

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

效果

image1

封装

可以看出如果要在子组件中使用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

image2

虽然还是不太复杂, 但是当有很多逻辑代码的时候, 我们可以进一步封装。

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中引入, 现在UserProviderUserProviderComponent.jsx中引入, 所以需要修改useUser.js中的引入路径。

import { useContext } from 'react'
import { UserProvider } from '../components/UserProviderComponent.jsx'

const useUser = () => {
  return useContext(UserProvider)
}

export default useUser
Last Updated:
Contributors: huangdingxin