Skip to content
This repository has been archived by the owner on Oct 19, 2018. It is now read-only.

Operation Anatomy

Mitch VanDuyn edited this page Jan 25, 2017 · 1 revision

Operations

Operations contain the dynamic logic of the Application. They are small bits of logic that perform some operation that usually will result in the Operation sending new data to one or more of your Stores.

Operations are Like Flux Action Creators

Operations combine in one place the notion of Action, Action Creator, and Dispatcher from Flux.

Operations Update Stores

In our WorldClock app we hard coded the details of New York and London. Lets first move this to another Store we will call Location

class Location < HyperStore
  LOCATIONS = {
    'New York' =>      [40.7128,  -74.0059, 5.hours],
    'London' =>        [51.5074,  -0.1278, 0],
    'Mumbai' =>        [19.0760,  72.8777, 5.hours+30.minutes],
    'Bangkok' =>       [13.7563,  100.5018, 7.hours],
    'Sydney' =>        [-33.8688, 151.2093, 11.hours],
    'San Francisco' => [37.7749,  122.4194, -8],
    'Buenos Aires' =>  [-34.6037, -58.3816, -3]
  ]
  def self.all 
    LOCATIONS
  end
end

questions and answers:

http://stackoverflow.com/questions/25630611/should-flux-stores-or-actions-or-both-touch-external-services

When responding to a user's interaction with the UI, I would make the async call in the action creator methods. But when you have a ticker or some other non-human driver, a call from the store works better. The important thing is to create an action in the error/success callback so data always originates with actions quote from Bill Fisher

http://stackoverflow.com/questions/35667249/accessing-redux-state-in-an-action-creator

In general accessing state in action creators is an anti-pattern and you should avoid this when possible. The few use cases where I think it’s acceptable is for checking cached data before you make a request, or for checking whether you are authenticated (in other words, doing a conditional dispatch). Passing data such as state.something.items in an action creator is definitely an anti-pattern and is discouraged because it obscured the change history: if there is a bug and items are incorrect, it is hard to trace where those incorrect values come from because they are already part of the action, rather than directly computed by a reducer in response to an action. So do this with care. implication is that its fine for stores to invoke action creators quote from Dan Abramov

It is easy to bind user input events in Components. For example, let's allow the user to select from different cities and add those WorldClocks to the UI dynamically.

First, we will need a simple static class to return a sample list of locations:

class Location
  LOCATIONS = {
    'New York' =>      [40.7128,  -74.0059, 5.hours],
    'London' =>        [51.5074,  -0.1278, 0],
    'Mumbai' =>        [19.0760,  72.8777, 5.hours+30.minutes],
    'Bangkok' =>       [13.7563,  100.5018, 7.hours],
    'Sydney' =>        [-33.8688, 151.2093, 11.hours],
    'San Francisco' => [37.7749,  122.4194, -8],
    'Buenos Aires' =>  [-34.6037, -58.3816, -3]
  ]
  def self.all 
    LOCATIONS
  end
end
class AddClock < Operation
  param :name
  def execute 
    WorldClock.new params.name, *Locations.all[params.name]
  end
end

Now let's define our application:

class App < React::Component::Base

  def remaining_locations
    Location.all.keys.select do |location|
      WordClock.all.detect { |clock| clock.name == location }
    end
  end

  def add_clock(name)
    
  end

  def build_select 
    SELECT do
      remaining_locations do |name|
        OPTION(value: name) { name }
      end
    end.on(:change) { |e| add_clock e.value }
  end
    
  render(DIV)
    build_select unless @locations.empty?
    WorldClock.all.each { |clock| DisplayTime clock: clock }
  end
end

When we generate the SELECT we bind the change event to the block

{ |e| add_clock e.value }

which will remove the selected clock from the @locations list, and create a new clock. WorldClock's all method will be updated causing a re-render with the new clock being displayed.

Events Always Update State

As we have seen an incoming event will most certainly update state somewhere in your system. Where you put that state and how you update it are important design considerations. Hyperloop gives you choices but stays out of the way.

  • If your state is completely local to the component - for example it is tracking the value of a text field - then it makes sense to use a private state directly in the component.
  • If the event triggers a single logical action - like adding a new clock - which is handled by a Store's protocol then you may consider directly update the store.
  • If the event triggers a more complex operation - like getting data from an API and transferring it to a Store - then define an Operation for this purpose.
  • If the event needs to broadcast to many stores - like clicking a "Restore Defaults" button - then again create an Operation for this purpose.

When in doubt create an Operation. Consider our example above: In all likelyhood

Clone this wiki locally