-
Notifications
You must be signed in to change notification settings - Fork 18
Operation Anatomy
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 combine in one place the notion of Action, Action Creator, and Dispatcher from Flux.
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:
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.
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