use-mobx-observable
TypeScript icon, indicating that this package has built-in type declarations

1.1.0-beta.1 • Public • Published

use-mobx-observable

npm version pages-build-deployment npm Coverage Status

Use mobx observable like useState

Note:

Install

npm install mobx@^6 react@^16.8 # peer dependencies
npm install use-mobx-observable

👉API Docs👈

Problem

The current mobx hooks from mobx-react-lite uses pattern like below:

function MyComponent() {

  let store = useLocalObservable(() => ({ count: 0 }))

  return (
    <Observer>                      // <---
    {                               // <---
      () =>                         // <---
        <div>{store.count}</div>
    }                               // <---
    </Observer>                     // <---
  )
}

To be reactive, you have to wrap your jsx with <Observer> and use render props, which is not a favorable way.

There was a Discussion around this issue. At the end of it, simple solution was proposed:

function useSelector(select) {
  const [selected, setSelected] = useState(select)
  useEffect(() => autorun(() => setSelected(select())), [])
  return selected
}

function myComponent({ observableOrder }) {
  const latestPrice = useSelector(() => observableOrder.price)
  return <h1>{latestPrice}</h1>
}

However, in practice, you'll still need to create an observable for computed and actions.

Do it at once

import { useObservable, select, useMultiObservables } from 'use-mobx-observable'

function MyComponent() {

  // create local observable, with plain object or an initializer
  let store = useObservable({
      count: 0,
      get countText() {
        return `Count: ${this.count}`
      },
      add() {
        this.count += 1
      },
    })

  // map external observable props to getters
  let store2 = useObservable(select(externalStore, ['propsA', 'propsB'])({ count: 0 }/* Optional */))

  // chaining with lodash.flow for mapping multiple sources
  let store3 = useObservable(
    _.flow(
      select(externalStore, ['propsA', 'propsB'],
      select(externalStore2, { renameC: 'propsC' },  // rename getters
    )({
      get sum() {
        return this.propsA + this.renameC
      }
     })
  )

  // use observable directly, returned value can be omitted since it is the same as input
  // Be ware of the performance impact: any props change will trigger a rerender.
  useObservable(store)

  return (
    <div>
      <h1>{store.countText}</h1>
      <button onClick={() => store.add()}>Add</button>
    </div>
  )
}

Automatically wrap observer HOC

Since React 17, jsx transformation is done via react/run-time instead of React.createElement. Typescript 4.1 introduced new jsx options which enabled customized jsx factory.

Hence, automatically wrap all your components with observer can be easily done via change your tsconfig.json:

{
  "compilerOptions": {
    "target": "esnext",
    "module": "commonjs",
    "jsx": "react-jsx",
    "jsxImportSource": "use-mobx-observable"
  }
}

If you choose this way, please remember install mobx-react-lite. Check auto-wrap-observer/wrap-jsx.js for detail.

Performance Impact?

TLDR; No, nothing you should worry about

Package Sidebar

Install

npm i use-mobx-observable

Weekly Downloads

25

Version

1.1.0-beta.1

License

MIT

Unpacked Size

23 kB

Total Files

25

Last publish

Collaborators

  • noru