Actions
In Act events are not dealt like in plain HTML. This may sound bad for who is starting, but it comes with nice perks. The way Act deals with events is closer to Redux's way, but with some more details.
To simplify let's start with something that resembles Redux. We'll call this events that dispatch an action.
The syntax is {event: handler}
, where event
is any DOM event like click
,
keyup
, keydown
, change
, and so on, and handler
a function that you'll
define. handler
will receive both the event object (so you can do things like
preventDefault
, stopPropagation
, get key codes or values from the target
DOM element) and a history
object. You can think of this function as the
dispatch
in Redux, or some sort of setState
on steroids :D.
const handler (event, history) =>
history.push('someAction', 'somePayload')
const button = () => ['button', {click: handler}]
That's the general syntax. history.push
will send this to your reducer and
your app will rerender. To find out more about history
check its docs.
Since this example is a little too simple, let's see a better one:
import main from '@act/main'
const add (event, history) =>
history.push('add', 1)
const view = (model) => ['button', {click: add}, model]
const reducer = (state = 0, {type, payload}) =>
type === 'add' ? state + payload : state
main(view, { reducer })
That's the whole app. When you click in the button, it calls add, that calls
history.push
, that calls the reducer that will rerender your whole app (yeah,
ok, just a button with a number).
Events with constant values
Now let's make it even simpler. Since most of the time you want to emit a simple value from your events, like in the example above, you can reduce it to:
import main from '@act/main'
const view = (model) => ['button', {click: {add: 1}}, model]
const reducer = (state = 0, {type, payload}) =>
type === 'add' ? state + payload : state
main(view, { reducer })
So we removed the handler and changed the syntax to be {click: {add: 1}}
.
Again, click
is any DOM event, but here add
will be the type
and 1
the
payload sent to the reducer when the event happens.
By now it seems this is just a simplified version of the above, and in practice it is. But you'll learn in the next section that this builds a signal. We'll look into signals in the next section, but I want to show you an even simpler version of that below, that will not require us to define any reducer:
import main from '@act/main'
import count from '@act/main/processes/count'
const view = (model) => ['button', {click: {add: count}}, model]
main(view, { model: 0 })
This works, first of all, because if you don't pass any reducer to main
, it
will use a default reducer that always override the state with any action
payload sent. And second, because count
is a process that uses fold
behinds
the scene to accumulate. But I guess we dug too deep. Now go to the
signals and this will be clearer.