const choo = require('choo')
const app = choo()
app.model({
state: { title: 'Set the title' },
reducers: {
update: (action, state) => ({ title: action.value })
}
})
const mainView = (params, state, send) => choo.view`
<main>
<h1>${state.title}</h1>
<div
contenteditable="true"
oninput=${(e) => send('update', { value: e.target.value })}>Type here</div>
</main>
`
app.router((route) => [
route('/', mainView)
])
const tree = app.start()
document.body.appendChild(tree)
Doesn't update the state, missing something?
Are you in Firefox?
some versions of firefox don't trigger oninput on conteneditable
@toddself Sorry forgot the mention. Chrome OSX: Version 51.0.2704.103 (64-bit) that snippet works fine for you?
I am doing bedtime with my kids on I am on the phone. I see no immediate gotchas though. Can you confirm your reducer fires with a log or a breakpoint?
It is getting triggered, but action's value is not defined. Object {type: "update", value: undefined} requirebin for reference.

@toddself Hope you are still awake ;)
<div>s don't have a value property - your looking for innerText. Here's an example of a content editable div in a choo app.
@timwis duh, value...same with innerText or textContent, could you please have a look at the requirebin thinge?
The reducer
update: (action, state) => ({ title: action.innerText })
should be:
update: (action, state) => ({ title: action.value })
because your send uses value as its key:
send('update', { value: e.target.innerText })
@timwis That was the first thing /me tried, it results in this
This works:
const choo = require('choo')
const app = choo()
app.model({
state: { title: 'Set the title' },
reducers: {
update: (action, state) => ({ title: action.value })
}
})
const mainView = (params, state, send) => choo.view`
<main>
<h1>${state.title}</h1>
<div
contenteditable="true"
oninput=${(e) => send('update', { value: e.target.innerText })}>${state.title}</div>
</main>
`
app.router((route) => [
route('/', mainView)
])
const tree = app.start()
document.body.appendChild(tree)
The reason is that you're sending the value of the <div> but not changing it. Every call to oninput causes the DOM to update based on the current value of the state.
So you delete the type here and it sends an oninput with e.target.innerText equal to '', and updates the state. Then it re-renders. But you haven't changed what the input value for the contenteditable is, so it's still type here when you go to type in it.
You delete, it sends, resets to type here, rerenders, etc.
You need to track the state in the element which is sending the updates as well since the state is the source of truth, not the DOM.
Most helpful comment
This works:
The reason is that you're sending the value of the
<div>but not changing it. Every call tooninputcauses the DOM to update based on the current value of the state.So you delete the
type hereand it sends an oninput withe.target.innerTextequal to'', and updates the state. Then it re-renders. But you haven't changed what the input value for thecontenteditableis, so it's stilltype herewhen you go to type in it.You delete, it sends, resets to
type here, rerenders, etc.You need to track the state in the element which is sending the updates as well since the state is the source of truth, not the DOM.