使用Context处理React全局共享数据

推荐在FC(function component)中使用,文中基本为FC组件

之前看到过使用Context去优雅的管理全局数据,这里做一些分享与代码组织方式,文中如果有不对的地方,欢迎大家指正出来!

用Context管理数据,更贴切React原生语法,并且新版ContextAPI性能也得以提升,之前旧版的Context发生改变时,并不能准确的知道具体有哪些子组件需要更新,需要所有组件更新(内部会有小优化去bailout跳过),新版ContextAPI更多像是一种依赖收集,将Context改变挂载在该组件中,就可以明确知道这个组件的依赖改变了,去更新他。

(上图为ReactFiber内Context依赖)

Context使用回顾

使用Context实现全局主题

// index.jsx

const ThemeContext = React.createContext('light');

const Index = () => {
  const [theme, setTheme] = React.useState('light');

  return (
    <ThemeContext.Provider value={theme}>
      <App />
    </ThemeContext.Provider>
  )
}

在某一个具体button中使用

// Button.jsx

const Button = props => {
  // 可直接在该Button中应用主题
  const theme = React.useContext(ThemeContext)

  return (
    <button {...props} />
  )
}

这样就避免了组件中充满了无用的透传,使代码变得较为简洁。

通过Context修改全局数据

我们将状态缓存在最顶部,将state,与setState都传递下去即可,就达到了缓存在子组件内修改全局状态。

// CountStateContext.js
const CountStateContext = React.createContext();

// index.jsx
const Index = () => {
  const [countState, setCountState] = React.useState(0)

  return (
    <CountStateContext.Provider value={[countState, setCountState]}>
      <App />
    </CountStateContext.Provider>
  )
}

// Sub1.jsx
const Sub1 = () => {
  const [countState, setCountState] = React.useContext(CountStateContext)

  return (
    <>
      <h1>Count: {countState}</h1>
      <button onClick={() => {setCountState(c => c + 1)}}>Increment CountState</button>
    </>
  )
}

// Sub2.jsx
const Sub2 = () => {
    const [countState] = React.useContext(CountStateContext)

  return (
    <>
      <h1>Count: {countState}</h1>
    </>
  )
}

// Sub3.jsx
const Sub3 = () => {
    const [, setCountState] = React.useContext(CountStateContext)

  return (
    <>
      <button onClick={() => {setCountState(c => c + 1)}}>Increment CountState</button>
    </>
  )
}

Context都传递下去的问题

将一个顶层状态的statesetState都传递下去,但是不一定在所有的子组件中都会用到这两个数据

比如某个子组件中可能只是单纯想进行一个数据的修改,但是由于context中同时也依赖了state,所以当顶层组件的state发生改变时,该组件也会进行一次重新的渲染,那这样其实不符合我们的预期的,所以就将Context存储的数据拆分,那我们的核心就是读写分离!

可能会发生Context套娃,没关系先把主要问题解决了

// CountContext.js
const CountContext = React.createContext();
const SetCountContext = React.createContext();

// index.jsx
const Index = () => {
  const [count, setCount] = React.useState(0)

  return (
    <CountContext.Provider value={count}>
      <SetCountContext.Provider value={setCount}>
        <App />
      </SetCountContext.Provider>
    </CountContext.Provider>
  )
}

// Sub1.jsx
const Sub1 = () => {
  const count = React.useContext(CountContext)
  const setCount = React.useContext(SetCountContext)

  return (
    <>
      <h1>Count: {count}</h1>
      <button onClick={() => {setCount(c => c + 1)}}>Increment CountState</button>
    </>
  )
}

// Sub2.jsx
const Sub2 = () => {
  const count = React.useContext(CountContext)

  return (
    <>
      <h1>Count: {count}</h1>
    </>
  )
}

// Sub3.jsx
const Sub3 = () => {
  const setCount = React.useContext(SetCountContext)

  return (
    <>
      <button onClick={() => {setCount(c => c + 1)}}>Increment CountState</button>
    </>
  )
}

将Context存储的数据抽离出来

目前是将所有的数据都存储在顶层组件中的,我们也可以使用一个自定义Hooks去存储我们的数据,最终附带吐出一个带有Context的组件即可

// CountContext.jsx
const CountContext = React.createContext();
const SetCountContext = React.createContext();

function useCount() {
  return React.useContext(CountContext)
}

function useSetCount() {
  return React.useContext(SetCountContext)
}

