<commit_before># 常见陷阱与最佳实践

使用 MobX 遇到坑了？本章节涵盖了一些 MobX 新手可能会遭遇的一些常见问题。

#### 导入的路径有误

因为 MobX 自带了 TypeScript 的 typings ，一些导入自动完成工具(至少在 VSCode 中是这样的)的自动导入会有问题，像这样:

```javascript
// 错误的
import { observable } from "mobx/lib/mobx"
```

这是不正确的，但却不会总是立即导致运行时错误。所以需要注意。导入 `mobx` 包中任何东西的唯一正确方式是:

```javascript
// 正确的
import { observable } from "mobx"
```

#### 装饰器问题?

有关装饰器的设置提示和限制，请参见 [装饰器](decorators.md) 一节。

#### `Array.isArray(observable([1,2,3])) === false`

在 ES5 中没有继承数组的可靠方法，因此 observabl e数组继承自对象。
这意味着一般的库没有办法识别出 observable 数组就是普通数组(像 lodash，或 `Array.concat` 这样的内置操作符)。
这个问题很容易解决，在把 observable 数组传递给其它库之前先调用 `observable.toJS()` 或 `observable.slice()` 将其转化为普通数组。
只要外部库没有修改数组的意图，那么一切都将如预期一样的正常运作。
可以使用 `isObservableArray(observable)` 来检查是否是 observable 数组。

#### `object.someNewProp = value` 不起作用

对于声明 observable 时未分配的属性，MobX observable **对象**  检测不到，也无法作出反应。
因此 MobX observable 对象充当具有预定义键的记录。
可以使用 `extendObservable(target, props)` 来为一个对象引入新的 observable 属性。
但是像 `for .. in` 或 `Object.keys()` 这样的对象迭代不会自动地对这样的改变作出反应。
如果你需要动态键对象，例如通过 id 来存储用户，可以使用 [`observable.map`](../refguide/map.md) 来创建 observable **映射**。
想了解更多详情，请参见 [MobX 会对什么作出反应?](react.md)。

### 在所有渲染 `@observable` 的组件上使用 `@observer`

`@observer` 只会增强你正在装饰的组件，而不是内部使用了的组件。
所以通常你的所有组件都应该是装饰了的。但别担心，这样不会降低效率，相反 `observer` 组件越多，渲染效率越高。

### 不要拷贝 observables 属性并存储在本地

Observer 组件只会追踪在 render 方法中存取的数据。常见的错误的是从 observable 属性中提取数据并存储，这样的数据是不会被追踪的:

```
class User {
  @observable name
}

class Profile extends React.Component {
  name

  componentWillMount() {
    // 错误的
    // 这会解除 user.name 的引用并只拷贝值一次！未来的更新不会被追踪，因为生命周期钩子不是响应的
    // 像这样的赋值会创建冗余数据
    this.name = this.props.user.name
  }

  render() {
    return <div>{this.name}</div>
  }
}
```

正确的方法通过不将 observables 的值存储在本地(显然，上面的示例很简单，但却是有意为之的)，或通过将其定义为计算属性:

```
class User {
  @observable name
}

class Profile extends React.Component {
  @computed get name() {
    // 正确的; 计算属性会追踪 `user.name` 属性
    return this.props.user.name
  }

  render() {
    return <div>{this.name}</div>
  }
}
```

### Render 回调函数**不是** render 方法的一部分

