mobx-react-lite
Advanced tools
Comparing version 0.3.5 to 0.3.6
@@ -15,3 +15,7 @@ (function (global, factory) { | ||
function useObservable(initialValue) { | ||
return react.useRef(mobx.observable(initialValue)).current; | ||
const observableRef = react.useRef(null); | ||
if (!observableRef.current) { | ||
observableRef.current = mobx.observable(initialValue); | ||
} | ||
return observableRef.current; | ||
} | ||
@@ -24,2 +28,5 @@ | ||
const doNothingDisposer = () => { | ||
// empty | ||
}; | ||
/** | ||
@@ -37,11 +44,34 @@ * Adds an observable effect (reaction, autorun, or anything else that returns a disposer) that will be registered upon component creation and disposed upon unmounting. | ||
const disposerRef = react.useRef(undefined); | ||
react.useMemo(() => { | ||
disposerRef.current = disposerGenerator(); | ||
const earlyDisposedRef = react.useRef(false); | ||
react.useEffect(() => { | ||
return lazyCreateDisposer(false); | ||
}, inputs); | ||
react.useEffect(() => () => { | ||
if (disposerRef.current && typeof disposerRef.current === "function") { | ||
disposerRef.current(); | ||
function lazyCreateDisposer(earlyDisposal) { | ||
// ensure that we won't create a new disposer if it was early disposed | ||
if (earlyDisposedRef.current) { | ||
return doNothingDisposer; | ||
} | ||
}, inputs); | ||
return disposerRef.current; | ||
if (!disposerRef.current) { | ||
const newDisposer = disposerGenerator(); | ||
if (typeof newDisposer !== "function") { | ||
const error = new Error("generated disposer must be a function"); | ||
{ | ||
// tslint:disable-next-line:no-console | ||
console.error(error); | ||
return doNothingDisposer; | ||
} | ||
} | ||
disposerRef.current = newDisposer; | ||
} | ||
return () => { | ||
if (disposerRef.current) { | ||
disposerRef.current(); | ||
disposerRef.current = undefined; | ||
} | ||
if (earlyDisposal) { | ||
earlyDisposedRef.current = true; | ||
} | ||
}; | ||
} | ||
return lazyCreateDisposer(true); | ||
} | ||
@@ -48,0 +78,0 @@ |
@@ -12,3 +12,7 @@ import { spy, observable, Reaction, computed } from 'mobx'; | ||
function useObservable(initialValue) { | ||
return useRef(observable(initialValue)).current; | ||
const observableRef = useRef(null); | ||
if (!observableRef.current) { | ||
observableRef.current = observable(initialValue); | ||
} | ||
return observableRef.current; | ||
} | ||
@@ -21,2 +25,5 @@ | ||
const doNothingDisposer = () => { | ||
// empty | ||
}; | ||
/** | ||
@@ -34,11 +41,34 @@ * Adds an observable effect (reaction, autorun, or anything else that returns a disposer) that will be registered upon component creation and disposed upon unmounting. | ||
const disposerRef = useRef(undefined); | ||
useMemo(() => { | ||
disposerRef.current = disposerGenerator(); | ||
const earlyDisposedRef = useRef(false); | ||
useEffect(() => { | ||
return lazyCreateDisposer(false); | ||
}, inputs); | ||
useEffect(() => () => { | ||
if (disposerRef.current && typeof disposerRef.current === "function") { | ||
disposerRef.current(); | ||
function lazyCreateDisposer(earlyDisposal) { | ||
// ensure that we won't create a new disposer if it was early disposed | ||
if (earlyDisposedRef.current) { | ||
return doNothingDisposer; | ||
} | ||
}, inputs); | ||
return disposerRef.current; | ||
if (!disposerRef.current) { | ||
const newDisposer = disposerGenerator(); | ||
if (typeof newDisposer !== "function") { | ||
const error = new Error("generated disposer must be a function"); | ||
{ | ||
// tslint:disable-next-line:no-console | ||
console.error(error); | ||
return doNothingDisposer; | ||
} | ||
} | ||
disposerRef.current = newDisposer; | ||
} | ||
return () => { | ||
if (disposerRef.current) { | ||
disposerRef.current(); | ||
disposerRef.current = undefined; | ||
} | ||
if (earlyDisposal) { | ||
earlyDisposedRef.current = true; | ||
} | ||
}; | ||
} | ||
return lazyCreateDisposer(true); | ||
} | ||
@@ -45,0 +75,0 @@ |
@@ -15,3 +15,7 @@ (function (global, factory) { | ||
function useObservable(initialValue) { | ||
return react.useRef(mobx.observable(initialValue)).current; | ||
const observableRef = react.useRef(null); | ||
if (!observableRef.current) { | ||
observableRef.current = mobx.observable(initialValue); | ||
} | ||
return observableRef.current; | ||
} | ||
@@ -24,2 +28,5 @@ | ||
const doNothingDisposer = () => { | ||
// empty | ||
}; | ||
/** | ||
@@ -37,11 +44,34 @@ * Adds an observable effect (reaction, autorun, or anything else that returns a disposer) that will be registered upon component creation and disposed upon unmounting. | ||
const disposerRef = react.useRef(undefined); | ||
react.useMemo(() => { | ||
disposerRef.current = disposerGenerator(); | ||
const earlyDisposedRef = react.useRef(false); | ||
react.useEffect(() => { | ||
return lazyCreateDisposer(false); | ||
}, inputs); | ||
react.useEffect(() => () => { | ||
if (disposerRef.current && typeof disposerRef.current === "function") { | ||
disposerRef.current(); | ||
function lazyCreateDisposer(earlyDisposal) { | ||
// ensure that we won't create a new disposer if it was early disposed | ||
if (earlyDisposedRef.current) { | ||
return doNothingDisposer; | ||
} | ||
}, inputs); | ||
return disposerRef.current; | ||
if (!disposerRef.current) { | ||
const newDisposer = disposerGenerator(); | ||
if (typeof newDisposer !== "function") { | ||
const error = new Error("generated disposer must be a function"); | ||
{ | ||
// tslint:disable-next-line:no-console | ||
console.error(error); | ||
return doNothingDisposer; | ||
} | ||
} | ||
disposerRef.current = newDisposer; | ||
} | ||
return () => { | ||
if (disposerRef.current) { | ||
disposerRef.current(); | ||
disposerRef.current = undefined; | ||
} | ||
if (earlyDisposal) { | ||
earlyDisposedRef.current = true; | ||
} | ||
}; | ||
} | ||
return lazyCreateDisposer(true); | ||
} | ||
@@ -48,0 +78,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.mobxReact={},e.mobx,e.React)}(this,function(e,r,t){"use strict";if(!t.useState)throw new Error("mobx-react-lite requires React with Hooks support (alpha versions)");if(!r.spy)throw new Error("mobx-react-lite requires mobx at least version 4 to be available");function n(e,r=[]){const n=t.useRef(void 0);return t.useMemo(()=>{n.current=e()},r),t.useEffect(()=>()=>{n.current&&"function"==typeof n.current&&n.current()},r),n.current}let o=!1;function u(){return o}const i=[];function s(e,n="observed"){if(u())return e();const o=function(){const[e,r]=t.useState(1);return t.useCallback(()=>{r(e+1)},[])}(),s=t.useRef(new r.Reaction(`observer(${n})`,()=>{o()}));let c;return function(e){t.useEffect(()=>e,i)}(()=>{s.current.dispose()}),s.current.track(()=>{c=e()}),c}function c({children:e,render:r}){const t=e||r;return"function"!=typeof t?null:s(t)}function f(e,r,t,n,o){const u="children"===r?"render":"children",i="function"==typeof e[r],s="function"==typeof e[u];return i&&s?new Error("MobX Observer: Do not use children and render in the same time in`"+t):i||s?null:new Error("Invalid prop `"+o+"` of type `"+typeof e[r]+"` supplied to `"+t+"`, expected `function`.")}c.propTypes={children:f,render:f},c.displayName="Observer",e.useObservable=function(e){return t.useRef(r.observable(e)).current},e.useComputed=function(e,n=[]){return t.useMemo(()=>r.computed(e),n).get()},e.useDisposable=n,e.useObservableEffect=function(e,r=[]){return n(e,r)},e.isUsingStaticRendering=u,e.useStaticRendering=function(e){o=e},e.observer=function(e,r){if(u())return e;const n=Object.assign({forwardRef:!1},r),o=e.displayName||e.name,i=(r,t)=>s(()=>e(r,t),o);let c;return(c=n.forwardRef?t.memo(t.forwardRef(i)):t.memo(i)).displayName=o,c},e.useObserver=s,e.Observer=c,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.mobxReact={},e.mobx,e.React)}(this,function(e,r,n){"use strict";if(!n.useState)throw new Error("mobx-react-lite requires React with Hooks support (alpha versions)");if(!r.spy)throw new Error("mobx-react-lite requires mobx at least version 4 to be available");const t=()=>{};function o(e,r=[]){const o=n.useRef(void 0),u=n.useRef(!1);function c(r){if(u.current)return t;if(!o.current){const r=e();if("function"!=typeof r){const e=new Error("generated disposer must be a function");return console.error(e),t}o.current=r}return()=>{o.current&&(o.current(),o.current=void 0),r&&(u.current=!0)}}return n.useEffect(()=>c(!1),r),c(!0)}let u=!1;function c(){return u}const i=[];function s(e,t="observed"){if(c())return e();const o=function(){const[e,r]=n.useState(1);return n.useCallback(()=>{r(e+1)},[])}(),u=n.useRef(new r.Reaction(`observer(${t})`,()=>{o()}));let s;return function(e){n.useEffect(()=>e,i)}(()=>{u.current.dispose()}),u.current.track(()=>{s=e()}),s}function f({children:e,render:r}){const n=e||r;return"function"!=typeof n?null:s(n)}function a(e,r,n,t,o){const u="children"===r?"render":"children",c="function"==typeof e[r],i="function"==typeof e[u];return c&&i?new Error("MobX Observer: Do not use children and render in the same time in`"+n):c||i?null:new Error("Invalid prop `"+o+"` of type `"+typeof e[r]+"` supplied to `"+n+"`, expected `function`.")}f.propTypes={children:a,render:a},f.displayName="Observer",e.useObservable=function(e){const t=n.useRef(null);return t.current||(t.current=r.observable(e)),t.current},e.useComputed=function(e,t=[]){return n.useMemo(()=>r.computed(e),t).get()},e.useDisposable=o,e.useObservableEffect=function(e,r=[]){return o(e,r)},e.isUsingStaticRendering=c,e.useStaticRendering=function(e){u=e},e.observer=function(e,r){if(c())return e;const t=Object.assign({forwardRef:!1},r),o=e.displayName||e.name,u=(r,n)=>s(()=>e(r,n),o);let i;return(i=t.forwardRef?n.memo(n.forwardRef(u)):n.memo(u)).displayName=o,i},e.useObserver=s,e.Observer=f,Object.defineProperty(e,"__esModule",{value:!0})}); |
@@ -12,3 +12,7 @@ import { spy, observable, Reaction, computed } from 'mobx'; | ||
function useObservable(initialValue) { | ||
return useRef(observable(initialValue)).current; | ||
const observableRef = useRef(null); | ||
if (!observableRef.current) { | ||
observableRef.current = observable(initialValue); | ||
} | ||
return observableRef.current; | ||
} | ||
@@ -21,2 +25,5 @@ | ||
const doNothingDisposer = () => { | ||
// empty | ||
}; | ||
/** | ||
@@ -34,11 +41,34 @@ * Adds an observable effect (reaction, autorun, or anything else that returns a disposer) that will be registered upon component creation and disposed upon unmounting. | ||
const disposerRef = useRef(undefined); | ||
useMemo(() => { | ||
disposerRef.current = disposerGenerator(); | ||
const earlyDisposedRef = useRef(false); | ||
useEffect(() => { | ||
return lazyCreateDisposer(false); | ||
}, inputs); | ||
useEffect(() => () => { | ||
if (disposerRef.current && typeof disposerRef.current === "function") { | ||
disposerRef.current(); | ||
function lazyCreateDisposer(earlyDisposal) { | ||
// ensure that we won't create a new disposer if it was early disposed | ||
if (earlyDisposedRef.current) { | ||
return doNothingDisposer; | ||
} | ||
}, inputs); | ||
return disposerRef.current; | ||
if (!disposerRef.current) { | ||
const newDisposer = disposerGenerator(); | ||
if (typeof newDisposer !== "function") { | ||
const error = new Error("generated disposer must be a function"); | ||
{ | ||
// tslint:disable-next-line:no-console | ||
console.error(error); | ||
return doNothingDisposer; | ||
} | ||
} | ||
disposerRef.current = newDisposer; | ||
} | ||
return () => { | ||
if (disposerRef.current) { | ||
disposerRef.current(); | ||
disposerRef.current = undefined; | ||
} | ||
if (earlyDisposal) { | ||
earlyDisposedRef.current = true; | ||
} | ||
}; | ||
} | ||
return lazyCreateDisposer(true); | ||
} | ||
@@ -45,0 +75,0 @@ |
@@ -16,3 +16,7 @@ 'use strict'; | ||
function useObservable(initialValue) { | ||
return react.useRef(mobx.observable(initialValue)).current; | ||
const observableRef = react.useRef(null); | ||
if (!observableRef.current) { | ||
observableRef.current = mobx.observable(initialValue); | ||
} | ||
return observableRef.current; | ||
} | ||
@@ -25,2 +29,5 @@ | ||
const doNothingDisposer = () => { | ||
// empty | ||
}; | ||
/** | ||
@@ -38,11 +45,34 @@ * Adds an observable effect (reaction, autorun, or anything else that returns a disposer) that will be registered upon component creation and disposed upon unmounting. | ||
const disposerRef = react.useRef(undefined); | ||
react.useMemo(() => { | ||
disposerRef.current = disposerGenerator(); | ||
const earlyDisposedRef = react.useRef(false); | ||
react.useEffect(() => { | ||
return lazyCreateDisposer(false); | ||
}, inputs); | ||
react.useEffect(() => () => { | ||
if (disposerRef.current && typeof disposerRef.current === "function") { | ||
disposerRef.current(); | ||
function lazyCreateDisposer(earlyDisposal) { | ||
// ensure that we won't create a new disposer if it was early disposed | ||
if (earlyDisposedRef.current) { | ||
return doNothingDisposer; | ||
} | ||
}, inputs); | ||
return disposerRef.current; | ||
if (!disposerRef.current) { | ||
const newDisposer = disposerGenerator(); | ||
if (typeof newDisposer !== "function") { | ||
const error = new Error("generated disposer must be a function"); | ||
{ | ||
// tslint:disable-next-line:no-console | ||
console.error(error); | ||
return doNothingDisposer; | ||
} | ||
} | ||
disposerRef.current = newDisposer; | ||
} | ||
return () => { | ||
if (disposerRef.current) { | ||
disposerRef.current(); | ||
disposerRef.current = undefined; | ||
} | ||
if (earlyDisposal) { | ||
earlyDisposedRef.current = true; | ||
} | ||
}; | ||
} | ||
return lazyCreateDisposer(true); | ||
} | ||
@@ -49,0 +79,0 @@ |
{ | ||
"name": "mobx-react-lite", | ||
"version": "0.3.5", | ||
"version": "0.3.6", | ||
"description": "Lightweight React bindings for MobX based on experimental React hooks", | ||
@@ -43,5 +43,5 @@ "main": "dist/index.js", | ||
"jest": "^23.6.0", | ||
"jest-dom": "^2.1.1", | ||
"jest-dom": "^3.0.0", | ||
"jest-environment-jsdom": "^23.4.0", | ||
"jest-mock-console": "^0.4.0", | ||
"jest-mock-console": "^0.4.2", | ||
"lint-staged": "^8.0.4", | ||
@@ -55,10 +55,10 @@ "lodash": "^4.17.11", | ||
"rimraf": "^2.6.2", | ||
"rollup": "^0.67.0", | ||
"rollup": "^1.1.0", | ||
"rollup-plugin-alias": "^1.4.0", | ||
"rollup-plugin-commonjs": "^9.2.0", | ||
"rollup-plugin-filesize": "^5.0.1", | ||
"rollup-plugin-node-resolve": "^3.4.0", | ||
"rollup-plugin-filesize": "^6.0.0", | ||
"rollup-plugin-node-resolve": "^4.0.0", | ||
"rollup-plugin-replace": "^2.1.0", | ||
"rollup-plugin-terser": "^3.0.0", | ||
"rollup-plugin-typescript2": "^0.17.2", | ||
"rollup-plugin-terser": "^4.0.0", | ||
"rollup-plugin-typescript2": "^0.19.0", | ||
"ts-jest": "^23.10.4", | ||
@@ -65,0 +65,0 @@ "tslint": "^5.11.0", |
@@ -9,4 +9,6 @@ # mobx-react-lite <!-- omit in toc --> | ||
**You need React version 16.7.0-alpha.2 which is highly experimental and not recommended for production.** | ||
**You need React version 16.7.0-alpha.2 or 16.8.0-alpha.0 which is highly experimental and not recommended for production.** | ||
**Do not use React 16.7 as it's [missing Hooks support](https://reactjs.org/blog/2018/12/19/react-v-16-7.html)!** | ||
[![NPM](https://nodei.co/npm/mobx-react-lite.png)](https://www.npmjs.com/package/mobx-react-lite) | ||
@@ -22,3 +24,3 @@ | ||
- [`useComputed(func: () => T, inputs: ReadonlyArray<any> = []): T`](#usecomputedfunc---t-inputs-readonlyarrayany---t) | ||
- [`useDisposable<D extends IReactionDisposer>(disposerGenerator: () => D, inputs: ReadonlyArray<any> = []): D`](#usedisposabled-extends-ireactiondisposerdisposergenerator---d-inputs-readonlyarrayany---d) | ||
- [`useDisposable<D extends TDisposable>(disposerGenerator: () => D, inputs: ReadonlyArray<any> = []): D`](#usedisposabled-extends-tdisposabledisposergenerator---d-inputs-readonlyarrayany---d) | ||
- [Server Side Rendering with `useStaticRendering`](#server-side-rendering-with-usestaticrendering) | ||
@@ -139,5 +141,5 @@ - [Why no Provider/inject?](#why-no-providerinject) | ||
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 and Map which are useful to track dynamic list/table of information. The Set is not supported (see https://github.com/mobxjs/mobx/issues/69). | ||
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` otherwise the rerender on update won't happen. | ||
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. | ||
@@ -147,3 +149,3 @@ ```tsx | ||
const TodoList = observer(() => { | ||
const TodoList = () => { | ||
const todos = useObservable(new Map<string, boolean>()) | ||
@@ -159,3 +161,3 @@ const todoRef = React.useRef() | ||
return ( | ||
return useObserver(() => ( | ||
<div> | ||
@@ -171,6 +173,24 @@ {Array.from(todos).map(([todo, done]) => ( | ||
</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) | ||
@@ -211,3 +231,3 @@ | ||
### `useDisposable<D extends IReactionDisposer>(disposerGenerator: () => D, inputs: ReadonlyArray<any> = []): D` | ||
### `useDisposable<D extends TDisposable>(disposerGenerator: () => D, inputs: ReadonlyArray<any> = []): D` | ||
@@ -286,3 +306,3 @@ 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. | ||
```tsx | ||
import { createSelector } from 'react-selector-hooks' | ||
import { createSelector } from "react-selector-hooks" | ||
@@ -307,5 +327,5 @@ const userSelector = createSelector(({ user }) => ({ | ||
const data = userSelector(store) | ||
return UiComponent({...data}) | ||
return UiComponent({ ...data }) | ||
// perhaps wrap it inside observer in here? | ||
return observer(UiComponent({...data})) | ||
return observer(UiComponent({ ...data })) | ||
} | ||
@@ -319,10 +339,9 @@ ``` | ||
const inject = (useSelector, baseComponent) => ( | ||
React.useMemo((props) => { | ||
const inject = (useSelector, baseComponent) => | ||
React.useMemo(props => { | ||
const store = useContext(StoreContext) | ||
const selected = useSelector(store) | ||
return baseComponent({ ...selected, ...props }) | ||
}) | ||
) | ||
@@ -329,0 +348,0 @@ // use the HOC with a selector |
Sorry, the diff of this file is not supported yet
261395
1014
342