MobX 中文文档

  开始纠错

@observer

observer 函数 / 装饰器可以将React组件转换为可响应MobX的组件。 它将components的render函数使用 MobX.autorun 包裹,以确保任何依赖数据的更新都会触发重渲染。 它是通过独立的 mobx-react 包提供的。

import {observer} from "mobx-react";

var timerData = observable({
    secondsPassed: 0
});

setInterval(() => {
    timerData.secondsPassed++;
}, 1000);

@observer class Timer extends React.Component {
    render() {
        return (<span>Seconds passed: { this.props.timerData.secondsPassed } </span> )
    }
});

React.render(<Timer timerData={timerData} />, document.body);

提示:当observer需要与其他装饰器组合使用时,请确保是最里层(第一个被应用)的装饰器,否则不会有任何效果。

注意@observer 装饰器是可选的,使用 observer(class Timer ... { }) 具有同样的效果。

疑难杂症: 在你的组件中解引用

MobX 可以做很多事,但是它不能使原始数据可观察(虽然可以把它们包含在一个对象中,见 boxed observables)。所以它观察的不是对象中的 ,而是 属性。这意味着 observer 实际上响应了你解引用值的事实。所以我们在上面的例子中,如果我们如下初始化,则 Timer 组件 不会 做出响应:

React.render(<Timer timerData={timerData.secondsPassed} />, document.body)

在这段代码里,我们只是把 secondsPassed 的当前值传递给 Timer ,这是不可变得值 0 (JS中所有的原始数据都是不可变的)。这个数字在将来不会再发生改变,所以 Timer 将永远不会更新。属性 secondsPassed 将来会发生改变,所以我们需要在组件 访问它。换句话说:值需要通过 引用 传递 而非 传递。

ES5支持

在 ES5 环境下,observer 组件可以简单的通过使用 observer(React.createClass({ ... 声明。另请参考 syntax guide

无状态函数组件

上面的 Timer 组件也可以通过给 observer 传递无状态函数进行编写:

import {observer} from "mobx-react";

const Timer = observer(({ timerData }) =>
    <span>Seconds passed: { timerData.secondsPassed } </span>
);

可观察的组件本地状态

就像正常的类一样,你可以在组件中使用 @observable 装饰器引入可观察的属性。这意味着你可以有组件自己的本地状态,而且不需要React的冗余和强制的 setState 机制来管理它,它是非常强大的。响应状态将由 render 接收,而不会显式的调用React的生命周期方法如 componentShouldUpdatecomponentWillUpdate 。如果你需要她们,只需正常使用React基于 state 的APIs即可。

上面的例子也可以写成:

import {observer} from "mobx-react"
import {observable} from "MobX"

@observer class Timer extends React.Component {
    @observable secondsPassed = 0

    componentWillMount() {
        setInterval(() => {
            this.secondsPassed++
        }, 1000)
    }

    render() {
        return (<span>Seconds passed: { this.secondsPassed } </span> )
    }
})

React.render(<Timer />, document.body)

对于使用可被观察的组件局部状态有很多优点,具体详见 我们为什么要停止使用setState

observer 连接到 stores

mobx-react 包也提供了 Provider 组件,可以用于使用React的上下文机制来传递stores。为了连接多个stores,可以传递一个store名称的数组参数给 observer ,它会把这些stores变成props。当使用装饰器(@observer(["store"]) class ...)或者 observer(["store"], React.createClass({ ... 方法也是支持的。

例如:

const colors = observable({
   foreground: '#000',
   background: '#fff'
});

const App = () =>
  <Provider colors={colors}>
     <app stuff... />
  </Provider>;

const Button = observer(["colors"], ({ colors, label, onClick }) =>
  <button style={{
      color: colors.foreground,
      backgroundColor: colors.background
    }}
    onClick={onClick}
  >{label}<button>
);

// later..
colors.foreground = 'blue';
// 所有按钮都更新了

更多信息请查阅 mobx-react 文档.

何时使用 observer ?

这里有一个简单的原则:所有使用可观察数据渲染的组件。如果你不想把这个组件标记为观察者,例如为了减少通用组件库的依赖,请确保你传递的只是纯粹的数据。

通过使用 @observer,我们就不需要为了渲染的目的区分智能(smart)组件和木偶(dump)组件。它仍然是一个很好的分离,我们只需关注在哪里处理事件,发起请求等。当它们 自身 的依赖发生变化时,所有的组件都负责更新。它的开销是可以忽略的,它可以确保每当你使用可观察的数据时,组件将会根据它响应。更多信息请参阅 thread

observerPureRenderMixin

observer 阻止 props 浅改变时的重新渲染,这使得传递到组件的数据是具有响应性的,这是非常有意义的。这个行为和 React PureRender mixin 是非常相似的,除了仍然需要处理状态改变。如果一个组件提供了它自己的 shouldComponentUpdate 函数,那么这个函数的优先级更高。可以参阅这个解释 github issue

componentWillReact (生命周期钩子)

React组件通常在一个新的堆栈上渲染,这使得它经常很难弄清楚到底是什么 导致 了组件的重新渲染。当使用mobx-react时,你可以定义一个新的生命周期钩子,当一个组件准备重新渲染时,componentWillReact (双关语)将会被触发,因为它观察到的数据已经发生了变化。这使得它很容易的追踪到什么造成了组件的重新渲染。

import {observer} from "mobx-react";

@observer class TodoView extends React.Component {
    componentWillReact() {
        console.log("我会重渲染, 当todo改变的时候!");
    }

    render() {
        return <div>this.props.todo.title</div>;
    }
}
  • componentWillReact 没有参数
  • componentWillReact 初始化 render 之前不会被触发(可使用 componentWillMount 取代)
  • componentWillReact 当接收到新的 props 或调用 setState 之后不会被触发(可使用 componentWillUpdate 替代)

优化 components

请参阅相关 章节.

observer 组件的特征

  • observer 组件仅仅订阅上次渲染时主动使用的数据结构。这意味着你不能 under-subscribe 或 over-subscribe,你甚至可以在渲染时使用稍后才可用的数据,这是异步加载数据的理想选择。
  • 你不需要声明组件将要使用什么数据,而相反,它是在运行时才确定其依赖并进行以精细化的方式跟踪。
  • 通常,响应式组件没有或有极少的状态,因为在与其它组件共享的对象中封装(视图)状态通常更方便。但你仍可以自由地使用其状态。
  • @observerPureRenderMixin 实现 shouldCompoentUpdate 的方式一样,所以其子元素没有重新进行渲染的必要。
  • 响应式组件侧向加载数据,所以其父组件如果没必要更新,其就不会重新渲染,即使它的子组件需要重新渲染。
  • @observer 不依赖于 React 的上下文系统。

使你的构建工具支持装饰器

装饰器语法默认是不被支持的。

  • 如果你使用的是 typescript ,请开启 --experimentalDecorators 这一构建工具标记,并在 tsconfig.json 中将experimentalDecorators 设置为 true (推荐)。
  • 如果你使用的是 babel5 , 请确保 --stage 0 传给了Babel CLI
  • 如果你使用的是 babel6 ,见下面这个配置例子)