Async atom example

Compare with Async redux-saga example: https://jsfiddle.net/zerkalica/3ogxg2sk/

by zerkalica

HTML

<body>
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script crossorigin src="https://unpkg.com/urc@1/dist/urc.umd.js"></script>
<script crossorigin src="https://unpkg.com/lom_atom@4/dist/lom_atom.umd.js"></script>

<div id="mount">
    </div>
</body>

Babel + JSX

// @flow
const {mem, action, ReactAtom} = lom_atom
const {createConnect} = urc
const {render} = ReactDOM
const {createElement, Component} = React

// initialization
function ErrorableView({error}) {
  if (error instanceof Promise) return <div>Loading...</div>

  const retry = mem.retry(error)

  return <div>
    <pre>{error.stack.toString()}</pre>
    {retry && <button onClick={retry}>Retry</button>}
  </div>
}

const connect = createConnect({
  BaseComponent: Component,
  renderError: ErrorableView,
  ReactAtom
})

class Fetcher {
  @mem emulateError = false
  @mem.key get(url) {
      throw fetch(url)
        .then(res => res.json())
        .then((data) => {
          if (this.emulateError) throw new Error('Fake error [500]')
          return data
        })
  }
}

function loader(child, loading) {
  try {
    return child()
  } catch(error) {
    if (error instanceof Error) throw error
    return loading
  }
}

// main

const fetcher = new Fetcher()

const DOG_URL = 'https://dog.ceo/api/breeds/image/random'

class Store {
  @mem get game() {
    return {
      url: fetcher.get(DOG_URL).message
    }
  }

  @action reset() {
    mem.cache(this.game)
  }
}

const ImgView = connect(({store}) => {
  return <p>
    {loader(
      () => <img src={store.game.url} />,
      <div>Custom loading...</div>
    )}
  </p>
})

const HelloView = connect(({store}) =>
  <div>
    <button onClick={store.reset}>Show dog</button>
    <input
      id="emulateError"
      type="checkbox"
      onChange={() => fetcher.emulateError = !fetcher.emulateError}
      value={fetcher.emulateError}
    />
    <label htmlFor="emulateError">Emulate error</label>
    <ImgView store={store} />
  </div>
)

const store = new Store()

ReactDOM.render(<HelloView store={store}/>, document.getElementById('mount'));