function CountStoreProvider({ children }) {
  const [count, setCount] = React.useState()

  return (
    <CountContext.Provider value={count}>
      <SetCountContext.Provider value={setCount}>
        {children}
      </SetCountContext.Provider>
    </CountContext.Provider>
  )
}

export { useCount, useSetCount, CountStoreProvider }

// index.jsx
import { CountStoreProvider } from './CountContext'

const Index = () => {

  return (
    <CountStoreProvider>
      <App />
    </CountStoreProvider>
  )
}

// Sub1.jsx
import { useCount, useSetCount } from './CountContext'

const Sub1 = () => {
  const count = useCount()
  const setCount = useSetCount()

  return (
    <>
      <h1>Count: {count}</h1>
      <button onClick={() => {setCount(c => c + 1)}}>Increment CountState</button>
    </>
  )
}

最终我们创建了一个类似store的组件,即CountContext,之后便可以方便的修改我们的数据,将此Context内部的数据都封装在自定义Hooks内部,也减少了对外部代码的侵入,使用起来比较简洁。

配合Reducer使用

前面一直使用useState,那么将其存储为useReducer使用范围会更广,那么将Context中存储的数据修改为reducer会更加优雅一点,这一部分就留给大家去改造了。

在线实例

另外文中一直尝试的修改数据为同步的,如果有异步的数据修改,是将其在React组件内部处理好,再同步的dispatch出去,还是可以在context组件内部去做更多异步的处理,需要大家自己去实践了。

优雅!

原文链接:juejin.im

上一篇:Vue3.0 尝鲜
下一篇:【真香系列】Vue-Next 源码第四章

相关推荐

  • 🌈 React 函数式组件优化

    1. React 性能优化思路 减少重新 render 的次数。 减少计算的量。主要是减少重复计算,对于函数式组件来说,每次 render 都会重新从头开始执行函数调用。

    1 个月前
  • 🆘 一次理解清楚,为什么使用 React useEffect 中使用 setInterval 获取的值不是最新的

    Intro 这篇文章将通过一个使用 React Hook 常遇到的问题(stale state)入手,尝试理解 Hook 的内部运行逻辑。 废话不多说,直接看示例Sandbox。

    3 个月前
  • (译)CSS 定位与层叠上下文(Stacking context)

    你是否在使用定位时,会遇到一个定位元素即使设置更高的层级,也无法将另一个定位元素覆盖的情况?通过理解层叠上下文,你就能更好的构建你的应用。理解渲染流程和层叠顺序当浏览器将 HTML 解析成 DOM 结...

    1 个月前
  • 高频数据交换下Flutter与ReactNative的对比

    (标题图片来自网络,侵删) 后端使用go写的socketio服务模拟期货行情数据,每10ms推送10条行情数据 ReactNative已经尽力优化了。 Flutter由于没flutter-socket...

    2 年前
  • 高阶组件 + New Context API = ?

    1. 前言 继上次小试牛刀尝到高价组件的甜头之后,现已深陷其中无法自拔。。。那么这次又会带来什么呢?今天,我们就来看看【高阶组件】和【New Context API】能擦出什么火花! 2. New C...

    2 年前
  • 高性能迷你React框架 anu1.3.0 发布

    anujs1.3.0是一款高性能React-like框架,是目前世界上对React16兼容最好的迷你库。 自React16起,相继推出createContext,createPortal, creat...

    3 年前
  • 高德地图 react-amap 实战

    react-amap 是基于 React 的高德地图组件。 1. 获取地图示例 react-amap 作为高德地图在 React 中的实现,实际使用中不可避免的需要通过地图对象调用各种方法,reac...

    1 年前
  • 高品质 React UI 组件

    A high quality UI Toolkit, A Component Library for React 16+. 💘 Installation npm install isui --s...

    3 年前
  • 骚操作!在react中使用vuex

    原文地址 前言 笔者最近在学习使用react,提到react就绕不过去redux。redux是一个状态管理架构,被广泛用于react项目中,但是redux并不是专为react而生,两者还需要react...

    2 年前
  • 项目文档说明:react + Ant Design 的 blog-react-admin

    前言 此 blog-react-admin 项目是基于 蚂蚁金服开源的 ant design pro 之上,用 react 全家桶 + Ant Design 的进行再次开发的,项目已经开源,项目地址...

    2 年前

官方社区

扫码加入 JavaScript 社区