likes
comments
collection
share

学习ReactJS Context: 深入理解和使用useContext

作者站长头像
站长
· 阅读数 15

不管是在 Vue 还是在 React 中,都是通过 Props 向数据传递到子组件中,如果组件嵌套很深,就会有 Props 透传的问题。

Vue 中提供了 provideinject 依赖注入的方式来解决,这样只需要在父组件通过 provide 提供数据,然后在任何深层的嵌套的子组件中都可以通过 inject 获取到父组件提供的数据,而且只要父组件提供的数据有变化,那么只要通过 inject 中访问数据的组件都会获取到最新的数据。

React 中也提供了对应的解决方式: Context 也可以将数据传递到任意深层嵌套的组件中。使用 Context 可以用来管理全局数据、主题、用户设置等全局状态信息,可以自己封装一个简易的 store 来管理全局的状态。在 React 中使用 Context 需要3个步骤:1. 创建 Context 2. 通过 context provider 提供数据,3. 在子组件中可以通过 ConsumeruseContext hook 访问父组件提供的数据。

创建 Context

在组件外,使用 React 中内置的方法 createContext(default) 创建 Context 的实例。 createContext的参数是 defaultValue 可以提供一个默认值。createContext 返回一个 Context 实例。

context.js

import { createContext } from 'react';
const Context = createContext(default value);

Provider 数据

把需要数据的组件用 Context.Provider 包括起来,并通过 value 属性提供数据。只有嵌套在 Context.Provider中的组件才能够访问到提供的数据。

import Context from './context';

function Main() {
  const value = 'My Context Value'; // 可以使任意的js类型
  return (
    <Context.Provider value={value}>
      <MyComponent />
    </Context.Provider>
  );
}

不同的 Context 可以嵌套使用

import { ThemeContext, AuthContext } from './context.js'

export const App = () => {
    return (
        <ThemeContext.Provider value={theme}>
            <AuthContext.Provider value={user}>
                <MyComponent />
            </AuthContext.Provider>
        </ThemeContext.Provider>
    )
}

同一个 Context 之间也可以相互嵌套,这是嵌套在内层的 Context 提供的数据会覆盖上层的数据

import { ThemeContext } from './context.js'

export const App = () => {
    return (
        <ThemeContext.Provider value='dark'>
            <ThemeContext.Provider value='light'>
                <MyComponent />
            </ThemeContext.Provider>
        </ThemeContext.Provider>
    )
}

最终在 MyComponent 中获取的数据会是 light

封装 Provider

import { ThemeContext } from './context.js'

const Provider = ({ children }) => {
    const theme = 'light'
    return (
        <ThemeContext.Provider value={theme}>
            { children }
        </ThemeContext.Provider>
    )
}

使用

<Provider>
    <MyComponent />
</Provider>

在子组件中获取数据

  • 使用 Context.Consumer,
import { ThemeContext } from './context.js'

// 这种方式在新版本中已不推荐使用
<ThemeContext.Consumer>
  {theme => (
    <button className={theme} />
  )}
</ThemeContext.Consumer>
  • 使用 hook - useContext
import { ThemeContext } from './context.js'

// 推荐使用 hook 方式
function Button() {
  const theme = useContext(ThemeContext);
  return <button className={theme} />;
}

优化重复渲染

你可以在 value 传递任何类型的数据,当传递的是 object function 时,父组件每一次渲染,就会导致所有调用 useContext 的子组件都会重新渲染一次

function MyApp() {
  const [currentUser, setCurrentUser] = useState(null);

  function login(response) {
    storeCredentials(response.credentials);
    setCurrentUser(response.user);
  }

  return (
    <AuthContext.Provider value={{ currentUser, login }}>
      <Page />
    </AuthContext.Provider>
  );
}

优化的思路就是使用 useCallback 优化方法

const login = useCallback((response) => {
    storeCredentials(response.credentials);
    setCurrentUser(response.user);
}, []);

使用 useMemo 优化对象,优化后

const contextValue = useMemo(() => ({
    currentUser,
    login
}), [currentUser, login]);

整体代码

import { useCallback, useMemo } from 'react';

function MyApp() {
  const [currentUser, setCurrentUser] = useState(null);

  const login = useCallback((response) => {
    storeCredentials(response.credentials);
    setCurrentUser(response.user);
  }, []);

  const contextValue = useMemo(() => ({
    currentUser,
    login
  }), [currentUser, login]);

  return (
    <AuthContext.Provider value={contextValue}>
      <Page />
    </AuthContext.Provider>
  );
}

这样优化之后,所有调用 useContext(AuthContext) 的子组件,不会再重复渲染,除非 currentUser 的值发生变化

相关主题

通过Vue3对比学习Reactjs: 模板语法 vs JSX Vue vs Reactjs之 props Vuejs vs Reactjs:组件之间如何通信 解密v-model:揭示Vue.js和React.js中实现双向数据绑定的不同策略 从零开始:如何在Vue.js和React.js中使用slot实现自定义内容

转载自:https://juejin.cn/post/7250669238418538553
评论
请登录