Fable.Reaction Release 2.1.0
Total Page:16
File Type:pdf, Size:1020Kb
Fable.Reaction Release 2.1.0 Oct 24, 2020 Contents 1 Fable Reaction 3 1.1 Introduction...............................................3 1.2 Getting Started..............................................3 1.3 Install...................................................5 1.4 Reactive MVU Architecture.......................................5 1.5 Examples.................................................6 1.6 WebSocket................................................7 1.7 Demo...................................................7 2 AsyncRx 9 2.1 Introduction...............................................9 2.2 Install...................................................9 2.3 Operators................................................. 10 2.4 Types................................................... 15 2.5 Disposables................................................ 17 2.6 Query Builder.............................................. 18 2.7 Subjects.................................................. 19 2.8 Error Handling.............................................. 20 2.9 Custom Observables........................................... 20 3 Indices and tables 23 Index 25 i ii Fable.Reaction, Release 2.1.0 Fable Reaction is a collection of projects that combines the power of asynchronous reactive (AsyncRx) functional programming (RFP) with F#, Fable and Elmish applications. Contents 1 Fable.Reaction, Release 2.1.0 2 Contents CHAPTER 1 Fable Reaction Fable Reaction is a library combining reactive programming with the model view update (MVU) style architecture. 1.1 Introduction Fable.Reaction is a library combining reactive programming with the model view update (MVU) style architecture. Fable.Reaction builds on React Hooks and can perform any task. Think of them as live components that you can include anywhere in your view or be mounted anywhere on the page. Fable.Reaction can easily be used together with Fable.Elmish, but have no dependency on Elmish itself. Views and components built using Fable.Reaction can be included in Elmish views or mounted by themselves anywhere on the page. 1.2 Getting Started To use Fable.Reaction you need to create an instance of a StreamView. The StreamView works almost the same way as an Elmish application taking an initial model, a view and and update function. In addition it takes a stream function that transforms the stream of dispatched messages before they reach the update function. open FSharp.Control.AsyncRx// 1. Enable AsyncRx open Fable.Reaction// 2. Enable Fable Reaction let update (currentModel : Model) (msg : Msg) : Model= ... let view (model : Model) (dispatch : (Msg-> unit))= ... let stream model msgs=// 3. Add the reactive stream msgs |> AsyncRx.delay 1000 (continues on next page) 3 Fable.Reaction, Release 2.1.0 (continued from previous page) |> AsyncRx.toStream"msgs" let app= Reaction.StreamView initialModel view update stream mountById"reaction-app" (ofFunction app () []) 1.2.1 Loading initial State To load initial state from the server without using commands (Cmd) you create an Async Observable using ofPromise and then concat the result into the message stream. Thus the message stream in the example below will start with the initialCountLoaded message. // Add open statements to top of file open Fable.Reaction let loadCount= ofPromise (fetchAs<int>"/api/init" []) |> AsyncRx.map (Ok >> InitialCountLoaded) |> AsyncRx.catch (Error >> InitialCountLoaded >> single) |> AsyncRx.toStream"loading" let stream model msgs= match model with | Loading-> loadCount |_-> msgs 1.2.2 Using Fable.Reaction with Elmish Fable.Reaction can be used all by itself without Elmish. But if you want to use Fable Reaction with Elmish you just add the Fable.Reaction component to the Elmish view like any other element such as e.g div and str. A Fable.Reaction component produces a ReactElement that can be used anywhere in your view such as with the autocomplete component below. let view (model: Model) (dispatch : Dispatch<Msg>)= Container.container [] [ h1 [] [ str"Search Wikipedia" ] autocomplete { Search=searchWikipedia; Dispatch= Select >> dispatch; ,!DebounceTimeout=750} div [ Style [ MarginTop"30px"]][ match model.Selection with | Some selection-> yield str"Selection:" yield str selection | None ->() ] ] 4 Chapter 1. Fable Reaction Fable.Reaction, Release 2.1.0 1.2.3 Doing side effects per message In the example below we flat map (map and merge) the result of querying Wikipedia back into the message stream. The flatMapLatest operator is a combination of the map and switchLatest operators. This operator works like flatMap but will auto-cancel any ongoing fetch operation if a new query is made before the previous result is ready. // Add open statements to top of file open Fable.Reaction let stream model msgs= msgs |> AsyncRx.choose Msg.asKeyboardEvent |> AsyncRx.map targetValue |> AsyncRx.filter (fun term-> term.Length>2|| term.Length=0) |> AsyncRx.debounce 750// Pause for 750ms |> AsyncRx.distinctUntilChanged// Only if the value has changed |> AsyncRx.flatMapLatest searchWikipedia |> AsyncRx.toStream"msgs" 1.3 Install Fable.Reaction is available as a NuGet package for NETStandard 2.0. Fable.Rection is the new release for Fable ~> 3 and follows the Fable versioning scheme, i.e if you use Fable.Core ~> 3, you should use Fable.Reaction ~> 3. 1.3.1 Paket > paket add Fable.Reaction 1.3.2 NuGet > dotnet add package Fable.Reaction 1.4 Reactive MVU Architecture Fable.Reaction is very similar to Elm and Elmish in regards to the MVU architecture. But when using Fable.Reaction, we do not need any commands (Cmd) or subscriptions with Elmish. Instead we use a ReactiveX (AsyncRx) style query that transforms the stream of messages (Msg). 1.3. Install 5 Fable.Reaction, Release 2.1.0 • Model, application state as immutable data • View, a pure function that takes the model to produce the output view (HTML elements) • Message, a data event that represents a change. Messages are generated by the view, or they may be generated by a stream, e.g. timer or initial message events. • Update, a pure function that produces a new model based on a received message and the previous model In addition to the normal MVU architecture, Fable.Reaction also have a stream that transforms the “stream” of mes- sages going from the view to the update function. • Stream, a function that takes the current model and the message stream and produces a new (transformed) message stream. Note that this also replaces the need for Elm(ish) commands (Cmd) since the stream is free to produce any messages (out of thin air), transform, filter, time-shift messages or combine side-effects such as web requests (fetch) etc. 1.5 Examples Examples of how to use Fable Reaction can be found in the examples folder. Current list of examples includes: • Counter, from the SAFE stack template. • Timeflies. See description below. • Autocomplete • Dragndrop • Magic The Timeflies example (source code) implements the classic Time Flies) example from RxJS. The Magic example (source code) shows howto scale Fable.Reaction using multiple sub-components. 6 Chapter 1. Fable Reaction Fable.Reaction, Release 2.1.0 1.6 WebSocket Fable.Reaction enables you to route stream of messages to the server and back again using “message channels”. Note that server side support for WebSocket message handling must also be in place using Reaction. AspNetCoreMiddleWare. 1.6.1 Message Channel A message channel is an AsyncRx operator that composes a WebSocket stream of messages into the reactive query. This basically routes the messages to the server and (possibly) back again. A message channnel works like this: • Every message that enters the operator will be sent to the server over a WebSocket. • Every message received from the server will be pushed to observers or the next composed operator. This enables us to compose the message channel into the qury without resorting to imperative programming. val msgChannel<'msg>: uri:string -> encode:('msg -> string) -> decode:(string -> 'msg option) -> source:IAsyncObservable<'msg> -> IAsyncObservable<'msg> Example let stream (msgs: Stream<Msgs, string>)= msgs |> AsyncRx.msgChannel"ws://localhost:8085/ws" Msg.Encode Msg.Decode |> AsyncRx.toStream"msgs" 1.7 Demo 1.7.1 Time Flies Like an Arrow The Timeflies example (source code) implements the classic Time Flies example from RxJS. In the Time files demo the stream of mouse moves are transformed into a stream of letters where each letter is delayed according to its position. The Model type holds data that you want to keep track of while the application is running. type Model={ Letters: Map<int, string * int * int> } The Msg type defines what events/actions can occur while the application is running. The state of the application changes only in reaction to these events type Msg= | Letter of int * string * int * int The update function computes the next state of the application based on the current state and the incoming messages let update (msg : Msg) (currentModel : Model) : Model= match currentModel.Letters, msg with | _, Letter (i, c, x, y)-> { currentModel with Letters= currentModel.Letters.Add (i, (c, x, y)) } 1.6. WebSocket 7 Fable.Reaction, Release 2.1.0 The view function renders the model and the result will be handled over to React. It produces a div element and then takes each letter in the Map and adds a span element containing the letter. The span element will have the top/left properties set so that the letter is rendered at the correct position on the page. let view (model : Model) (dispatch : Dispatch<Msg>)= let letters= model.Letters