Skip to content
This repository has been archived by the owner on Dec 31, 2020. It is now read-only.

Version 2.0 #212

Merged
merged 34 commits into from
Apr 6, 2020
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
62c8620
Schedule uncommitted reaction cleanup (#121)
RoystonS Apr 28, 2019
fbbe89a
2.0.0-alpha.0
danielkcz Apr 28, 2019
17d1511
2.0.0-alpha.1
danielkcz Apr 28, 2019
5abebcf
Merge branch 1.4.0 into next
danielkcz May 27, 2019
5713939
2.0.0-alpha.2
danielkcz May 27, 2019
bdaf5be
Merge branch 'master' into next
danielkcz Aug 23, 2019
cd3a3ae
Upgrade to React 16.9
danielkcz Aug 23, 2019
57f00cc
Add dedup script and run it
danielkcz Aug 23, 2019
cceb90f
Remove deprecated hooks
danielkcz Aug 23, 2019
4e02e92
Increase size limit
danielkcz Aug 23, 2019
3178015
Remove note about Next version
danielkcz Aug 23, 2019
ffc1b56
Remove unused productionMode util
danielkcz Aug 23, 2019
531acbb
Improve readme
danielkcz Aug 23, 2019
5f2d035
Pin dependencies
renovate-bot Aug 23, 2019
cb13811
Merge branch 'master' into next
FredyC Sep 8, 2019
cffeb40
Merge master properly
danielkcz Sep 8, 2019
4282272
Ignore build cache files
danielkcz Sep 8, 2019
bd54c8c
Revert removal of tsdx dep
danielkcz Sep 8, 2019
a218f5f
Remove .browserlistrc
danielkcz Sep 8, 2019
bfe0a0c
Apply fix for #226
danielkcz Oct 15, 2019
fc35eb0
Upgrade to React 16.10
danielkcz Oct 15, 2019
0f63639
2.0.0-alpha.3
danielkcz Oct 15, 2019
77239f1
Merge branch 'master' into next
danielkcz Oct 15, 2019
f4a0fa2
Bundling need to use build tsconfig
danielkcz Nov 10, 2019
5eeea38
2.0.0-alpha.4
danielkcz Nov 10, 2019
f08562a
Remove object destructuring from optimizeForReactDOM/Native (#240)
Tarpsvo Nov 12, 2019
dfcbb67
Preserve generics when using `observer` (#244)
Kukkimonsuta Feb 16, 2020
072b30c
Merge branch 'master' into next
danielkcz Feb 16, 2020
2da0284
Remove copy of UMD bundle
danielkcz Feb 16, 2020
c012a66
2.0.0-alpha.5
danielkcz Feb 16, 2020
5a5d106
Batched updates are mandatory
danielkcz Apr 6, 2020
d060318
Replace .npmignore with files field
danielkcz Apr 6, 2020
0ebb074
Fix tests
danielkcz Apr 6, 2020
5655419
Increase size limit
danielkcz Apr 6, 2020
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
6 changes: 0 additions & 6 deletions .browserlistrc

This file was deleted.

2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
dist/
coverage/

.rpt2_cache
.rts2_cache_*
.DS_Store

npm-debug.log*
Expand Down
10 changes: 8 additions & 2 deletions .size-limit.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
[
{
"path": "dist/index.js",
"limit": "4 KB",
"path": "dist/mobxreactlite.umd.production.min.js",
"limit": "1.7 KB",
"webpack": false,
"running": false
},
{
"path": "dist/mobxreactlite.cjs.production.min.js",
"limit": "1.7 KB",
"webpack": false,
"running": false
}
Expand Down
181 changes: 30 additions & 151 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,52 +1,63 @@
# mobx-react-lite <!-- omit in toc -->
# mobx-react-lite

[![CircleCI](https://circleci.com/gh/mobxjs/mobx-react-lite.svg?style=svg)](https://circleci.com/gh/mobxjs/mobx-react-lite)[![Coverage Status](https://coveralls.io/repos/github/mobxjs/mobx-react-lite/badge.svg)](https://coveralls.io/github/mobxjs/mobx-react-lite)
[![CircleCI](https://circleci.com/gh/mobxjs/mobx-react-lite.svg?style=svg)](https://circleci.com/gh/mobxjs/mobx-react-lite)[![Coverage Status](https://coveralls.io/repos/github/mobxjs/mobx-react-lite/badge.svg)](https://coveralls.io/github/mobxjs/mobx-react-lite)[![NPM downloads](https://img.shields.io/npm/dm/mobx-react-lite.svg?style=flat)](https://npmjs.com/package/mobx-react-lite)[![Minzipped size](https://img.shields.io/bundlephobia/minzip/mobx-react-lite.svg)](https://bundlephobia.com/result?p=mobx-react-lite)

[![TypeScript](https://badges.frapsoft.com/typescript/code/typescript.svg?v=101)](https://github.com/ellerbrock/typescript-badges/)[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier)

[![Join the chat at https://gitter.im/mobxjs/mobx](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/mobxjs/mobx?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

This is a next iteration of [mobx-react](https://github.com/mobxjs/mobx-react) coming from introducing React hooks which simplifies a lot of internal workings of this package.
[![NPM](https://nodei.co/npm/mobx-react-lite.png)](https://www.npmjs.com/package/mobx-react-lite)

**You need React version 16.8.0 and above**

Class based components **are not supported** except using `<Observer>` directly in class `render` method. If you want to transition existing projects from classes to hooks (as most of us do), you can use this package alongside the [mobx-react](https://github.com/mobxjs/mobx-react) just fine. The only conflict point is about the `observer` HOC. Subscribe [to this issue](https://github.com/mobxjs/mobx-react/issues/640) for a proper migration guide.
This is a lighter version of [mobx-react](https://github.com/mobxjs/mobx-react) which supports React **functional components only** and as such makes the library slightly faster and smaller (_only 1.5kB gzipped_). In fact `mobx-react@6` has this library as a dependency and builds on top of it.

[![NPM](https://nodei.co/npm/mobx-react-lite.png)](https://www.npmjs.com/package/mobx-react-lite)
The library does not include any Provider/inject utilities as they can be fully replaced with [React Context](https://mobx-react.js.org/recipes-context). Check out [the migration guide](https://mobx-react.js.org/recipes-migration).

Project is written in TypeScript and provides type safety out of the box. No Flow Type support is planned at this moment, but feel free to contribute.
Class based components **are not supported** except using `<Observer>` directly in class `render` method. If you want to transition existing projects from classes to hooks, use [mobx-react 6+](https://github.com/mobxjs/mobx-react).

See more at [the libraries overview](https://mobx-react.js.org/libraries).

## User Guide 👉 https://mobx-react.js.org

The site contains various examples and recipes for using MobX in React world. Feel free to contribute. The API reference of this package follows 👇.

## API reference ⚒

> **`<Observer>{renderFn}</Observer>`** _([user guide](https://mobx-react.js.org/observer-component))_
### **`<Observer>{renderFn}</Observer>`** _([user guide](https://mobx-react.js.org/observer-component))_

Is a React component, which applies observer to an anonymous region in your component.

> **`observer<P>(baseComponent: FunctionComponent<P>, options?: IObserverOptions): FunctionComponent<P>`** _([user guide](https://mobx-react.js.org/observer-hoc))_
### **`observer<P>(baseComponent: FunctionComponent<P>, options?: IObserverOptions): FunctionComponent<P>`** _([user guide](https://mobx-react.js.org/observer-hoc))_

```ts
interface IObserverOptions {
// Pass true to use React.forwardRef over the inner component. It's false by the default.
forwardRef?: boolean
// Pass true to wrap the inner component with React.forwardRef.
// It's false by the default.
forwardRef?: boolean;
}
```

> **`useObserver<T>(fn: () => T, baseComponentName = "observed", options?: IUseObserverOptions): T`** _([user guide](https://mobx-react.js.org/observer-hook))_
The observer converts a component into a reactive component, which tracks which observables are used automatically and re-renders the component when one of these values changes.

### **`useObserver<T>(fn: () => T, baseComponentName = "observed", options?: IUseObserverOptions): T`** _([user guide](https://mobx-react.js.org/observer-hook))_

```ts
interface IUseObserverOptions {
// optional custom hook that should make a component re-render (or not) upon changes
useForceUpdate: () => () => void
// optional custom hook that should make a component re-render (or not) upon changes
useForceUpdate: () => () => void;
}
```

**`useLocalStore<T, S>(initializer: () => T, source?: S): T`** _([user guide](https://mobx-react.js.org/state-local))_
It allows you to use an observer like behaviour, but still allowing you to optimize the component in any way you want (e.g. using memo with a custom areEqual, using forwardRef, etc.) and to declare exactly the part that is observed (the render phase).

### **`useLocalStore<T, S>(initializer: () => T, source?: S): T`** _([user guide](https://mobx-react.js.org/state-local))_

**`useAsObservableSource<T>(source: T): T`** _([user guide](https://mobx-react.js.org/state-outsourcing))_
Local observable state can be introduced by using the useLocalStore hook, that runs its initializer function once to create an observable store and keeps it around for a lifetime of a component.

## React Strict mode ☄
### **`useAsObservableSource<T>(source: T): T`** _([user guide](https://mobx-react.js.org/state-outsourcing))_

Feel free to try out `mobx-react-lite@next` which is based on latest 1.x, but contains experimental support for handling Concurrent mode in React properly.
The useAsObservableSource hook can be used to turn any set of values into an observable object that has a stable reference (the same object is returned every time from the hook).

## Optimize rendering

Expand All @@ -69,138 +80,6 @@ Import one of these before any React rendering is happening, typically `index.js
Above imports are for a convenience. If you for some reason have customized version of batched updates, you can do the following instead.

```js
import { optimizeScheduler } from "mobx-react-lite"
optimizeScheduler(customBatchedUpdates)
```

## Deprecation notice ⚠

Following utilities are still available in the package, but they are deprecated and will be removed in the next major version (2.x). As such, they are not mentioned in the user guide and it's not recommend to continue using these.

---

### `useObservable<T>(initialValue: T): T`

> **Use the `useLocalStore` instead** ([user guide](https://mobx-react.js.org/state-local))

React hook that allows creating observable object within a component body and keeps track of it over renders. Gets all the benefits from [observable objects](https://mobx.js.org/refguide/object.html) including computed properties and methods. You can also use arrays, Map and Set.

Warning: With current implementation you also need to wrap your component to `observer`. It's also possible to have `useObserver` only in case you are not expecting rerender of the whole component.

```tsx
import { observer, useObservable, useObserver } from "mobx-react-lite"

const TodoList = () => {
const todos = useObservable(new Map<string, boolean>())
const todoRef = React.useRef()
const addTodo = React.useCallback(() => {
todos.set(todoRef.current.value, false)
todoRef.current.value = ""
}, [])
const toggleTodo = React.useCallback((todo: string) => {
todos.set(todo, !todos.get(todo))
}, [])

return useObserver(() => (
<div>
{Array.from(todos).map(([todo, done]) => (
<div onClick={() => toggleTodo(todo)} key={todo}>
{todo}
{done ? " ✔" : " ⏲"}
</div>
))}
<input ref={todoRef} />
<button onClick={addTodo}>Add todo</button>
</div>
))
}
```

#### Lazy initialization

Lazy initialization (similar to `React.useState`) is not available. In most cases your observable state should be a plain object which is cheap to create. With `useObserver` the component won't even rerender and state won't be recreated. In case you really want a more complex state or you need to use `observer`, it's very simple to use MobX directly.

```tsx
import { observer } from "mobx-react-lite"
import { observable } from "mobx"
import { useState } from "react"

const WithComplexState = observer(() => {
const [complexState] = useState(() => observable(new HeavyState()))
if (complexState.loading) {
return <Loading />
}
return <div>{complexState.heavyName}</div>
})
```

[![Edit TodoList](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/jzj48v2xry?module=%2Fsrc%2FTodoList.tsx)

Note that if you want to track a single scalar value (string, number, boolean), you would need [a boxed value](https://mobx.js.org/refguide/boxed.html) which is not recognized by `useObservable`. However, we recommend to just `useState` instead which gives you almost same result (with slightly different API).

### `useComputed(func: () => T, inputs: ReadonlyArray<any> = []): T`

> **Use the `useLocalStore` instead** ([user guide](https://mobx-react.js.org/state-local))

Another React hook that simplifies computational logic. It's just a tiny wrapper around [MobX computed](https://mobx.js.org/refguide/computed-decorator.html#-computed-expression-as-function) function that runs computation whenever observable values change. In conjuction with `observer` the component will rerender based on such a change.

```tsx
const Calculator = observer(({ hasExploded }: { hasExploded: boolean }) => {
const inputRef = React.useRef()
const inputs = useObservable([1, 3, 5])
const result = useComputed(
() => (hasExploded ? "💣" : inputs.reduce(multiply, 1) * Number(!hasExploded)),
[hasExploded]
)

return (
<div>
<input ref={inputRef} />
<button onClick={() => inputs.push(parseInt(inputRef.current.value) | 1)}>
Multiply
</button>
<div>
{inputs.join(" * ")} = {result}
</div>
</div>
)
})
```

Notice that since the computation depends on non-observable value, it has to be passed as a second argument to `useComputed`. There is [React `useMemo`](https://reactjs.org/docs/hooks-reference.html#usememo) behind the scenes and all rules applies here as well except you don't need to specify dependency on observable values.

[![Edit Calculator](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/jzj48v2xry?module=%2Fsrc%2FCalculator.tsx)

### `useDisposable<D extends TDisposable>(disposerGenerator: () => D, inputs: ReadonlyArray<any> = []): D`

> **Use the `React.useEffect` instead** ([user guide](https://mobx-react.js.org/recipes-effects))

The disposable is any kind of function that returns another function to be called on a component unmount to clean up used resources. Use MobX related functions like [`reaction`](https://mobx.js.org/refguide/reaction.html), [`autorun`](https://mobx.js.org/refguide/autorun.html), [`when`](https://mobx.js.org/refguide/when.html), [`observe`](https://mobx.js.org/refguide/observe.html), or anything else that returns a disposer.
Returns the generated disposer for early disposal.

Example (TypeScript):

```typescript
import { reaction } from "mobx"
import { observer, useComputed, useDisposable } from "mobx-react-lite"

const Name = observer((props: { firstName: string; lastName: string }) => {
const fullName = useComputed(() => `${props.firstName} ${props.lastName}`, [
props.firstName,
props.lastName
])

// when the name changes then send this info to the server
useDisposable(() =>
reaction(
() => fullName,
() => {
// send this to some server
}
)
)

// render phase
return `Your full name is ${props.firstName} ${props.lastName}`
})
import { optimizeScheduler } from "mobx-react-lite";
optimizeScheduler(customBatchedUpdates);
```
4 changes: 2 additions & 2 deletions optimizeForReactDom.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
const { unstable_batchedUpdates } = require("react-dom")
require("./dist").optimizeScheduler(unstable_batchedUpdates)
const ReactDOM = require("react-dom")
require("./dist").optimizeScheduler(ReactDOM.unstable_batchedUpdates)
4 changes: 2 additions & 2 deletions optimizeForReactNative.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
const { unstable_batchedUpdates } = require("react-native")
require("./dist").optimizeScheduler(unstable_batchedUpdates)
const ReactNative = require("react-native")
require("./dist").optimizeScheduler(ReactNative.unstable_batchedUpdates)
24 changes: 10 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
{
"name": "mobx-react-lite",
"version": "1.5.0",
"version": "2.0.0-alpha.4",
"description": "Lightweight React bindings for MobX based on React 16.8 and Hooks",
"main": "dist/index.js",
"jsnext:main": "dist/index.module.js",
"module": "dist/index.module.js",
"react-native": "dist/native.js",
"typings": "dist/index.d.ts",
"types": "dist/index.d.ts",
"jsnext:main": "dist/mobxreactlite.esm.js",
"module": "dist/mobxreactlite.esm.js",
"unpkg": "dist/mobxreactlite.umd.production.min.js",
"react-native": "dist/mobxreactlite.esm.js",
"repository": {
"type": "git",
"url": "https://github.com/mobxjs/mobx-react-lite.git"
Expand All @@ -21,8 +22,10 @@
"size": "size-limit",
"coverage": "jest --coverage",
"prebuild": "rimraf dist",
"build": "node build-rollup.js",
"prepublishOnly": "yarn build"
"build": "yarn bundle",
"bundle": "tsdx build --name mobxReactLite --format=cjs,esm,umd --tsconfig tsconfig.build.json",
"prepublishOnly": "yarn build",
"dedup": "npx yarn-deduplicate -s fewer yarn.lock"
},
"author": "Daniel K.",
"license": "MIT",
Expand Down Expand Up @@ -56,16 +59,9 @@
"react-dom": "16.10.2",
"react-test-renderer": "16.10.2",
"rimraf": "3.0.0",
"rollup": "1.23.1",
"rollup-plugin-alias": "1.5.2",
"rollup-plugin-commonjs": "10.1.0",
"rollup-plugin-filesize": "6.2.0",
"rollup-plugin-node-resolve": "5.2.0",
"rollup-plugin-replace": "2.2.0",
"rollup-plugin-terser": "5.1.2",
"rollup-plugin-typescript2": "0.24.3",
"size-limit": "2.1.6",
"ts-jest": "24.1.0",
"tsdx": "^0.9.1",
"tslint": "5.20.0",
"tslint-config-prettier": "1.18.0",
"typescript": "3.6.4"
Expand Down
3 changes: 0 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import "./assertEnvironment"

export { useObservable } from "./useObservable"
export { useComputed } from "./useComputed"
export { useDisposable } from "./useDisposable"
export { isUsingStaticRendering, useStaticRendering } from "./staticRendering"
export { observer, IObserverOptions } from "./observer"
export { useObserver, ForceUpdateHook, IUseObserverOptions } from "./useObserver"
Expand Down
7 changes: 2 additions & 5 deletions src/printDebugValue.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { getDependencyTree, Reaction } from "mobx"

export function printDebugValue(v: React.MutableRefObject<Reaction | null>) {
if (!v.current) {
return "<unknown>"
}
return getDependencyTree(v.current)
export function printDebugValue(v: Reaction) {
return getDependencyTree(v)
}
Loading