mobx-react-lite的🏆observer都干了啥?

495 阅读5分钟

mobx-react-lite 是 MobX 和 React 的轻量级集成库,它提供了简单且高效的方式来在 React 组件中使用 MobX 状态管理。它的设计目标是减少与 React 的集成代码,优化性能,并且保持 MobX 的灵活性和声明性。mobx-react-lite 特别适用于功能组件(Function Components)和钩子(Hooks)模式,而不是传统的类组件和生命周期方法。

MobX 和 React 的关系

MobX 是一个状态管理库,它通过观测和响应式编程来帮助开发者管理应用的状态。MobX 使得状态的变更可以自动反映到 UI 中,从而简化了复杂的状态管理逻辑。

React 和 MobX 结合时,通常是通过组件将 MobX 中的状态映射到 UI 上,React 在此负责渲染视图,而 MobX 则负责状态的管理。mobx-react 是 MobX 和 React 的官方集成库,它的目标是帮助开发者将 MobX 状态与 React 组件进行连接。

然而,mobx-react-lite 是一个为 React 16.8+ 中的函数组件和 Hooks 提供优化的轻量级实现,它对 MobX 与 React 的集成做了优化,特别是在性能方面。

mobx-react-lite 主要特点

  1. 轻量级

    • 相比于 mobx-reactmobx-react-lite 仅依赖于 React 和 MobX,因此它的体积更小,适合在 React 函数组件中使用。
    • 不支持类组件和生命周期方法,只适用于函数组件和 hooks 模式。
  2. 优化性能

    • mobx-react-lite 使用 React 的 useEffectuseState 等 hook 来实现状态管理,从而减少了不必要的渲染。
    • 内部使用 React.memouseObserver 来确保只有在必要时才重新渲染组件,避免了不必要的渲染。
  3. 简化 API

    • mobx-react-lite 使用简单的 API,主要是通过 observer 高阶组件来使组件响应 MobX 状态变化,并通过 useObserver hook 来处理状态变化。
    • 不需要 Providerinject 等复杂的 API,可以更简洁地集成 MobX。
  4. 支持 Hooks

    • mobx-react-lite 主要针对函数组件和 hooks。你可以通过 useObserverobserver 来观察状态的变化。
    • 它不需要复杂的类组件和生命周期方法,符合 React 的现代开发风格。

核心概念和使用方式

1. observer 高阶组件

observermobx-react-lite 中的主要工具,它是一个高阶组件(HOC),用于将组件包裹起来并使其响应 MobX 的状态变化。

import { observer } from 'mobx-react-lite';
import React from 'react';

// 一个简单的 MobX store
const store = observable({
  count: 0,
  increment() {
    this.count += 1;
  }
});

// 使用 observer 包裹组件,使其响应 MobX 状态
const Counter = observer(() => {
  return (
    <div>
      <p>Count: {store.count}</p>
      <button onClick={() => store.increment()}>Increment</button>
    </div>
  );
});

在上面的代码中,Counter 组件被 observer 包裹后,每当 store.count 的值发生变化时,Counter 组件会自动重新渲染。

2. useObserver Hook

useObserver 是一个用于在函数组件中直接观察 MobX 状态的 hook,它的功能和 observer 类似,但提供了更细粒度的控制,通常在更复杂的场景下使用。

import { useObserver } from 'mobx-react-lite';
import React from 'react';

// 一个简单的 MobX store
const store = observable({
  count: 0,
  increment() {
    this.count += 1;
  }
});

const Counter = () => {
  return useObserver(() => (
    <div>
      <p>Count: {store.count}</p>
      <button onClick={() => store.increment()}>Increment</button>
    </div>
  ));
};

在上面的例子中,useObserver hook 返回一个响应式组件,自动监听 MobX 状态的变化。

3. observable

observable 是 MobX 中用于创建可观察状态的函数,它可以将对象、数组、或者简单的变量标记为响应式。

