Skip to content

Instantly share code, notes, and snippets.

@alexbepple
Last active April 22, 2018 09:26
Show Gist options
  • Save alexbepple/731869c0a995e36f645fa35c8dc7682d to your computer and use it in GitHub Desktop.
Save alexbepple/731869c0a995e36f645fa35c8dc7682d to your computer and use it in GitHub Desktop.
Simple reactive programming with S.js

autoscale: true

Simple reactive programming with S.js

Alex Bepple


Getting started with S.js

import s from 's-js'

// create signal
const a = s.value(0)

// construct computation ('derived signal')
const b = s(() => a() + 1)
console.log(b()) // => 1

// change signal value
a(1)
// computation updates automatically
console.log(b()) // => 2

Let’s explore S.js together


Functional Reactive Programming

Reactive + Functional = S.js + pick you poison (mine is Ramda)

No need to reinvent the wheel!
RxJS, Bacon, Kefir, most etc.

They might have super powers.
But sometimes, we just need wheels.


Simple state management1

(with React)


How would you implement this with Redux?

right fit


How would you implement this with Redux?

  • Duplicate everything?
  • Parameterize everything?
    • But what about separation of concerns?

right fit


Simplest approach with S.js:

Just rerender everything

const s1 = s.data(0)
const s2 = s.data(1)

const Counter = ({ signal }) => <div>
  <h2>Count: {signal()}</h2>
  <button onClick={() => signal(signal() + 1)}>+</button>
</div>

const App = () => <div>
  <Counter signal={s1} />
  <Counter signal={s2} />
  <Counter signal={s2} />
</div>

s.root(() =>
  s.on([s1, s2], () => render(<App />, document.getElementById('root')))
)

Extract presentation component

const CounterPresentation = ({ value, onIncrement }) => <div>
  <h2>Count: {value}</h2>
  <button onClick={onIncrement}>+</button>
</div>

const Counter = ({ signal }) =>
  <CounterPresentation
    value={signal()}
    onIncrement={() => signal(signal() + 1)}
  />

Optimize rendering by only rerendering when necessary

… think connect in react-redux

const connect = mapOwnProps => re.compose(
  re.lifecycle({
    componentDidMount() {
      s(() => this.setState(mapOwnProps(this.props)))
    }
  }),
  re.mapProps(mapOwnProps)
)

const Counter = z.connect(own => ({
  value: own.signal(),
  onIncrement: () => z.over(r.inc, own.signal)
}))(CounterPresentation)

Add saving and restoring from localStorage

without touching existing code

if (localStorage.counter) 
  s1(localStorage.counter)

s.root(() => 
  s(() => (localStorage.counter = s1())))

Bottomline

We lose

  • dev tools
  • middleware
    • Although, how much of it do we still need?

But we gain

  • no cruft
  • degrees of freedom for simplicity/optimized performance
  • modularity
  • keep our familiar tools, no need to learn entirely new API
    • S.js API is tiny


Photo credits

Hugh Stevenson Markus Petritz

Footnotes

  1. Gernot Höflechner’s talk “You don’t need state management” really struck a chord with me. He is onto something. Maybe we really can push all state to the boundaries.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment