mobx-react-lite
Advanced tools
Comparing version 1.4.0 to 1.4.1
@@ -284,3 +284,5 @@ (function (global, factory) { | ||
var _a = __read(React__default.useState(function () { return mobx.observable(current, {}, { deep: false }); }), 1), res = _a[0]; | ||
Object.assign(res, current); | ||
mobx.runInAction(function () { | ||
Object.assign(res, current); | ||
}); | ||
return res; | ||
@@ -297,7 +299,9 @@ } | ||
if (isPlainObject(local)) { | ||
Object.keys(local).forEach(function (key) { | ||
var value = local[key]; | ||
if (typeof value === "function") { | ||
local[key] = wrapInTransaction(value, local); | ||
} | ||
mobx.runInAction(function () { | ||
Object.keys(local).forEach(function (key) { | ||
var value = local[key]; | ||
if (typeof value === "function") { | ||
local[key] = wrapInTransaction(value, local); | ||
} | ||
}); | ||
}); | ||
@@ -304,0 +308,0 @@ } |
@@ -1,2 +0,2 @@ | ||
import { spy, observable, computed, getDependencyTree, Reaction, transaction } from 'mobx'; | ||
import { spy, observable, computed, getDependencyTree, Reaction, runInAction, transaction } from 'mobx'; | ||
import React, { useState, useRef, useMemo, useEffect, useCallback, useDebugValue, memo, forwardRef } from 'react'; | ||
@@ -279,3 +279,5 @@ | ||
var _a = __read(React.useState(function () { return observable(current, {}, { deep: false }); }), 1), res = _a[0]; | ||
Object.assign(res, current); | ||
runInAction(function () { | ||
Object.assign(res, current); | ||
}); | ||
return res; | ||
@@ -292,7 +294,9 @@ } | ||
if (isPlainObject(local)) { | ||
Object.keys(local).forEach(function (key) { | ||
var value = local[key]; | ||
if (typeof value === "function") { | ||
local[key] = wrapInTransaction(value, local); | ||
} | ||
runInAction(function () { | ||
Object.keys(local).forEach(function (key) { | ||
var value = local[key]; | ||
if (typeof value === "function") { | ||
local[key] = wrapInTransaction(value, local); | ||
} | ||
}); | ||
}); | ||
@@ -299,0 +303,0 @@ } |
@@ -284,3 +284,5 @@ (function (global, factory) { | ||
var _a = __read(React__default.useState(function () { return mobx.observable(current, {}, { deep: false }); }), 1), res = _a[0]; | ||
Object.assign(res, current); | ||
mobx.runInAction(function () { | ||
Object.assign(res, current); | ||
}); | ||
return res; | ||
@@ -297,7 +299,9 @@ } | ||
if (isPlainObject(local)) { | ||
Object.keys(local).forEach(function (key) { | ||
var value = local[key]; | ||
if (typeof value === "function") { | ||
local[key] = wrapInTransaction(value, local); | ||
} | ||
mobx.runInAction(function () { | ||
Object.keys(local).forEach(function (key) { | ||
var value = local[key]; | ||
if (typeof value === "function") { | ||
local[key] = wrapInTransaction(value, local); | ||
} | ||
}); | ||
}); | ||
@@ -304,0 +308,0 @@ } |
@@ -1,1 +0,1 @@ | ||
!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("mobx"),require("react")):"function"==typeof define&&define.amd?define(["exports","mobx","react"],r):r((e=e||self).mobxReactLite={},e.mobx,e.React)}(this,function(e,r,n){"use strict";var t="default"in n?n.default:n;if(!n.useState)throw new Error("mobx-react-lite requires React with Hooks support");if(!r.spy)throw new Error("mobx-react-lite requires mobx at least version 4 to be available");var o=function(){};var u=!1;function i(){return u}var c=function(){return(c=Object.assign||function(e){for(var r,n=1,t=arguments.length;n<t;n++)for(var o in r=arguments[n])Object.prototype.hasOwnProperty.call(r,o)&&(e[o]=r[o]);return e}).apply(this,arguments)};function f(e,r){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var t,o,u=n.call(e),i=[];try{for(;(void 0===r||r-- >0)&&!(t=u.next()).done;)i.push(t.value)}catch(e){o={error:e}}finally{try{t&&!t.done&&(n=u.return)&&n.call(u)}finally{if(o)throw o.error}}return i}function a(e){return e.current?r.getDependencyTree(e.current):"<unknown>"}var s=[];function l(){var e=f(n.useState(0),2)[1];return n.useCallback(function(){e(function(e){return e+1})},[])}var p={};function d(e,t,o){if(void 0===t&&(t="observed"),void 0===o&&(o=p),i())return e();var u=(o.useForceUpdate||l)(),c=n.useRef(null);c.current||(c.current=new r.Reaction("observer("+t+")",function(){u()}));var f,d,v=function(){c.current&&!c.current.isDisposed&&c.current.dispose()};if(n.useDebugValue(c,a),function(e){n.useEffect(function(){return e},s)}(function(){v()}),c.current.track(function(){try{f=e()}catch(e){d=e}}),d)throw v(),d;return f}var v={$$typeof:!0,render:!0,compare:!0,type:!0};function b(e){var r=e.children,n=e.render,t=r||n;return"function"!=typeof t?null:d(t)}function y(e,r,n,t,o){var u="children"===r?"render":"children",i="function"==typeof e[r],c="function"==typeof e[u];return i&&c?new Error("MobX Observer: Do not use children and render in the same time in`"+n):i||c?null:new Error("Invalid prop `"+o+"` of type `"+typeof e[r]+"` supplied to `"+n+"`, expected `function`.")}function m(e,n){if(!n||void 0!==e){var o=f(t.useState(function(){return r.observable(e,{},{deep:!1})}),1)[0];return Object.assign(o,e),o}}b.propTypes={children:y,render:y},b.displayName="Observer",e.Observer=b,e.isUsingStaticRendering=i,e.observer=function(e,r){if(i())return e;var t,o,u,f=c({forwardRef:!1},r),a=e.displayName||e.name,s=function(r,n){return d(function(){return e(r,n)},a)};return s.displayName=a,t=f.forwardRef?n.memo(n.forwardRef(s)):n.memo(s),o=e,u=t,Object.keys(o).forEach(function(e){o.hasOwnProperty(e)&&!v[e]&&Object.defineProperty(u,e,Object.getOwnPropertyDescriptor(o,e))}),t.displayName=a,t},e.useAsObservableSource=function(e){return m(e,!1)},e.useComputed=function(e,t){return void 0===t&&(t=[]),n.useMemo(function(){return r.computed(e)},t).get()},e.useDisposable=function(e,r){void 0===r&&(r=[]);var t=n.useRef(null),u=n.useRef(!1);function i(r){if(u.current)return o;if(!t.current){var n=e();if("function"!=typeof n){var i=new Error("generated disposer must be a function");return console.error(i),o}t.current=n}return function(){t.current&&(t.current(),t.current=null),r&&(u.current=!0)}}return n.useEffect(function(){return i(!1)},r),i(!0)},e.useForceUpdate=l,e.useLocalStore=function(e,n){var o=m(n,!0);return t.useState(function(){var n=r.observable(e(o));return function(e){if(!e||"object"!=typeof e)return!1;var r=Object.getPrototypeOf(e);return!r||r===Object.prototype}(n)&&Object.keys(n).forEach(function(e){var t,o,u=n[e];"function"==typeof u&&(n[e]=(t=u,o=n,function(){for(var e=[],n=0;n<arguments.length;n++)e[n]=arguments[n];return r.transaction(function(){return t.apply(o,e)})}))}),n})[0]},e.useObservable=function(e){var t=n.useRef(null);return t.current||(t.current=r.observable(e)),t.current},e.useObserver=d,e.useStaticRendering=function(e){u=e},Object.defineProperty(e,"__esModule",{value:!0})}); | ||
!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("mobx"),require("react")):"function"==typeof define&&define.amd?define(["exports","mobx","react"],r):r((e=e||self).mobxReactLite={},e.mobx,e.React)}(this,function(e,r,n){"use strict";var t="default"in n?n.default:n;if(!n.useState)throw new Error("mobx-react-lite requires React with Hooks support");if(!r.spy)throw new Error("mobx-react-lite requires mobx at least version 4 to be available");var o=function(){};var u=!1;function i(){return u}var c=function(){return(c=Object.assign||function(e){for(var r,n=1,t=arguments.length;n<t;n++)for(var o in r=arguments[n])Object.prototype.hasOwnProperty.call(r,o)&&(e[o]=r[o]);return e}).apply(this,arguments)};function f(e,r){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var t,o,u=n.call(e),i=[];try{for(;(void 0===r||r-- >0)&&!(t=u.next()).done;)i.push(t.value)}catch(e){o={error:e}}finally{try{t&&!t.done&&(n=u.return)&&n.call(u)}finally{if(o)throw o.error}}return i}function a(e){return e.current?r.getDependencyTree(e.current):"<unknown>"}var s=[];function l(){var e=f(n.useState(0),2)[1];return n.useCallback(function(){e(function(e){return e+1})},[])}var p={};function d(e,t,o){if(void 0===t&&(t="observed"),void 0===o&&(o=p),i())return e();var u=(o.useForceUpdate||l)(),c=n.useRef(null);c.current||(c.current=new r.Reaction("observer("+t+")",function(){u()}));var f,d,v=function(){c.current&&!c.current.isDisposed&&c.current.dispose()};if(n.useDebugValue(c,a),function(e){n.useEffect(function(){return e},s)}(function(){v()}),c.current.track(function(){try{f=e()}catch(e){d=e}}),d)throw v(),d;return f}var v={$$typeof:!0,render:!0,compare:!0,type:!0};function b(e){var r=e.children,n=e.render,t=r||n;return"function"!=typeof t?null:d(t)}function y(e,r,n,t,o){var u="children"===r?"render":"children",i="function"==typeof e[r],c="function"==typeof e[u];return i&&c?new Error("MobX Observer: Do not use children and render in the same time in`"+n):i||c?null:new Error("Invalid prop `"+o+"` of type `"+typeof e[r]+"` supplied to `"+n+"`, expected `function`.")}function m(e,n){if(!n||void 0!==e){var o=f(t.useState(function(){return r.observable(e,{},{deep:!1})}),1)[0];return r.runInAction(function(){Object.assign(o,e)}),o}}b.propTypes={children:y,render:y},b.displayName="Observer",e.Observer=b,e.isUsingStaticRendering=i,e.observer=function(e,r){if(i())return e;var t,o,u,f=c({forwardRef:!1},r),a=e.displayName||e.name,s=function(r,n){return d(function(){return e(r,n)},a)};return s.displayName=a,t=f.forwardRef?n.memo(n.forwardRef(s)):n.memo(s),o=e,u=t,Object.keys(o).forEach(function(e){o.hasOwnProperty(e)&&!v[e]&&Object.defineProperty(u,e,Object.getOwnPropertyDescriptor(o,e))}),t.displayName=a,t},e.useAsObservableSource=function(e){return m(e,!1)},e.useComputed=function(e,t){return void 0===t&&(t=[]),n.useMemo(function(){return r.computed(e)},t).get()},e.useDisposable=function(e,r){void 0===r&&(r=[]);var t=n.useRef(null),u=n.useRef(!1);function i(r){if(u.current)return o;if(!t.current){var n=e();if("function"!=typeof n){var i=new Error("generated disposer must be a function");return console.error(i),o}t.current=n}return function(){t.current&&(t.current(),t.current=null),r&&(u.current=!0)}}return n.useEffect(function(){return i(!1)},r),i(!0)},e.useForceUpdate=l,e.useLocalStore=function(e,n){var o=m(n,!0);return t.useState(function(){var n=r.observable(e(o));return function(e){if(!e||"object"!=typeof e)return!1;var r=Object.getPrototypeOf(e);return!r||r===Object.prototype}(n)&&r.runInAction(function(){Object.keys(n).forEach(function(e){var t,o,u=n[e];"function"==typeof u&&(n[e]=(t=u,o=n,function(){for(var e=[],n=0;n<arguments.length;n++)e[n]=arguments[n];return r.transaction(function(){return t.apply(o,e)})}))})}),n})[0]},e.useObservable=function(e){var t=n.useRef(null);return t.current||(t.current=r.observable(e)),t.current},e.useObserver=d,e.useStaticRendering=function(e){u=e},Object.defineProperty(e,"__esModule",{value:!0})}); |
@@ -1,2 +0,2 @@ | ||
import { spy, observable, computed, getDependencyTree, Reaction, transaction } from 'mobx'; | ||
import { spy, observable, computed, getDependencyTree, Reaction, runInAction, transaction } from 'mobx'; | ||
import React, { useState, useRef, useMemo, useEffect, useCallback, useDebugValue, memo, forwardRef } from 'react'; | ||
@@ -279,3 +279,5 @@ | ||
var _a = __read(React.useState(function () { return observable(current, {}, { deep: false }); }), 1), res = _a[0]; | ||
Object.assign(res, current); | ||
runInAction(function () { | ||
Object.assign(res, current); | ||
}); | ||
return res; | ||
@@ -292,7 +294,9 @@ } | ||
if (isPlainObject(local)) { | ||
Object.keys(local).forEach(function (key) { | ||
var value = local[key]; | ||
if (typeof value === "function") { | ||
local[key] = wrapInTransaction(value, local); | ||
} | ||
runInAction(function () { | ||
Object.keys(local).forEach(function (key) { | ||
var value = local[key]; | ||
if (typeof value === "function") { | ||
local[key] = wrapInTransaction(value, local); | ||
} | ||
}); | ||
}); | ||
@@ -299,0 +303,0 @@ } |
@@ -286,3 +286,5 @@ 'use strict'; | ||
var _a = __read(React__default.useState(function () { return mobx.observable(current, {}, { deep: false }); }), 1), res = _a[0]; | ||
Object.assign(res, current); | ||
mobx.runInAction(function () { | ||
Object.assign(res, current); | ||
}); | ||
return res; | ||
@@ -299,7 +301,9 @@ } | ||
if (isPlainObject(local)) { | ||
Object.keys(local).forEach(function (key) { | ||
var value = local[key]; | ||
if (typeof value === "function") { | ||
local[key] = wrapInTransaction(value, local); | ||
} | ||
mobx.runInAction(function () { | ||
Object.keys(local).forEach(function (key) { | ||
var value = local[key]; | ||
if (typeof value === "function") { | ||
local[key] = wrapInTransaction(value, local); | ||
} | ||
}); | ||
}); | ||
@@ -306,0 +310,0 @@ } |
{ | ||
"name": "mobx-react-lite", | ||
"version": "1.4.0", | ||
"version": "1.4.1", | ||
"description": "Lightweight React bindings for MobX based on React 16.8 and Hooks", | ||
@@ -31,3 +31,3 @@ "main": "dist/index.js", | ||
}, | ||
"homepage": "https://mobxjs.github.io/mobx", | ||
"homepage": "https://mobx-react.js.org", | ||
"peerDependencies": { | ||
@@ -38,32 +38,32 @@ "mobx": "^4.0.0 || ^5.0.0", | ||
"devDependencies": { | ||
"@types/jest": "24.0.13", | ||
"@types/node": "12.0.2", | ||
"@types/react": "16.8.18", | ||
"@testing-library/react": "8.0.4", | ||
"@testing-library/react-hooks": "1.0.4", | ||
"@types/jest": "24.0.15", | ||
"@types/node": "12.0.10", | ||
"@types/react": "16.8.22", | ||
"@types/react-dom": "16.8.4", | ||
"coveralls": "3.0.3", | ||
"husky": "2.3.0", | ||
"coveralls": "3.0.4", | ||
"husky": "2.5.0", | ||
"jest": "24.8.0", | ||
"jest-dom": "3.4.0", | ||
"jest-dom": "3.5.0", | ||
"jest-environment-jsdom": "24.8.0", | ||
"jest-mock-console": "1.0.0", | ||
"lint-staged": "8.1.7", | ||
"lint-staged": "8.2.1", | ||
"lodash": "4.17.11", | ||
"mobx": "5.9.4", | ||
"prettier": "1.17.1", | ||
"mobx": "5.10.1", | ||
"prettier": "1.18.2", | ||
"react": "16.8.6", | ||
"react-dom": "16.8.6", | ||
"react-hooks-testing-library": "0.5.0", | ||
"react-test-renderer": "16.8.6", | ||
"react-testing-library": "7.0.1", | ||
"rimraf": "2.6.3", | ||
"rollup": "1.12.4", | ||
"rollup-plugin-alias": "1.5.1", | ||
"rollup": "1.16.2", | ||
"rollup-plugin-alias": "1.5.2", | ||
"rollup-plugin-commonjs": "10.0.0", | ||
"rollup-plugin-filesize": "6.1.0", | ||
"rollup-plugin-node-resolve": "5.0.0", | ||
"rollup-plugin-filesize": "6.1.1", | ||
"rollup-plugin-node-resolve": "5.1.0", | ||
"rollup-plugin-replace": "2.2.0", | ||
"rollup-plugin-terser": "5.0.0", | ||
"rollup-plugin-typescript2": "0.21.1", | ||
"rollup-plugin-typescript2": "0.21.2", | ||
"ts-jest": "24.0.2", | ||
"tslint": "5.16.0", | ||
"tslint": "5.18.0", | ||
"tslint-config-prettier": "1.18.0", | ||
@@ -70,0 +70,0 @@ "typescript": "3.4.5" |
319
README.md
@@ -11,3 +11,3 @@ # mobx-react-lite <!-- omit in toc --> | ||
Class based components **are not supported** except using `<Observer>` directly in its `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. | ||
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. | ||
@@ -18,218 +18,45 @@ [![NPM](https://nodei.co/npm/mobx-react-lite.png)](https://www.npmjs.com/package/mobx-react-lite) | ||
- [API documentation](#api-documentation) | ||
- [`<Observer/>`](#observer) | ||
- [`observer<P>(baseComponent: FunctionComponent<P>, options?: IObserverOptions): FunctionComponent<P>`](#observerpbasecomponent-functioncomponentp-options-iobserveroptions-functioncomponentp) | ||
- [`useObserver<T>(fn: () => T, baseComponentName = "observed", options?: IUseObserverOptions): T`](#useobservertfn---t-basecomponentname--%22observed%22-options-iuseobserveroptions-t) | ||
- [`useLocalStore<T>(initializer: () => T): T`](#uselocalstoretinitializer---t-t) | ||
- [`useAsObservableSource<T>(state: T): T`](#useasobservablesourcetstate-t-t) | ||
- [(deprecated) `useObservable<T>(initialValue: T): T`](#useobservabletinitialvalue-t-t) | ||
- [Lazy initialization](#lazy-initialization) | ||
- [(deprecated) `useComputed(func: () => T, inputs: ReadonlyArray<any> = []): T`](#usecomputedfunc---t-inputs-readonlyarrayany---t) | ||
- [(deprecated) `useDisposable<D extends TDisposable>(disposerGenerator: () => D, inputs: ReadonlyArray<any> = []): D`](#usedisposabled-extends-tdisposabledisposergenerator---d-inputs-readonlyarrayany---d) | ||
- [Creating MobX reactions inside hook components](#creating-mobx-reactions-inside-hook-components) | ||
- [Server Side Rendering with `useStaticRendering`](#server-side-rendering-with-usestaticrendering) | ||
- [Why no Provider/inject?](#why-no-providerinject) | ||
## User Guide 👉 https://mobx-react.js.org | ||
## API documentation | ||
The site contains various examples and recipes for using MobX in React world. Feel free to contribute. The API reference of this package follows 👇. | ||
### `<Observer/>` | ||
## API reference ⚒ | ||
`Observer` is a React component, which applies observer to an anonymous region in your component. | ||
It takes as children a single, argumentless function which should return exactly one React component. | ||
The rendering in the function will be tracked and automatically re-rendered when needed. | ||
This can come in handy when needing to pass render function to external components (for example the React Native listview), or if you want to observe only relevant parts of the output for a performance reasons. | ||
> **`<Observer>{renderFn}</Observer>`** _([user guide](https://mobx-react.netlify.com/observer-component))_ | ||
```jsx | ||
import { Observer, useObservable } from "mobx-react-lite" | ||
> **`observer<P>(baseComponent: FunctionComponent<P>, options?: IObserverOptions): FunctionComponent<P>`** _([user guide](https://mobx-react.netlify.com/observer-hoc))_ | ||
function ObservePerson(props) { | ||
const person = useObservable({ name: "John" }) | ||
return ( | ||
<div> | ||
{person.name} | ||
<Observer>{() => <div>{person.name}</div>}</Observer> | ||
<button onClick={() => (person.name = "Mike")}>No! I am Mike</button> | ||
</div> | ||
) | ||
```ts | ||
interface IObserverOptions { | ||
// Pass true to use React.forwardRef over the inner component. It's false by the default. | ||
forwardRef?: boolean | ||
} | ||
``` | ||
[![Edit ObservePerson](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/jzj48v2xry?module=%2Fsrc%2FObservePerson.tsx) | ||
> **`useObserver<T>(fn: () => T, baseComponentName = "observed", options?: IUseObserverOptions): T`** _([user guide](https://mobx-react.netlify.com/observer-hook))_ | ||
In case you are a fan of render props, you can use that instead of children. Be advised, that you cannot use both approaches at once, children have a precedence. | ||
Example | ||
```jsx | ||
import { Observer, useObservable } from "mobx-react-lite" | ||
function ObservePerson(props) { | ||
const person = useObservable({ name: "John" }) | ||
return ( | ||
<div> | ||
{person.name} | ||
<Observer render={() => <div>{person.name}</div>} /> | ||
<button onClick={() => (person.name = "Mike")}>No! I am Mike</button> | ||
</div> | ||
) | ||
```ts | ||
interface IUseObserverOptions { | ||
// optional custom hook that should make a component re-render (or not) upon changes | ||
useForceUpdate: () => () => void | ||
} | ||
``` | ||
### `observer<P>(baseComponent: FunctionComponent<P>, options?: IObserverOptions): FunctionComponent<P>` | ||
**`useLocalStore<T, S>(initializer: () => T, source?: S): T`** _([user guide](https://mobx-react.netlify.com/state-local))_ | ||
Function that converts a function component into a reactive component, which tracks which observables are used automatically re-renders the component when one of these values changes. Observables can be passed through props, accessed from context or created locally with `useObservable`. | ||
**`useAsObservableSource<T>(source: T): T`** _([user guide](https://mobx-react.netlify.com/state-outsourcing))_ | ||
As for options, it is an optional object with the following optional properties: | ||
## React Strict mode ☄ | ||
- `forwardRef`: pass `true` to use [`forwardRef`](https://reactjs.org/docs/forwarding-refs.html) over the inner component, pass `false` (the default) otherwise. | ||
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. | ||
```tsx | ||
import { observer, useObservable } from "mobx-react-lite" | ||
## Deprecation notice ⚠ | ||
const FriendlyComponent = observer(() => { | ||
const friendNameRef = React.useRef() | ||
const data = useObservable({ | ||
friends: [] as string[], | ||
addFriend(favorite: boolean = false) { | ||
if (favorite === true) { | ||
data.friends.unshift(friendNameRef.current.value + " * ") | ||
} else { | ||
data.friends.push(friendNameRef.current.value) | ||
} | ||
friendNameRef.current.value = "" | ||
}, | ||
get friendsCount() { | ||
return data.friends.length | ||
} | ||
}) | ||
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. | ||
return ( | ||
<div> | ||
<b>Count of friends: {data.friendsCount} </b> | ||
{data.friends.map(friend => ( | ||
<div>{friend}</div> | ||
))} | ||
<hr /> | ||
<input ref={friendNameRef} /> | ||
<button onClick={data.addFriend}>Add friend </button> | ||
<button onClick={() => data.addFriend(true)}>Add favorite friend</button> | ||
</div> | ||
) | ||
}) | ||
``` | ||
--- | ||
[![Edit FriendlyComponent](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/jzj48v2xry?module=%2Fsrc%2FFriendlyComponent.tsx) | ||
### `useObserver<T>(fn: () => T, baseComponentName = "observed", options?: IUseObserverOptions): T` | ||
Low level implementation used internally by `observer`. | ||
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). One good thing about this is that if any hook changes an observable for some reason then the component won't rerender twice unnecessarily. | ||
The following optional parameters are available: | ||
- `baseComponentName`: a string that will be used as part of the reaction name. | ||
As for the options, the following are available: | ||
- `useForceUpdate`: optional custom hook that should make a component re-render (or not) when changes are detected. | ||
```tsx | ||
import { memo } from "react" | ||
import { useObserver, useObservable } from "mobx-react-lite" | ||
const Person = memo(props => { | ||
const person = useObservable({ name: "John" }) | ||
return useObserver(() => ( | ||
<div> | ||
{person.name} | ||
<button onClick={() => (person.name = "Mike")}>No! I am Mike</button> | ||
</div> | ||
)) | ||
}) | ||
``` | ||
### `useLocalStore<T>(initializer: () => T): T` | ||
`useLocalStore` creates a local, observable store that is initialized once, and can be used throughout the life-cycle of the component. Use it if you want to use mobx-powered, local store. | ||
For simple cases it is recommended to use `React.setState`, but if your component requires complex view models, consider creating a local mobx store by using this hook. | ||
If the returned value is a plain object, it will be automatically be passed through `observable`, turning fields into observable properties, and `get` based property accessors in computed values, and functions in bound actions. | ||
If new class instances are returned from the initializer, they will be kept as is. Quick example: | ||
```typescript | ||
function Counter() { | ||
const store = useLocalStore(() => ({ | ||
count: 0, | ||
inc() { | ||
this.count += 1 | ||
} | ||
})) | ||
return useObserver(() => ( | ||
<div> | ||
Count: {store.count} | ||
<button onClick={store.inc}>Increment</button> | ||
</div> | ||
)) | ||
} | ||
``` | ||
It is important to realize that the store is created only once! It is not possible to specify dependencies to force re-creation, _nor should you directly be referring to props for the initializer function_, as changes in those won't propagate. | ||
Instead, if your store needs to refer to props (or `useState` based local state), the `useLocalStore` should be combined with the `useAsObservableSource` hook, see below. | ||
### `useAsObservableSource<T>(state: T): T` | ||
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). | ||
The goal of this hook is to trap React primitives such as props or state into a local, observable object | ||
so that the `store` initializer can safely refer to it, and get notified if any of the values change. | ||
Example: | ||
```typescript | ||
function Counter({ multiplier }) { | ||
const observableProps = useAsObservableSource({ multiplier }) | ||
const store = useLocalStore(() => ({ | ||
count: 0, | ||
get multiplied() { | ||
return observableProps.multiplier * this.count | ||
}, | ||
inc() { | ||
this.count += 1 | ||
} | ||
})) | ||
return ( | ||
<Observer> | ||
{() => ( | ||
<div> | ||
Multiplied count: {store.multiplied} | ||
<button onClick={store.inc}>Increment</button> | ||
</div> | ||
)} | ||
</Observer> | ||
) | ||
} | ||
``` | ||
In the above example, any change to `multiplier` prop will show up in the `observableProps` observable object, and be picked up by the `store`. | ||
Warning: _the return value of `useAsObservableSource` should never be deconstructed! So, don't write: `const {multiplier} = useAsObservableSource({ multiplier })`!_ | ||
The value passed to `useAsObservableSource` should always be an object, and is made only shallowly observable. | ||
The object returned by `useAsObservableSource`, although observable, should be considered read-only for all practical purposes. | ||
Use `useLocalStore` to create local, observable, mutable, state. | ||
Tip: for optimal performance it is recommend to not use `useAsObservableSource` together on the same component as `useObserver` (or `observer`), as it might trigger double renderings. Instead, use `<Observer>`. | ||
# Notice of deprecation | ||
We will be deprecating following utilities from the package in the next major version. See [the discussion](https://github.com/mobxjs/mobx-react-lite/issues/94) and [the relevant PR](https://github.com/mobxjs/mobx-react-lite/pull/130) for details. | ||
---- | ||
### `useObservable<T>(initialValue: T): T` | ||
_Deprecated, will be removed in next major_ | ||
> **Use the `useLocalStore` instead** ([user guide](https://mobx-react.netlify.com/state-local)) | ||
@@ -293,3 +120,3 @@ 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. | ||
_Deprecated, will be removed in next major_ | ||
> **Use the `useLocalStore` instead** ([user guide](https://mobx-react.netlify.com/state-local)) | ||
@@ -327,3 +154,3 @@ 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. | ||
_Deprecated, will be removed in next major_ | ||
> **Use the `React.useEffect` instead** ([user guide](https://mobx-react.netlify.com/recipes-effects)) | ||
@@ -359,97 +186,1 @@ 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. | ||
``` | ||
## Creating MobX reactions inside hook components | ||
If needed, it is possible to create MobX based side effects in hook based components using the standard APIs. For example: | ||
```typescript | ||
function Counter() { | ||
const store = useLocalStore(() => ({ | ||
count: 0, | ||
inc() { | ||
store.count += 1 | ||
} | ||
})) | ||
useEffect( | ||
() => | ||
autorun(() => { | ||
document.title = "Ticked: " + store.count | ||
}), | ||
[] | ||
) | ||
return /* etc */ | ||
} | ||
``` | ||
Note that the disposer function of `autorun` should be returned to `useEffect` so that the effect is cleaned up properly by React. | ||
Secondly, when using MobX based side effects, you typically don't want to re-create them after each rendering, so make sure to pass in an empty array `[]` as deps to `useEffect`. | ||
This will yield the same limitation as when using `useLocalStore`: changes to props used by the side-effect won't be picked up automatically, so don't refer to them directly. Instead, leverage `useAsObservableSource` again: | ||
```typescript | ||
function Counter({ prefix }) { | ||
const observableProps = useAsObservableSource({ prefix }) | ||
const store = useLocalStore(() => ({ | ||
count: 0, | ||
inc() { | ||
store.count += 1 | ||
} | ||
})) | ||
useEffect( | ||
() => | ||
autorun(() => { | ||
document.title = `${observableProps.prefix}: ${store.count}` | ||
}), | ||
[] | ||
) | ||
return useObserver(() => ( | ||
<div> | ||
Count: {store.count} | ||
<button onClick={store.inc}>Increment</button> | ||
</div> | ||
)) | ||
} | ||
``` | ||
## Server Side Rendering with `useStaticRendering` | ||
When using server side rendering, the components are rendered only once. | ||
Since components are never unmounted, `observer` components would in this case leak memory when being rendered server side. | ||
To avoid leaking memory, call `useStaticRendering(true)` when using server side rendering which essentially disables observer. | ||
```js | ||
import { useStaticRendering } from "mobx-react-lite" | ||
useStaticRendering(true) | ||
``` | ||
This makes sure the component won't try to react to any future data changes. | ||
## Why no Provider/inject? | ||
Historically the Provider was useful because a lot of boilerplate was required due to experimental (but widely used) context. By introducing new [Context API](https://reactjs.org/docs/context.html) in React 16.3 it's fairly easy to do this. | ||
```js | ||
const StoreContext = React.createContext(createStore()) | ||
// a file with a component | ||
function ConnectedComponent() { | ||
// replacement for inject | ||
const store = useContext(StoreContext) | ||
} | ||
``` | ||
If you need to create a store sometimes later, you can just render `StoreContext.Provider` somewhere in tree. | ||
```js | ||
const StoreContext = React.createContext() | ||
function App({ children }) { | ||
return <StoreContext.Provider value={createStore()}>{children}</StoreContext.Provider> | ||
} | ||
``` |
Sorry, the diff of this file is not supported yet
1633
293514
182