因为 `observer` 只作用于当前组件的 `render` 函数，传递一个 render 回调函数或组件给子组件不会自动地变成响应的。
想了解更多详情，请参见 [MobX 会对什么作出反应](https://github.com/mobxjs/mobx/blob/gh-pages/docs/best/react.md#mobx-only-tracks-data-accessed-for-observer-components-if-they-are-directly-accessed-by-render) 指南。
<commit_msg>update the link to CN docs<commit_after># 常见陷阱与最佳实践

使用 MobX 遇到坑了？本章节涵盖了一些 MobX 新手可能会遭遇的一些常见问题。

#### 导入的路径有误

因为 MobX 自带了 TypeScript 的 typings ，一些导入自动完成工具(至少在 VSCode 中是这样的)的自动导入会有问题，像这样:

```javascript
// 错误的
import { observable } from "mobx/lib/mobx"
```

这是不正确的，但却不会总是立即导致运行时错误。所以需要注意。导入 `mobx` 包中任何东西的唯一正确方式是:

```javascript
// 正确的
import { observable } from "mobx"
```

#### 装饰器问题?

有关装饰器的设置提示和限制，请参见 [装饰器](decorators.md) 一节。

#### `Array.isArray(observable([1,2,3])) === false`

在 ES5 中没有继承数组的可靠方法，因此 observabl e数组继承自对象。
这意味着一般的库没有办法识别出 observable 数组就是普通数组(像 lodash，或 `Array.concat` 这样的内置操作符)。
这个问题很容易解决，在把 observable 数组传递给其它库之前先调用 `observable.toJS()` 或 `observable.slice()` 将其转化为普通数组。
只要外部库没有修改数组的意图，那么一切都将如预期一样的正常运作。
可以使用 `isObservableArray(observable)` 来检查是否是 observable 数组。

#### `object.someNewProp = value` 不起作用

对于声明 observable 时未分配的属性，MobX observable **对象**  检测不到，也无法作出反应。
因此 MobX observable 对象充当具有预定义键的记录。
可以使用 `extendObservable(target, props)` 来为一个对象引入新的 observable 属性。
但是像 `for .. in` 或 `Object.keys()` 这样的对象迭代不会自动地对这样的改变作出反应。
如果你需要动态键对象，例如通过 id 来存储用户，可以使用 [`observable.map`](../refguide/map.md) 来创建 observable **映射**。
想了解更多详情，请参见 [MobX 会对什么作出反应?](react.md)。

### 在所有渲染 `@observable` 的组件上使用 `@observer`

`@observer` 只会增强你正在装饰的组件，而不是内部使用了的组件。
所以通常你的所有组件都应该是装饰了的。但别担心，这样不会降低效率，相反 `observer` 组件越多，渲染效率越高。

### 不要拷贝 observables 属性并存储在本地

Observer 组件只会追踪在 render 方法中存取的数据。常见的错误的是从 observable 属性中提取数据并存储，这样的数据是不会被追踪的:

```
class User {
  @observable name
}

class Profile extends React.Component {
  name

  componentWillMount() {
    // 错误的
    // 这会解除 user.name 的引用并只拷贝值一次！未来的更新不会被追踪，因为生命周期钩子不是响应的
    // 像这样的赋值会创建冗余数据
    this.name = this.props.user.name
  }

  render() {
    return <div>{this.name}</div>
  }
}
```

正确的方法通过不将 observables 的值存储在本地(显然，上面的示例很简单，但却是有意为之的)，或通过将其定义为计算属性:

```
class User {
  @observable name
}

class Profile extends React.Component {
  @computed get name() {
    // 正确的; 计算属性会追踪 `user.name` 属性
    return this.props.user.name
  }

  render() {
    return <div>{this.name}</div>
  }
}
```

### Render 回调函数**不是** render 方法的一部分

因为 `observer` 只作用于当前组件的 `render` 函数，传递一个 render 回调函数或组件给子组件不会自动地变成响应的。
想了解更多详情，请参见 [MobX 会对什么作出反应](https://github.com/SangKa/mobx-docs-cn/blob/master/docs/best/react.md#mobx-只会为-observer-组件追踪数据存取如果数据是直接通过-render-进行存取的) 指南。

{'fraction_non_alphanumeric': 0.13873834814654237, 'fraction_numerical': 0.0017342293518317797, 'mean_word_length': 5.286103542234333, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '730044', 'n_tokens_mistral': 2845, 'n_tokens_neox': 3084, 'n_words': 351}