import { observable } from 'mobx';

// 创建可观察的对象
const store = observable({
  count: 0,
  increment() {
    this.count += 1;
  }
});

observable 会使 store.count 成为一个响应式值,当它改变时,所有依赖于 store.count 的组件都会自动更新。

4. action

在 MobX 中,action 是用于修改状态的函数。通常情况下,我们将状态的变更封装在 action 中,以便更好地跟踪和调试状态变化。

import { action, observable } from 'mobx';

const store = observable({
  count: 0,
  increment: action(function() {
    this.count += 1;
  })
});

action 可以帮助 MobX 更好地优化状态变更的追踪,尤其在进行复杂的多步操作时,它也有助于避免状态不一致的问题。

observer源码

export function observer<P extends object, TRef = {}>(
    baseComponent: React.RefForwardingComponent<TRef, P>,
    options?: IObserverOptions
) {
    // The working of observer is explained step by step in this talk: https://www.youtube.com/watch?v=cPF4iBedoF0&feature=youtu.be&t=1307
    if (isUsingStaticRendering()) {
        return baseComponent
    }

    const realOptions = {
        forwardRef: false,
        ...options
    }

    const baseComponentName = baseComponent.displayName || baseComponent.name

    const wrappedComponent = (props: P, ref: React.Ref<TRef>) => {
        return useObserver(() => baseComponent(props, ref), baseComponentName)
    }
    wrappedComponent.displayName = baseComponentName

    // memo; we are not interested in deep updates
    // in props; we assume that if deep objects are changed,
    // this is in observables, which would have been tracked anyway
    let memoComponent
    if (realOptions.forwardRef) {
        // we have to use forwardRef here because:
        // 1. it cannot go before memo, only after it
        // 2. forwardRef converts the function into an actual component, so we can't let the baseComponent do it
        //    since it wouldn't be a callable function anymore
        memoComponent = memo(forwardRef(wrappedComponent))
    } else {
        memoComponent = memo(wrappedComponent)
    }

    copyStaticProperties(baseComponent, memoComponent)
    memoComponent.displayName = baseComponentName

    return memoComponent
}

这段代码定义了一个名为observer的函数,它是一个React高阶组件(HOC),用于将普通的React组件转换为响应式组件。这意味着包裹后的组件能够响应状态的变化,并在状态变化时重新渲染。

  1. 检查静态渲染:如果检测到当前环境是静态渲染(如服务器端渲染),则直接返回原始组件。

  2. 处理选项:合并用户提供的选项options与默认选项,这里默认选项是{ forwardRef: false }

  3. 获取组件名称:获取传入组件的displayNamename属性,用于调试和显示。

  4. 定义包裹组件:定义了一个wrappedComponent函数,它使用useObserver钩子来包裹原始组件baseComponentuseObserver钩子负责设置观察器,以便在组件依赖的可观察数据变化时触发更新。

  5. 设置displayName:设置wrappedComponentdisplayName属性,以便在React开发者工具中正确显示。

  6. 使用memo优化:使用React.memowrappedComponent进行包裹,以避免不必要的重新渲染。如果选项中forwardReftrue,则使用React.forwardRef包裹wrappedComponent,以便能够正确地传递ref

  7. 复制静态属性:使用copyStaticProperties函数将原始组件的静态属性复制到memoComponent,以保留原始组件的所有静态方法和属性。

  8. 返回memoComponent:返回最终的memoComponent,它是经过memo和可能的forwardRef包裹的响应式组件。

END

mobx-react-lite 是 MobX 和 React 的高效、轻量级集成库,适用于函数组件和现代的 React 开发风格。它优化了性能,减少了不必要的渲染,并通过 observeruseObserver 提供了简单的 API 来使组件响应 MobX 的状态变化。如果你正在使用 React 函数组件并且希望使用 MobX 作为状态管理方案,mobx-react-lite 是一个非常合适的选择。