Ovto::Actions

Actions are the only way to change the state. Actions must be defined as methods of the Actions class. Here is some more conventions:

  • You must use keyword arguments
  • You must return state updates as a Hash. It will be merged into the app state.
  • You can get the current state by state method

Example:

require 'opal'
require 'ovto'

class MyApp < Ovto::App
  class State < Ovto::State
    item :count, default: 0
  end

  class Actions < Ovto::Actions
    def increment(by:)
      return {count: state.count + by}
    end
  end

  class MainComponent < Ovto::Component
    def render
      o 'span', state.count
      o 'button', onclick: ->{ actions.increment(by: 1) }
    end
  end
end

MyApp.run(id: 'ovto')

Calling actions

Actions can be called from components via actions method. This is an instance of Ovto::WiredActions and has methods of the same name as your Actions class.

  o 'button', onclick: ->{ actions.increment(by: 1) }

Arguments are almost the same as the original but you don't need to provide state; it is automatically passed by Ovto::WiredActions class. It also updates the app state with the return value of the action, and schedules rendering the view.

Skipping state update

An action may return nil when no app state changes are needed.

Promises are also special values which does not cause state changes (see the next section).

Async actions

When calling server apis, you cannot tell how the app state will change until the server responds. In such cases, you can call another action via actions to tell Ovto to reflect the api result to the app state.

Example:

  class Actions < Ovto::Actions
    def fetch_tasks
      Ovto.fetch('/tasks.json').then {|tasks_json|
        actions.receive_tasks(tasks: tasks_json)
      }.fail {|e|
        console.log("failed:", e)
      }
    end

    def receive_tasks(tasks_json:)
      tasks = tasks_json.map{|item| Task.new(**item)}
      return {tasks: tasks}
    end
  end