Skip to content
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
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ This conceptual picture can be applied to the above example, or any other applic

## Getting started

To learn about the core concepts of MobX using a larger example, check out **[The gist of MobX](https://mobx.js.org/the-gist-of-mobx.html)** page, or take the **[10 minute interactive introduction to MobX and React](https://mobx.js.org/getting-started)**.
To learn about the core concepts of MobX using a larger example, check out **[The gist of MobX](the-gist-of-mobx.md)** page, or take the **[10 minute interactive introduction to MobX and React](https://mobx.js.org/getting-started)**.

The philosophy and benefits of the mental model provided by MobX are also described in great detail in the blog posts [UI as an afterthought](https://michel.codes/blogs/ui-as-an-afterthought) and [How to decouple state and UI (a.k.a. you don鈥檛 need componentWillMount)](https://hackernoon.com/how-to-decouple-state-and-ui-a-k-a-you-dont-need-componentwillmount-cc90b787aa37).

Expand Down
6 changes: 3 additions & 3 deletions docs/actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ For debugging purposes, we recommend to either name the wrapped function, or pas

<details id="actions-are-untracked"><summary>**Note:** actions are untracked<a href="#actions-are-untracked" class="tip-anchor"></a></summary>

Another feature of actions is that they are [untracked](api.md#untracked). When an action is called from inside a side effect or a computed value (very rare!), observables read by the action won't be counted towards the dependencies of the derivation
Another feature of actions is that they are [untracked](api.md#untracked). When an action is called from inside a side effect or a computed value (very rare!), observables read by the action won't be counted towards the dependencies of the derivation.

`makeAutoObservable`, `extendObservable` and `observable` use a special flavour of `action` called [`autoAction`](observable-state.md#autoAction),
that will determine at runtime if the function is a derivation or action.
Expand Down Expand Up @@ -235,7 +235,7 @@ class Parent {

constructor() {
makeObservable(this, {
arrowAction: action
arrowAction: action,
action: action,
boundAction: action.bound,
})
Expand Down Expand Up @@ -486,7 +486,7 @@ Usage:
- `flow.bound` _(annotation)_

The `flow.bound` annotation can be used to automatically bind a method to the correct instance, so that `this` is always correctly bound inside the function.
Similary to actions, flows can be bound by default using [`autoBind` option](#auto-bind).
Similarly to actions, flows can be bound by default using [`autoBind` option](#auto-bind).

## Cancelling flows {馃殌}

Expand Down
6 changes: 3 additions & 3 deletions docs/migrating-from-4-or-5.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ _鈿狅笍 **Warning**: Depending on factors like the size and complexity of your c
4. For babel users:
- If you are using Babel and have class-properties enabled, disable the legacy loose field support: `["@babel/plugin-proposal-class-properties", { "loose": false }]`
- (Optional) In MobX 6 decorators have become opt-in. If you no longer wish to use decorators, remove `plugin-proposal-decorators` from your babel configuration and dependencies. Check out the [Enabling decorators {馃殌}](enabling-decorators.md) section for more details.
5. For Typescript users:
5. For TypeScript users:
- Add the flag `"useDefineForClassFields": true` to your compiler config.
- (Optional) In MobX 6 decorators have become opt-in. If you no longer wish to use decorators, remove / disable the `experimentalDecorators` configuration from your TypeScript config. Check out the [Enabling decorators {馃殌}](enabling-decorators.md) section for more details.
6. The MobX default configuration has become more strict. We recommend to adopt the new defaults after completing the upgrade, check out the [Configuration {馃殌}](configuration.md) section. During migration, we recommend to configure MobX in the same way as it would be in v4/v5 out of the box: `import {configure} from "mobx"; configure({ enforceActions: "never" });`. After finishing the entire migration process and validating that your project works as expected, consider enabling the flags `computedRequiresReaction`, `reactionRequiresObservable` and `observableRequiresReaction` and `enforceActions: "observed"` to write more idiomatic MobX code.
Expand Down Expand Up @@ -75,6 +75,6 @@ pass along `props` to the superclass.

## Functions are auto-converted

Functions that become part of a deep observable structure are automatically converted to [`autoAction`](observable-state.md#autoAction) or to [`flow`](actions.html#using-flow-instead-of-async--await-) if it's a generator function. See [inference rules](observable-state.html#makeautoobservable) for details.
Functions that become part of a deep observable structure are automatically converted to [`autoAction`](observable-state.md#autoAction) or to [`flow`](actions.md#using-flow-instead-of-async--await-) if it's a generator function. See [inference rules](observable-state.md#makeautoobservable) for details.
This means that the original function reference is not preserved - in the same spirit as the original array/object/set/map reference is lost when converted to observable. [This can be surprising in some situations](https://github.com/mobxjs/mobx/issues/3616).
If this behavior is not desired use [`observable.shallow`](observable-state.html#available-annotations) / [`observable.ref`](observable-state.html#available-annotations) / [`false`](observable-state.html#available-annotations) / [`deep: flase`](observable-state.html#options-) to prevent the conversion process or make sure the function is already an `action` as shown in the issue.
If this behavior is not desired use [`observable.shallow`](observable-state.md#available-annotations) / [`observable.ref`](observable-state.md#available-annotations) / [`false`](observable-state.md#available-annotations) / [`deep: false`](observable-state.md#options-) to prevent the conversion process or make sure the function is already an `action` as shown in the issue.
8 changes: 4 additions & 4 deletions docs/observable-state.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ function createDoubler(value) {
```

Note that classes can leverage `makeAutoObservable` as well.
The difference in the examples just demonstrate how MobX can be applied to different programming styles.
The difference in the examples just demonstrates how MobX can be applied to different programming styles.

<!--observable-->

Expand Down Expand Up @@ -290,7 +290,7 @@ Note that it is possible to pass `{ proxy: false }` as an option to `observable`

| Annotation | Description |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `observable`<br/>`observable.deep` | Defines a trackable field that stores state. If possible, any value assigned to `observable` is automatically converted to (deep) `observable`, [`autoAction`](#autoAction) or `flow` based on it's type. Only `plain object`, `array`, `Map`, `Set`, `function`, `generator function` are convertible. Class instances and others are untouched. |
| `observable`<br/>`observable.deep` | Defines a trackable field that stores state. If possible, any value assigned to `observable` is automatically converted to (deep) `observable`, [`autoAction`](#autoAction) or `flow` based on its type. Only `plain object`, `array`, `Map`, `Set`, `function`, `generator function` are convertible. Class instances and others are untouched. |
| `observable.ref` | Like `observable`, but only reassignments will be tracked. The assigned values are completely ignored and will NOT be automatically converted to `observable`/[`autoAction`](#autoAction)/`flow`. For example, use this if you intend to store immutable data in an observable field. |
| `observable.shallow` | Like `observable.ref` but for collections. Any collection assigned will be made observable, but the contents of the collection itself won't become observable. |
| `observable.struct` | Like `observable`, except that any assigned value that is structurally equal to the current value will be ignored. |
Expand Down Expand Up @@ -328,8 +328,8 @@ Note that it is possible to pass `{ proxy: false }` as an option to `observable`

The above APIs take an optional `options` argument which is an object that supports the following options:

- **`autoBind: true`** uses `action.bound`/`flow.bound` by default, rather than `action`/`flow`. Does not affect explicitely annotated members.
- **`deep: false`** uses `observable.ref` by default, rather than `observable`. Does not affect explicitely annotated members.
- **`autoBind: true`** uses `action.bound`/`flow.bound` by default, rather than `action`/`flow`. Does not affect explicitly annotated members.
- **`deep: false`** uses `observable.ref` by default, rather than `observable`. Does not affect explicitly annotated members.
- **`name: <string>`** gives the object a debug name that is printed in error messages and reflection APIs.
- **`proxy: false`** forces `observable(thing)` to use non-[**proxy**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) implementation. This is a good option if the shape of the object will not change over time, as non-proxied objects are easier to debug and faster. This option is **not** available for `make(Auto)Observable`, see [avoiding proxies](#avoid-proxies).

Expand Down
4 changes: 2 additions & 2 deletions docs/react-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ If `observer` is used in server side rendering context; make sure to call `enabl

<details id="react-vs-lite"><summary>**Note:** mobx-react vs. mobx-react-lite<a href="#react-vs-lite" class="tip-anchor"></a></summary>
In this documentation we used `mobx-react-lite` as default.
[mobx-react](https://github.com/mobxjs/mobx-react/) is it's big brother, which uses `mobx-react-lite` under the hood.
[mobx-react](https://github.com/mobxjs/mobx-react/) is its big brother, which uses `mobx-react-lite` under the hood.
It offers a few more features which are typically not needed anymore in greenfield projects. The additional things offered by mobx-react:

1. Support for React class components.
Expand All @@ -336,7 +336,7 @@ Briefly, you can wrap class-based components in `observer` just like
you can wrap function components:

```javascript
import React from "React"
import React from "react"

const TimerView = observer(
class TimerView extends React.Component {
Expand Down
4 changes: 2 additions & 2 deletions docs/subclassing.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class Child extends Parent {
computed: override,
// new
childObservable: observable,
childArrowAction: action
childArrowAction: action,
childAction: action,
childActionBound: action.bound,
childComputed: computed,
Expand All @@ -84,7 +84,7 @@ class Child extends Parent {
1. Extending builtins (`ObservableMap`, `ObservableArray`, etc) is not supported.
1. You can't provide different options to `makeObservable` in subclass.
1. You can't mix annotations/decorators in single inheritance chain.
1. [All other limitations apply as well](observable-state.html#limitations)
1. [All other limitations apply as well](observable-state.md#limitations)

### `TypeError: Cannot redefine property`

Expand Down
9 changes: 4 additions & 5 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
const buildConfig = require("./jest.base.config")

module.exports = buildConfig(__dirname, {
module.exports = {
coverageDirectory: "<rootDir>/coverage/",
coverageReporters: ["lcov", "text"],
projects: ["<rootDir>/packages/*/jest.config.js", "<rootDir>/packages/*/jest.config-*.js"]
// collectCoverageFrom: ["<rootDir>/packages/*/src/**/*.{ts,tsx}"]
})
}
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"packages/*"
],
"resolutions": {
"jest": "^29.5.0",
"jest": "^30.3.0",
"typescript": "^5.9.2",
"recast": "^0.23.1"
},
Expand Down Expand Up @@ -36,7 +36,7 @@
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^16.1.0",
"@types/jest": "^26.0.15",
"@types/jest": "^30.0.0",
"@types/node": "18",
"@types/prop-types": "^15.5.2",
"@types/react": "^18.0.0",
Expand All @@ -50,8 +50,8 @@
"husky": "^4.2.5",
"import-size": "^1.0.2",
"iterall": "^1.3.0",
"jest": "^29.5.0",
"jest-environment-jsdom": "^29.5.0",
"jest": "^30.3.0",
"jest-environment-jsdom": "^30.3.0",
"jest-mock-console": "^1.0.1",
"lerna": "^3.22.1",
"lint-staged": "^10.1.7",
Expand All @@ -66,7 +66,7 @@
"react-test-renderer": "^18.0.0",
"serializr": "^2.0.3",
"tape": "^5.0.1",
"ts-jest": "^29.0.5",
"ts-jest": "^29.4.6",
"tsdx": "^0.14.1",
"typescript": "^5.9.2"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing

exports[`changing state in render should fail 1`] = `
<div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing

exports[`printDebugValue 1`] = `
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing

exports[`base useAsObservableSource should work with <Observer> 1`] = `
[MockFunction] {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing

exports[`base useLocalStore should work 1`] = `
[MockFunction] {
Expand Down
6 changes: 3 additions & 3 deletions packages/mobx-react-lite/__tests__/enforceActions.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ describe("enforcing actions", () => {
})

render(<Parent />)
expect(consoleWarnMock).not.toBeCalled()
expect(consoleWarnMock).not.toHaveBeenCalled()
})

it("'observed' should work", () => {
Expand All @@ -50,7 +50,7 @@ describe("enforcing actions", () => {
})

render(<Parent />)
expect(consoleWarnMock).toBeCalledTimes(1)
expect(consoleWarnMock).toHaveBeenCalledTimes(1)
})

it("'always' should work", () => {
Expand All @@ -69,6 +69,6 @@ describe("enforcing actions", () => {
})

render(<Parent />)
expect(consoleWarnMock).toBeCalledTimes(1)
expect(consoleWarnMock).toHaveBeenCalledTimes(1)
})
})
16 changes: 8 additions & 8 deletions packages/mobx-react-lite/__tests__/observer.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -996,32 +996,32 @@ it("dependencies should not become temporarily unobserved", async () => {

expect(computed).toBe(1)
expect(renders).toBe(1)
expect(doubleDisposed).toBeCalledTimes(0)
expect(doubleDisposed).toHaveBeenCalledTimes(0)

store.inc()
expect(computed).toBe(2) // change propagated
expect(renders).toBe(1) // but not yet rendered
expect(doubleDisposed).toBeCalledTimes(0) // if we dispose to early, this fails!
expect(doubleDisposed).toHaveBeenCalledTimes(0) // if we dispose to early, this fails!

// Bug: change the state, before the useEffect fires, can cause the reaction to be disposed
mobx.reaction(() => store.x, reactionFired)
expect(reactionFired).toBeCalledTimes(0)
expect(reactionFired).toHaveBeenCalledTimes(0)
expect(computed).toBe(2) // Not 3!
expect(renders).toBe(1)
expect(doubleDisposed).toBeCalledTimes(0)
expect(doubleDisposed).toHaveBeenCalledTimes(0)

await runEffects()
expect(reactionFired).toBeCalledTimes(0)
expect(reactionFired).toHaveBeenCalledTimes(0)
expect(computed).toBe(2) // Not 3!
expect(renders).toBe(2)
expect(doubleDisposed).toBeCalledTimes(0)
expect(doubleDisposed).toHaveBeenCalledTimes(0)

r.unmount()
cleanups.filter(Boolean).forEach(f => f())
expect(reactionFired).toBeCalledTimes(0)
expect(reactionFired).toHaveBeenCalledTimes(0)
expect(computed).toBe(2)
expect(renders).toBe(2)
expect(doubleDisposed).toBeCalledTimes(1)
expect(doubleDisposed).toHaveBeenCalledTimes(1)
})

it.skip("Legacy context support", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ describe("enforcing actions", () => {
}
}
)
expect(onError).not.toBeCalled()
expect(onError).not.toHaveBeenCalled()
})
it("only when 'observed' should work", () => {
configure({ enforceActions: "observed" })
Expand All @@ -323,7 +323,7 @@ describe("enforcing actions", () => {
}
}
)
expect(onError).not.toBeCalled()
expect(onError).not.toHaveBeenCalled()
})
it("'always' should work", () => {
configure({ enforceActions: "always" })
Expand All @@ -343,6 +343,6 @@ describe("enforcing actions", () => {
}
}
)
expect(onError).not.toBeCalled()
expect(onError).not.toHaveBeenCalled()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ describe("enforcing actions", () => {
}
}
)
expect(onError).not.toBeCalled()
expect(onError).not.toHaveBeenCalled()
})
it("only when 'observed' should work", () => {
configure({ enforceActions: "observed" })
Expand All @@ -386,7 +386,7 @@ describe("enforcing actions", () => {
}
}
)
expect(onError).not.toBeCalled()
expect(onError).not.toHaveBeenCalled()
})
it("'always' should work", () => {
configure({ enforceActions: "always" })
Expand All @@ -406,7 +406,7 @@ describe("enforcing actions", () => {
}
}
)
expect(onError).not.toBeCalled()
expect(onError).not.toHaveBeenCalled()
})
})

Expand Down
12 changes: 6 additions & 6 deletions packages/mobx-react-lite/__tests__/useLocalObservable.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -468,8 +468,8 @@ describe("enforcing actions", () => {
}
)

expect(onError).not.toBeCalled()
expect(consoleWarnMock).not.toBeCalled()
expect(onError).not.toHaveBeenCalled()
expect(consoleWarnMock).not.toHaveBeenCalled()
})
it("only when 'observed' should work", () => {
mobx.configure({ enforceActions: "observed" })
Expand Down Expand Up @@ -504,8 +504,8 @@ describe("enforcing actions", () => {
}
)

expect(onError).not.toBeCalled()
expect(consoleWarnMock).not.toBeCalled()
expect(onError).not.toHaveBeenCalled()
expect(consoleWarnMock).not.toHaveBeenCalled()
})
it("'always' should work", () => {
mobx.configure({ enforceActions: "always" })
Expand Down Expand Up @@ -540,7 +540,7 @@ describe("enforcing actions", () => {
}
)

expect(onError).not.toBeCalled()
expect(consoleWarnMock).toBeCalledTimes(2)
expect(onError).not.toHaveBeenCalled()
expect(consoleWarnMock).toHaveBeenCalledTimes(2)
})
})
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing

exports[`computed properties react to props when using hooks 1`] = `
[MockFunction] {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing

exports[`#3492 should not cause warning by calling forceUpdate on uncommited components 1`] = `[MockFunction]`;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing

exports[`stateless component with forwardRef is reactive 1`] = `
<div>
Expand Down
Loading