DisplayGamecomponent. State was updated by "bubbling up" events from lower level components up to
DisplayGamewhere the event handler updated the state.
DisplayGamecomponent is responsible for both managing state and displaying the game.
Hyperstack::State::Observablemodule allows us to put the game's state into a separate class which can be accessed from any component: No more need to bubble up events, and no more cluttering up our
DisplayGamecomponent with state management and details of the game's data structure.
DisplayGamecomponent into its own class:
Gameis now in its own class and includes
Hyperstack::State::Observable. This adds a number of methods to
Gamethat allows our class to become a reactive store. When
Gameinteracts with other stores and components they will be updated as the state of
before_mountcallback. The same initialization is now in the
initializemethod which will be called when a new instance of the game is created. This will still be done in the
before_mountcallback (see below.)
current. Now that
Gameis a separate class we define these methods using
observermethod creates a method that is the inverse of
mutator) indicate that state has been changed
observerindicate that state has been accessed outside the class.
state_writer, we have
current_winner?. It accesses the internal state through the
currentmethod so there is no need to explicitly make
current_winner?an observer (but we could, without affecting anything.)
before_mountcallback is still responsible for initializing the game, but it no longer needs to be aware of the internals of the game's state. It simply calls
Game.newand stores the result in the
@gameinstance variable. For the rest of the component's code we call the appropriate method on
DisplayBoard(we will see why shortly) so we will rename it to
DisplayCurrentBoardwill be responsible for directly notifying the game that a user has clicked, so we do not need to handle any events coming back from
DisplayCurrentBoardcomponent receives the entire game, and it will access the current board, using the
currentmethod, and will directly notify the game when a user clicks using the
DisplayCurrentBoarddirectly deal with user actions, we simplify both components as they do not have to communicate back upwards via events. Instead we communicate through the central game store.
Gamestore holds the state, the top level component reads the state and sends it down to lower level components, those components update the
Gamestate causing the top level component to re-rerender.
current_winner?now are neatly abstracted out into their own class.
Gamewith class methods like this:
Hyperstack::State::Observablemodule will call any class level
initializemethods in the class or subclasses before the first component mounts.
DisplayBoardcan directly access
Game.handle_click!since there is only one game.
mutatemethods marks when its internal data has been observed by some other class, or when its internal data has changed.
state_accessormethods. Likewise a method that changes internal state must declare this using
DisplayBoardto demonstrate this:
DisplayBoardno longer takes any parameter (and could be renamed again to
DisplayCurrentBoard) and now a new component -
DisplaySquare- takes the id of the square to display, but the game or the current board are never passed as parameters; there is no need to as they are implicit.
observe, observer, state_reader
observemethod takes any number of arguments and/or a block. The last argument evaluated or the value of the block is returned.
observermethod defines a new method with an implicit observe:
breakthe state will not be observed.
state_readermethod declares one or more state accessors with an implicit state observation:
mutate, mutator, state_writer, toggle
mutatemethod takes any number of arguments and/or a block. The last argument evaluated or the value of the block is returned.
mutatormethod defines a new method with an implicit mutate:
breakthe state will not be mutated.
state_writermethod declares one or more state accessors with an implicit state mutation:
togglemethod reverses the polarity of a instance variable:
HyperComponentbase class includes
HyperComponenthas access to all of the above methods. A component also always observes itself so you never need to use
observewithin a component unless the state will be accessed outside the component. However once you start doing that you would be better off to move the state into a separate store.
In addition components also act as the Observers in the system. What this means is that the current component that is running its render method is recording all stores that call
observe, when a store mutates, then all the components that recorded observations will be rerendered.