Skip to content

Instantly share code, notes, and snippets.

@busypeoples
Last active August 9, 2019 20:38
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save busypeoples/91b0aea4f264336f28767b310f581186 to your computer and use it in GitHub Desktop.
Save busypeoples/91b0aea4f264336f28767b310f581186 to your computer and use it in GitHub Desktop.
// A quick experiment with Channels and React
// this needs more thinking
// but might be useful as a starting point.
import React from 'react'
import { render } from 'react-dom'
import { chan, go, timeout, take, put, putAsync, buffers } from 'js-csp';
import { add, assoc, curry, compose, map, mapObjIndexed, max, pluck, prop, reduce } from 'ramda'
const initialState = {
items: [],
isLoading: false,
}
let createStore = function(state) {
let _state = state
return {
get: () => _state,
set: state => {
_state = state
}
}
}
// helper
const createRender = curry((node, app) => render(app, node))
// create App channel... and render function
const AppChannel = chan(buffers.sliding(1))
const doRender = createRender(document.getElementById('mountNode'))
const store = createStore(initialState)
function* fetchItems(query) {
yield timeout(2000)
return [{id: 1, title: 'foo'}, {id: 2, title: 'bar'}]
}
const createChannel = (action, store) => {
const ch = chan()
go(function* () {
while(true) {
const value = yield take(ch)
yield put(AppChannel, action(store.get(), value));
}
})
return ch
}
// helper function for passing an object and getting channels
const createChannels = (actions, store) =>
mapObjIndexed(fn => createChannel(fn, store), actions)
// channels...
const getItems = () => {
go(function* () {
yield put(isLoading, true)
const fetchedItems = yield* fetchItems()
yield put(items, fetchedItems)
yield timeout(10)
yield put(isLoading, false)
})
}
const getNextId = compose(add(1), reduce(max, 0), pluck('id'))
const Actions = {
isLoading: (model, isLoading) => assoc('isLoading', isLoading, model),
items: (model, items) => assoc('items', items, model),
addItem: (model, title) =>
assoc('items',
[ ...prop('items', model), {title, id: getNextId(prop('items', model))} ],
model),
}
const { isLoading, items, addItem } = createChannels(Actions, store)
const findText = () => {
const textEl = document.getElementById('add')
const val = textEl.value
textEl.value = ''
return val
}
const App = ({ items, isLoading }) => {
if (isLoading) return (<p>loading...</p>)
return (
<div>
<h2>Random Items List</h2>
<ul>
{items.map(item => (
<li key={item.id} >{item.title}</li>
))}
</ul>
<input type='text' id='add' />
<button onClick={() => putAsync(addItem, findText())}>Add Item</button>
<button onClick={() => getItems()}>LoadItems</button>
</div>
)
}
const AppStart = (Component, store) => {
// initial render...
putAsync(AppChannel, store.get())
go(function* () {
while(true) {
store.set(yield take(AppChannel))
doRender(<Component {...store.get() } />)
}
})
}
// start
AppStart(App, store)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment