Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update docs to mobx 3 #725

Merged
merged 2 commits into from
Jan 9, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions docs/best/pitfalls.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ MobX observable _objects_ do not detect or react to property assignments that we
So MobX observable objects act as records with predefined keys.
You can use `extendObservable(target, props)` to introduce new observable properties to an object.
However object iterators like `for .. in` or `Object.keys()` won't react to this automatically.
If you need a dynamically keyed object, for example to store users by id, create observable _map_s using `asMap`.
More info on [asMap](https://github.com/mobxjs/mobx/issues/219#issuecomment-220224813).
If you need a dynamically keyed object, for example to store users by id, create observable _map_s using [`observable.map`](../refguide/map.md).
For more info see [what will MobX react to?](react.md).

### Use `@observer` on all components that render `@observable`'s.
Expand Down
4 changes: 3 additions & 1 deletion docs/intro/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ setInterval(action(function tick() {
}), 1000);
```

The `action` wrapper is only needed when using MobX in strict mode (by default off), but will help you to better structure applications and expresses the intention of a function to modify state.
The `action` wrapper is only needed when using MobX in strict mode (by default off).
It is recommended to use action though as it will help you to better structure applications and expresses the intention of a function to modify state.
Also it automatically applies transactions for optimal performance.

Feel free to try this example on [JSFiddle](http://jsfiddle.net/mweststrate/wgbe4guu/) or by cloning the [MobX boilerplate project](https://github.com/mobxjs/mobx-react-boilerplate)
43 changes: 41 additions & 2 deletions docs/refguide/action.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
Usage:
* `action(fn)`
* `action(name, fn)`
* `@action classMethod`
* `@action(name) classMethod`
* `@action classMethod() {}`
* `@action(name) classMethod () {}`
* `@action boundClassMethod = (args) => { body }`
* `@action(name) boundClassMethod = (args) => { body }`
* `@action.bound classMethod() {}`
* `@action.bound(function() {})

Any application has actions. Actions are anything that modify the state.
With MobX you can make it explicit in your code where your actions live by marking them.
Expand Down Expand Up @@ -68,3 +70,40 @@ Example:
The usage of `runInAction` is: `runInAction(name?, fn, scope?)`.

If you use babel, this plugin could help you to handle your async actions: [mobx-deep-action](https://github.com/mobxjs/babel-plugin-mobx-deep-action).

## Bound actions

The `action` decorator / function follows the normal rules for binding in javascript.
However, Mobx 3 introduces `action.bound` to automatically bind actions to the targeted object.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please shortly explain the difference as in:

The action decorator / function follows the normal rules for binding in javascript... That is it [only] binds this and that
... action.bound to automatically bind actions to the targeted object. That is in this and that situation

Note that `(@)action.bound` does, unlike `action`, not take a name parameter, the name will always be based on the property the action is bound to.

Example:

```javascript
class Ticker {
@observable this.tick = 0

@action.bound
increment() {
this.tick++ // 'this' will always be correct

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please explain why this might not be correct using plain action

}
}

const ticker = new Ticker()
setInterval(ticker.increment, 1000)
```

Or

```javascript
const ticker = observable({
tick: 1,
increment: action.bound(function() {
this.tick++ // bound 'this'
})
})

setInterval(ticker.increment, 1000)
```

_Note: don't use *action.bind* with arrow functions; arrow functions are already bound and cannot be rebound._
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: should sayaction.bound

132 changes: 98 additions & 34 deletions docs/refguide/api.md
Original file line number Diff line number Diff line change
@@ -1,46 +1,129 @@
# MobX Api Reference

Applies to MobX 3 and higher. For MobX 2, the old documentation is still available on [githib](https://github.com/mobxjs/mobx/blob/7c9e7c86e0c6ead141bb0539d33143d0e1f576dd/docs/refguide/api.md)

# Core API

_The most important MobX api's. Understanding `observable`, `computed`, `reactions` and `actions` is enough to master MobX and use it in your applications!_

## Creating observables

### `observable`

### `observable(value)`
Usage:
* `observable(value)`
* `@observable classProperty = value`

Observable values can be JS primitives, references, plain objects, class instances, arrays and maps.
`observable(value)` is a convenience overload, that always tries to create the best matching observable types.
You can also directly create the desired observable type, see below.

The following conversion rules are applied, but can be fine-tuned by using *modifiers*. See below.

1. If *value* is wrapped in the *modifier* `asMap`: a new [Observable Map](map.md) will be returned. Observable maps are very useful if you don't want to react just to the change of a specific entry, but also to the addition or removal of entries.
1. If *value* is an wrapped is an instance of an [ES6 Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map): a new [Observable Map](map.md) will be returned. Observable maps are very useful if you don't want to react just to the change of a specific entry, but also to the addition or removal of entries.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is an wrapped is an instance

should probably be

is a wrapped instance

1. If *value* is an array, a new [Observable Array](array.md) will be returned.
1. If *value* is an object *without* prototype, all its current properties will be made observable. See [Observable Object](object.md)
1. If *value* is an object *without* prototype, the object will be cloned and all its current properties will be made observable. See [Observable Object](object.md)
1. If *value* is an object *with* a prototype, a JavaScript primitive or function, a [Boxed Observable](boxed.md) will be returned. MobX will not make objects with a prototype automatically observable; as that is the responsibility of it's constructor function. Use `extendObservable` in the constructor, or `@observable` in it's class definition instead.

These rules might seem complicated at first sight, but you will notice that in practice they are very intuitive to work with.
Some notes:
* To create dynamically keyed objects use the `asMap` modifier! Only initially existing properties on an object will be made observable, although new ones can be added using `extendObservable`.
* To create dynamically keyed objects always use maps! Only initially existing properties on an object will be made observable, although new ones can be added using `extendObservable`.
* To use the `@observable` decorator, make sure that [decorators are enabled](http://mobxjs.github.io/mobx/refguide/observable-decorator.html) in your transpiler (babel or typescript).
* By default making a data structure observable is *infective*; that means that `observable` is applied automatically to any value that is contained by the data structure, or will be contained by the data structure in the future. This behavior can be changed by using *modifiers*.
* By default making a data structure observable is *infective*; that means that `observable` is applied automatically to any value that is contained by the data structure, or will be contained by the data structure in the future. This behavior can be changed by using *modifiers* or *shallow*.

[«`observable`»](observable.md) — [«`@observable`»](observable-decorator.md)

### `extendObservable`
Usage: `extendObservable(target, propertyMap)`. For each key/value pair in `propertyMap` a (new) observable property will be introduced on the target object.
### `@observable property = value`

`observable` can also be used as property decorator. It requires [decorators to be enabled](../best/decorators.md) and is syntactic
sugar for `extendObservable(this, { property: value })`.

[«`details`»](observable-decorator.md)

### `observable.box(value)` & `observable.shallowBox(value)`

Creates an observable _box_ that stores an observable reference to a value. Use `get()` to get the current value of the box, and `set()` to update it.
This is the foundation on which all other observables are built, but in practice you will use it rarely.
Normal boxes will automatically try to turn any new value into an observable if it isn't already. Use `shallowBox` to disable this behavior.

[«`details`»](boxed.md)

### `observable.object(value)` & `observable.shallowObject(value)`

Creates a clone of the provided object and makes all it's properties observable.
By default any values in those properties will be made observable as well, but when using `shallowObject` only the properties will be made into observable
references, but the values will be untouched. (This holds also for any values assigned in the future)

[«`details`»](object.md)

### `observable.array(value)` & `observable.shallowArray(value)`

Creates a new observable array based on the provided value. Use `shallowArray` if the values in the array should not be turned into observables.

[«`details`»](array.md)

### `observable.map(value)` & `observable.shallowMap(value)`

Creates a new observable map based on the provided value. Use `shallowMap` if the values in the array should not be turned into observables.
Use `map` whenever you want to create a dynamically keyed collections and the addition / removal of keys needs to be observed.
Note that only string keys are supported.

[«`details`»](map.md)

### `extendObservable` & `extendShallowObservable`
Usage: `extendObservable(target, ...propertyMaps)`. For each key/value pair in each `propertyMap` a (new) observable property will be introduced on the target object.
This can be used in constructor functions to introduce observable properties without using decorators.
If a value of the `propertyMap` is an argumentless function, a *computed* property will be introduced.
If a value of the `propertyMap` is a getter function, a *computed* property will be introduced.

Use `extendShallowObservable` if the new properties should not be infective (that is; newly assigned values should not be turned into observables automatically).
Note that `extendObservable` enhances existing objects, unlike `observable.object` which creates a new object.

[«details»](extend-observable.md)

### Modifiers

Modifiers can be used decorator or in combination with `extendObservable` and `observable.object` to change the autoconversion rules for specific properties.

The following modifiers are available:

* `observable.deep`: This is the default modifier, used by any observable. It converts any assigned, non-primitive value into an observable if it isn't one yet.
* `observable.ref`: Disables automatic observable conversion, just creates an observable reference instead.
* `observable.shallow`: Can only used in combination with collections. Turns any assigned collection into an collection, which is shallowly observable (instead of deep). In other words; the values inside the collection won't become observables automatically.
* `computed`: Creates a derived property, see [`computed`](computed-decorator.md)
* `action`: Creates an action, see [`action`](action.md)

Modifiers can be used as decorator:

```javascript
class TaskStore {
@observable.shallow tasks = []
}
```

Or as property modifier in combination with `observable.object` / `observable.extendObservable`.
Note that modifiers always 'stick' to the property. So they will remain in effect even if a new value is assigned.

```javascript
const taskStore = observable({
tasks: observable.shallow([])
})
```

[«details»](modifiers.md)


## Computed values

Usage:
* `computed(() => expression)`
* `computed(() => expression, (newValue) => void)`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

computed(() => expression, (newValue) => void) isn't explained in the computed docs, only computed(() => expression) and computed(() => expression, options) are explained

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, does the setter behave as an action?

* `computed(() => expression, options)`
* `@computed get classProperty() { return expression; }`
* `@computed.struct get classProperty() { return expression; }`

Creates a computed property. The `expression` should not have side effects but return a value.
The expression will automatically be re-evaluated if any observables it uses changes, but only if it is in use by some *reaction*.

[«details»](computed-decorator.md)

## Actions
Expand All @@ -64,7 +147,8 @@ Usage:

For one-time-actions `runInAction(name?, fn, scope?)` can be used, which is sugar for `action(name, fn, scope)()`.

## Reactions
## Reactions & Derivations

*Computed values* are **values** that react automatically to state changes.
*Reactions* are **side effects** that react automatically to state changes.
Reactions _can_ be used to ensure that a certain side effect (mainly I/O) is automatically executed when relevant state changes, like logging, network requests etc.
Expand Down Expand Up @@ -105,16 +189,11 @@ It takes two function, the first one is tracked and returns data that is used as
Unlike `autorun` the side effect won't be run initially, and any observables that are accessed while executing the side effect will not be tracked.
The side effect can be debounced, just like `autorunAsync`. [«details»](reaction.md)

## Modifiers for `observable`

By default `observable` is applied recursively and to values that are assigned in the future as well.
Modifiers can be used to influence how `observable` treats specific values.
* `asMap`: This is the most important modifier. Instead of creating an object with observable properties, an *Observable Map* is created instead. The main difference with observable objects is that the addition and removal of properties can be easily observed. Use `asMap` if you want a map like data structure where the keys will change over time.
* `asFlat`: Will not apply `observable` recursively. The passed object / collection itself will be observable, but the values in it won't. This disables the possibility to deeply observe objects.
* `asReference`: Use the passed in value verbatim, just create an observable reference to the object.
* `asStructure`: When new values are assigned, ignore the new value if it structurally equal to the previous value.

[«details»](modifiers.md)
### `expr`
Usage: `expr(() => someExpression)`. Just a shorthand for `computed(() => someExpression).get()`.
`expr` is useful in some rare cases to optimize another computed function or reaction.
In general it is simpler and better to just split the function in multiple smaller computed's to achieve the same effect.
[«details»](expr.md)

------

Expand Down Expand Up @@ -281,21 +360,6 @@ Resets MobX internal global state. MobX by defaults fails fast if an exception o
This function resets MobX to the zero state. Existing `spy` listeners and the current value of strictMode will be preserved though.


# Functions that might get deprecated

### `map`
*Will probably by deprecated, use `observable(asMap())` instead*. Usage: `map()`, `map(keyValueObject)`, `map(entries)`.
Returns an observable, largely ES6 compliant [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) data structure.
This is useful if you want to store data based on string keys.
For the full api of the returned `ObservableMap` see *Observable maps*.
[«details»](map.md)

### `expr`
Usage: `expr(() => someExpression)`. Just a shorthand for `computed(() => someExpression).get()`.
`expr` is useful in some rare cases to optimize another computed function or reaction.
In general it is simpler and better to just split the function in multiple smaller computed's to achieve the same effect.
[«details»](expr.md)




16 changes: 12 additions & 4 deletions docs/refguide/array.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## Observable Arrays

Similar to objects, arrays can be made observable using `observable`.
Similar to objects, arrays can be made observable using `observable.array(values?)` or by passing an array to `observable`.
This works recursively as well, so all (future) values of the array will also be observable.

```javascript
Expand Down Expand Up @@ -30,8 +30,7 @@ todos.shift();
// Prints: 'Remaining: Make coffee, Take a nap'
```

Due to limitations of native arrays in ES5 (`array.observe` is only available in ES7, and arrays cannot be extend),
`observable` will instrument a clone of the provided array instead of the original one.
Due to limitations of native arrays in ES5 `observable.array` will create a faux-array (array-like object) instead of a real array.
In practice, these arrays work just as fine as native arrays and all native methods are supported, including index assignments, up-to and including the length of the array.

Bear in mind however that `Array.isArray(observable([]))` will yield `false`, so whenever you need to pass an observable array to an external library,
Expand All @@ -42,13 +41,22 @@ Unlike the built-in implementation of the functions `sort` and `reverse`, observ

Besides all built-in functions, the following goodies are available as well on observable arrays:

* `intercept(interceptor)`. Can be used to intercept any change before it is applied to the array. See [observe & intercept](observe.md)
* `intercept(interceptor)`. Can be used to intercept any change before it is applied to the array. See [observe & intercept](observe.md)
* `observe(listener, fireImmediately? = false)` Listen to changes in this array. The callback will receive arguments that express an array splice or array change, conforming to [ES7 proposal](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/observe). It returns a disposer function to stop the listener.
* `clear()` Remove all current entries from the array.
* `replace(newItems)` Replaces all existing entries in the array with new ones.
* `find(predicate: (item, index, array) => boolean, thisArg?, fromIndex?)` Basically the same as the ES7 `Array.find` proposal, except for the additional `fromIndex` parameter.
* `remove(value)` Remove a single item by value from the array. Returns `true` if the item was found and removed.
* `peek()` Returns an array with all the values which can safely be passed to other libraries, similar to `slice()`.

In contrast to `slice`, `peek` doesn't create a defensive copy. Use this in performance critical applications if you know for sure that you use the array in a read-only manner.
In performance critical sections it is recommended to use a flat observable array as well.

## `observable.shallowArray(values)`

Any values assigned to an observable array will be default passed through [`observable`](observable.md) to make them observable.
Create a shallow array to disable this behavior and store are values as-is. See also [modifiers](modifiers.md) for more details on this mechanism.

## Name argument

Both `observable.array` and `observable.shallowArray` take a second parameter which is used as debug name in for example `spy` or the MobX dev tools.
Loading