React-Redux TodoMVC with Fable

Use Redux development tools with Fable

This is a port of React-Redux TodoMVC to show how easy is to take advantage of the full power of React and Redux in Fable apps. You can also compare the F# source code with the original JS implementation to see the advantages of Fable programming. And you can install the Redux DevTools to get the full debugging experience of modern JS development with Fable!

JavaScript bindings

Fable includes React bindings and helpers to make interaction with the tool more idiomatic in F#.

1: 
2: 
3: 
#r "node_modules/fable-core/Fable.Core.dll"
#load "node_modules/fable-import-react/Fable.Import.React.fs"
#load "node_modules/fable-import-react/Fable.Helpers.React.fs"

Redux helper

Fable's support for Redux is experimental and there're no bindings available. In order to use Redux in you Fable apps, just copy the code of the module below and be sure to add Redux as a JS dependency.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
module Redux =
    open System
    open Fable.Import
    open Fable.Core
    open Fable.Core.JsInterop

    type IStore<'TState, 'TAction> = interface end

    let [<Import("createStore","redux")>] private createStore' = obj()

    let createStore (reducer: 'TState->'TAction->'TState) (initState: 'TState) =
        // Check if the action is a union type before applying the reducer
        let reducer = fun state action ->
            match box action?Case with
            | :? string -> reducer state action
            | _ -> state
        match unbox Browser.window?devToolsExtension with
        | Some ext -> createStore'$(Func<_,_,_> reducer, initState, ext$())
        | None -> createStore'$(Func<_,_,_> reducer, initState)
        |> unbox<IStore<'TState, 'TAction>>

    let dispatch (store: IStore<'TState, 'TAction>) (x: 'TAction) =
        let x = toPlainJsObj x
        x?``type`` <- x?Case
        store?dispatch(x) |> ignore

    let inline subscribe (store: IStore<'TState, 'TAction>) (f: unit->unit) =
        store?subscribe(f) |> ignore

    let inline getState (store: IStore<'TState, 'TAction>) =
        store?getState() |> unbox<'TState>

Domain models

Open the necessary namespaces, define our domain models (Todo, TodoAction, TodoFilter) and some utilities (key codes, classNames).

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
open System
open Fable.Core
open Fable.Core.JsInterop
open Fable.Import

module R = Fable.Helpers.React
open R.Props

type Todo = { Text: string; Completed: bool; Id: int } 

type TodoAction =
    | AddTodo of text:string
    | DeleteTodo of id:int
    | EditTodo of id:int * text:string
    | CompleteTodo of id:int
    | CompleteAll
    | ClearCompleted

type TodoFilter =
    | ShowAll = 0
    | ShowActive = 1
    | ShowCompleted = 2

let [<Literal>] ESCAPE_KEY = 27.
let [<Literal>] ENTER_KEY = 13.

let classNames =
    List.choose (fun (txt,add) -> if add then Some txt else None)
    >> String.concat " "

React components

Define React components in the same way as explained in React guide but with statically checked props and state types and the DSL from the Fable React helper. Check the render method of each component and the Fable React TodoMVC sample for details, or scroll down to see how to subscribe React components to the Redux store.

  1: 
  2: 
  3: 
  4: 
  5: 
  6: 
  7: 
  8: 
  9: 
 10: 
 11: 
 12: 
 13: 
 14: 
 15: 
 16: 
 17: 
 18: 
 19: 
 20: 
 21: 
 22: 
 23: 
 24: 
 25: 
 26: 
 27: 
 28: 
 29: 
 30: 
 31: 
 32: 
 33: 
 34: 
 35: 
 36: 
 37: 
 38: 
 39: 
 40: 
 41: 
 42: 
 43: 
 44: 
 45: 
 46: 
 47: 
 48: 
 49: 
 50: 
 51: 
 52: 
 53: 
 54: 
 55: 
 56: 
 57: 
 58: 
 59: 
 60: 
 61: 
 62: 
 63: 
 64: 
 65: 
 66: 
 67: 
 68: 
 69: 
 70: 
 71: 
 72: 
 73: 
 74: 
 75: 
 76: 
 77: 
 78: 
 79: 
 80: 
 81: 
 82: 
 83: 
 84: 
 85: 
 86: 
 87: 
 88: 
 89: 
 90: 
 91: 
 92: 
 93: 
 94: 
 95: 
 96: 
 97: 
 98: 
 99: 
100: 
101: 
102: 
103: 
104: 
105: 
106: 
107: 
108: 
109: 
110: 
111: 
112: 
113: 
114: 
115: 
116: 
117: 
118: 
119: 
120: 
121: 
122: 
123: 
124: 
125: 
126: 
127: 
128: 
129: 
130: 
131: 
132: 
133: 
134: 
135: 
136: 
137: 
138: 
139: 
140: 
141: 
142: 
143: 
144: 
145: 
146: 
147: 
148: 
149: 
150: 
151: 
152: 
153: 
154: 
155: 
156: 
157: 
158: 
159: 
160: 
161: 
162: 
163: 
164: 
165: 
166: 
167: 
168: 
169: 
170: 
171: 
172: 
173: 
174: 
175: 
176: 
177: 
178: 
179: 
180: 
181: 
182: 
183: 
184: 
185: 
186: 
187: 
188: 
189: 
190: 
191: 
192: 
193: 
194: 
195: 
196: 
197: 
198: 
199: 
200: 
201: 
202: 
203: 
204: 
205: 
206: 
207: 
208: 
209: 
210: 
211: 
212: 
213: 
214: 
215: 
216: 
217: 
218: 
219: 
220: 
221: 
222: 
223: 
224: 
225: 
226: 
227: 
type TodoTextInputProps =
    abstract OnSave: string->unit
    abstract Text: string option
    abstract Placeholder: string
    abstract Editing: bool
    abstract NewTodo: bool

type TodoTextInputState = { Text: string }

type TodoTextInput(props, ctx) as this =
    inherit React.Component<TodoTextInputProps, TodoTextInputState>(props, ctx)
    do this.state <- { Text = defaultArg props.Text "" } 

    member this.HandleSubmit(e: React.KeyboardEvent) =
        if e.which = ENTER_KEY then
            let text = (unbox<string> e.target?value).Trim()
            this.props.OnSave(text)
            if this.props.NewTodo then
                this.setState({ Text = "" })

    member this.HandleChange(e: React.SyntheticEvent) =
        this.setState({ Text=unbox e.target?value })

    member this.HandleBlur(e: React.SyntheticEvent) =
        if not this.props.NewTodo then
            this.props.OnSave(unbox e.target?value)

    member this.render() =
        R.input [
            ClassName(
                classNames [
                    "edit", this.props.Editing
                    "new-todo", this.props.NewTodo
                ])
            Type "text"
            OnBlur this.HandleBlur
            OnChange this.HandleChange
            OnKeyDown this.HandleSubmit
            AutoFocus (this.state.Text.Length > 0)
            Placeholder this.props.Placeholder
        ] []

type TodoItemProps =
    abstract Todo: Todo
    abstract EditTodo: int * string -> unit
    abstract DeleteTodo: int -> unit
    abstract CompleteTodo: int -> unit

type TodoItemState = { Editing: bool }

type TodoItem(props, ctx) as this =
    inherit React.Component<TodoItemProps, TodoItemState>(props, ctx)
    do this.state <- { Editing = false } 

    member this.HandleDoubleClick(_) =
        this.setState({ Editing = true })

    member this.HandleSave(id, text: string) =
        if text.Length = 0
        then this.props.DeleteTodo(id)
        else this.props.EditTodo(id, text)
        this.setState({ Editing = false })

    member this.render() =
        let element =
            if this.state.Editing
            then R.com<TodoTextInput,_,_>
                    { new TodoTextInputProps with
                        member __.OnSave(text: string) =
                            this.HandleSave(this.props.Todo.Id, text)
                        member __.Editing = this.state.Editing
                        member __.Text = Some this.props.Todo.Text
                        member __.Placeholder = ""
                        member __.NewTodo = false } []
            else R.div [ClassName "view"] [
                    R.input [
                        ClassName "toggle"
                        Type "checkbox"
                        Checked this.props.Todo.Completed
                        OnChange (fun _ ->
                            this.props.CompleteTodo(this.props.Todo.Id))
                    ] []
                    R.label [OnDoubleClick this.HandleDoubleClick]
                            [unbox this.props.Todo.Text]
                    R.div [
                        ClassName "destroy"
                        OnClick (fun _ ->
                            this.props.DeleteTodo(this.props.Todo.Id))
                    ] []
                ]
        R.li [ClassName(
                classNames [
                    "completed", this.props.Todo.Completed
                    "editing", this.state.Editing])]
             [element]

type HeaderProps = { AddTodo: string->unit }

let Header (props: HeaderProps) =
    R.header [ClassName "header"] [
        R.h1 [] [unbox "todos"]
        R.com<TodoTextInput,_,_>
            { new TodoTextInputProps with
                member __.OnSave(text: string) =
                    if text.Length <> 0 then
                        props.AddTodo text
                member __.Placeholder = "What needs to be done?"
                member __.NewTodo = true
                member __.Text = None
                member __.Editing = false } []
    ]

type FooterProps =
    abstract ActiveCount: int
    abstract CompletedCount: int
    abstract Filter: TodoFilter
    abstract OnShow: TodoFilter->unit
    abstract OnClearCompleted: React.SyntheticEvent->unit

let Footer =
    let filterTitles =
        dict [
            TodoFilter.ShowAll, "All"
            TodoFilter.ShowActive, "Active"
            TodoFilter.ShowCompleted, "Completed"
        ]
    let renderTodoCount activeCount =
        R.span [ClassName "todo-count"] [
            unbox(sprintf "%s item%s left"
                (if activeCount > 0 then string activeCount else "No")
                (if activeCount <> 1 then "s" else ""))
        ]
    let renderFilterLink filter selectedFilter onShow =
        R.a [
            ClassName (classNames ["selected", filter = selectedFilter])
            Style [unbox("cursor", "pointer")]
            OnClick (fun _ -> onShow filter)
        ] [unbox filterTitles.[filter]]
    let renderClearButton completedCount onClearCompleted =
        if completedCount > 0
        then R.button [
                ClassName "clear-completed"
                OnClick onClearCompleted
             ] [unbox "Clear completed"] |> Some
        else None
    fun (props: FooterProps) ->
        let listItems =
            [ TodoFilter.ShowAll
              TodoFilter.ShowActive
              TodoFilter.ShowCompleted ]
            |> List.map (fun filter ->
                [renderFilterLink filter props.Filter props.OnShow]
                |> R.li [Key (string filter)])
        R.footer [ClassName "footer"] [
            renderTodoCount props.ActiveCount
            R.ul [ClassName "filters"] listItems
            // We can pass option types as React children but we
            // need to use `unbox` to appease the F# compiler
            unbox(renderClearButton props.CompletedCount props.OnClearCompleted)
        ]

type MainSectionProps = { Todos: Todo[]; Dispatch: TodoAction->unit }
type MainSectionState = { Filter: TodoFilter }

type MainSection(props, ctx) as this =
    inherit React.Component<MainSectionProps, MainSectionState>(props, ctx)
    let todoFilters =
        dict [
            TodoFilter.ShowAll, fun _ -> true
            TodoFilter.ShowActive, fun (todo: Todo) -> not todo.Completed
            TodoFilter.ShowCompleted, fun todo -> todo.Completed
        ]
    do this.state <- { Filter = TodoFilter.ShowAll } 

    member this.HandleClearCompleted() =
        this.props.Dispatch(ClearCompleted)

    member this.HandleShow(filter) =
        this.setState({ Filter = filter })

    member this.renderToggleAll(completedCount) =
        if this.props.Todos.Length > 0
        then R.input [
                ClassName "toggle-all"
                Type "checkbox"
                Checked (completedCount = this.props.Todos.Length)
                OnChange (fun _ -> this.props.Dispatch(CompleteAll))
             ] [] |> Some
        else None

    member this.renderFooter(completedCount) =
        if this.props.Todos.Length > 0
        then R.fn Footer
                { new FooterProps with
                    member __.ActiveCount =
                        this.props.Todos.Length - completedCount
                    member __.CompletedCount = completedCount
                    member __.Filter = this.state.Filter
                    member __.OnShow filter = this.HandleShow filter
                    member __.OnClearCompleted _ =
                        this.HandleClearCompleted() } [] |> Some
        else None

    member this.render() =
        let filteredTodos =
            this.props.Todos
            |> Array.filter todoFilters.[this.state.Filter]
            |> Array.toList
        let completedCount =
            (0, this.props.Todos) ||> Array.fold (fun count todo ->
                if todo.Completed then count + 1 else count)
        R.section [ClassName "main"] [
            unbox(this.renderToggleAll completedCount)
            R.ul [ClassName "todo-list"]
                (filteredTodos
                |> List.map (fun todo ->
                    R.com<TodoItem,_,_>
                        { new TodoItemProps with
                            member __.Todo = todo
                            member __.EditTodo(id, text) =
                                this.props.Dispatch(EditTodo(id, text))
                            member __.DeleteTodo(id) =
                                this.props.Dispatch(DeleteTodo id)
                            member __.CompleteTodo(id) =
                                this.props.Dispatch(CompleteTodo id) } []))
            unbox(this.renderFooter completedCount)
        ]

Subscription to Redux store

The component on top of our UI hierarchy subscribes to the Redux store and will automatically invalidate children rendering as needed. React checks when DOM updates are actually necessary, so we don't need to worry about the performance hit of too frequent updates.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
type AppProps = { Store: Redux.IStore<Todo[], TodoAction> }

type App(props, ctx) as this =
    inherit React.Component<AppProps, MainSectionProps>(props, ctx)
    let dispatch = Redux.dispatch props.Store
    let getState() = { Todos=Redux.getState props.Store; Dispatch=dispatch }
    do this.state <- getState()
    do Redux.subscribe props.Store (getState >> this.setState)

    member this.render() =
        R.div [] [
            R.fn Header { AddTodo = AddTodo >> dispatch } []
            R.com<MainSection,_,_> this.state []
        ]

Reducer

The reducer is a single function (which can be composed of other smaller function) with the responsibility of updating the state in reaction to the actions dispatched to the Redux store. F# union types and pattern matching makes it really easy to identify and extract the data from the received actions in a type-safe manner. The compiler will even warn us if we forget to handle any of the possible TodoAction cases.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
let reducer (state: Todo[]) = function
    | AddTodo text ->
        let id =
            (-1, state)
            ||> Array.fold(fun id todo -> max id todo.Id)
            |> (+) 1
        state
        |> Array.append [|{Id=id; Completed=false; Text=text}|]
    | DeleteTodo id ->
        state
        |> Array.filter(fun todo -> todo.Id <> id) 
    | EditTodo(id, text) ->
        state
        |> Array.map(fun todo ->
            if todo.Id = id
            then { todo with Text=text }
            else todo)
    | CompleteTodo id ->
        state
        |> Array.map(fun todo ->
            if todo.Id = id
            then { todo with Completed=not todo.Completed }
            else todo)
    | CompleteAll ->
        let areAllMarked =
            state |> Array.forall(fun todo -> todo.Completed)
        state
        |> Array.map(fun todo -> { todo with Completed=not areAllMarked})
    | ClearCompleted ->
        state
        |> Array.filter(fun todo -> not todo.Completed) 

Firing up the app

There's nothing left to do but create the Redux store with the reducer and the initial state, pass it to our App component, and mount it onto the DOM by using ReactDom.render. Happy coding!

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
let store =
    { Text="Use Fable + React + Redux"; Completed=false; Id=0}
    |> Array.singleton
    |> Redux.createStore reducer

ReactDom.render(
    R.com<App,_,_> { Store=store } [],
    Browser.document.getElementsByClassName("todoapp").[0]
) |> ignore
namespace System
namespace Fable
namespace Fable.Import
namespace Fable.Core
module JsInterop

from Fable.Core
type IStore<'TState,'TAction>

Full name: Redux-todomvc.Redux.IStore<_,_>
Multiple items
type ImportAttribute =
  inherit Attribute
  new : get:string * from:string -> ImportAttribute

Full name: Fable.Core.ImportAttribute

--------------------
new : get:string * from:string -> ImportAttribute
val private createStore' : Object

Full name: Redux-todomvc.Redux.createStore'
type obj = Object

Full name: Microsoft.FSharp.Core.obj
val createStore : reducer:('TState -> 'TAction -> 'TState) -> initState:'TState -> IStore<'TState,'TAction>

Full name: Redux-todomvc.Redux.createStore
val reducer : ('TState -> 'TAction -> 'TState)
val initState : 'TState
val state : 'TState
val action : 'TAction
val box : value:'T -> obj

Full name: Microsoft.FSharp.Core.Operators.box
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = String

Full name: Microsoft.FSharp.Core.string
val unbox : value:obj -> 'T

Full name: Microsoft.FSharp.Core.Operators.unbox
module Browser

from Fable.Import
val window : Browser.Window

Full name: Fable.Import.Browser.window
union case Option.Some: Value: 'T -> Option<'T>
val ext : obj
Multiple items
type Func<'TResult> =
  delegate of unit -> 'TResult

Full name: System.Func<_>

--------------------
type Func<'T,'TResult> =
  delegate of 'T -> 'TResult

Full name: System.Func<_,_>

--------------------
type Func<'T1,'T2,'TResult> =
  delegate of 'T1 * 'T2 -> 'TResult

Full name: System.Func<_,_,_>

--------------------
type Func<'T1,'T2,'T3,'TResult> =
  delegate of 'T1 * 'T2 * 'T3 -> 'TResult

Full name: System.Func<_,_,_,_>

--------------------
type Func<'T1,'T2,'T3,'T4,'TResult> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 -> 'TResult

Full name: System.Func<_,_,_,_,_>

--------------------
type Func<'T1,'T2,'T3,'T4,'T5,'TResult> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 -> 'TResult

Full name: System.Func<_,_,_,_,_,_>

--------------------
type Func<'T1,'T2,'T3,'T4,'T5,'T6,'TResult> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 -> 'TResult

Full name: System.Func<_,_,_,_,_,_,_>

--------------------
type Func<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'TResult> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 -> 'TResult

Full name: System.Func<_,_,_,_,_,_,_,_>

--------------------
type Func<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'TResult> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 -> 'TResult

Full name: System.Func<_,_,_,_,_,_,_,_,_>

--------------------
type Func<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'TResult> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 -> 'TResult

Full name: System.Func<_,_,_,_,_,_,_,_,_,_>

--------------------
type Func<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'TResult> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 -> 'TResult

Full name: System.Func<_,_,_,_,_,_,_,_,_,_,_>

--------------------
type Func<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'TResult> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 -> 'TResult

Full name: System.Func<_,_,_,_,_,_,_,_,_,_,_,_>

--------------------
type Func<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'TResult> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 -> 'TResult

Full name: System.Func<_,_,_,_,_,_,_,_,_,_,_,_,_>

--------------------
type Func<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13,'TResult> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 * 'T13 -> 'TResult

Full name: System.Func<_,_,_,_,_,_,_,_,_,_,_,_,_,_>

--------------------
type Func<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13,'T14,'TResult> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 * 'T13 * 'T14 -> 'TResult

Full name: System.Func<_,_,_,_,_,_,_,_,_,_,_,_,_,_,_>

--------------------
type Func<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13,'T14,'T15,'TResult> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 * 'T13 * 'T14 * 'T15 -> 'TResult

Full name: System.Func<_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_>

--------------------
type Func<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'T8,'T9,'T10,'T11,'T12,'T13,'T14,'T15,'T16,'TResult> =
  delegate of 'T1 * 'T2 * 'T3 * 'T4 * 'T5 * 'T6 * 'T7 * 'T8 * 'T9 * 'T10 * 'T11 * 'T12 * 'T13 * 'T14 * 'T15 * 'T16 -> 'TResult

Full name: System.Func<_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_>
union case Option.None: Option<'T>
val dispatch : store:IStore<'TState,'TAction> -> x:'TAction -> unit

Full name: Redux-todomvc.Redux.dispatch
val store : IStore<'TState,'TAction>
val x : 'TAction
val x : obj
val toPlainJsObj : o:'T -> obj

Full name: Fable.Core.JsInterop.toPlainJsObj
val ignore : value:'T -> unit

Full name: Microsoft.FSharp.Core.Operators.ignore
val subscribe : store:IStore<'TState,'TAction> -> f:(unit -> unit) -> unit

Full name: Redux-todomvc.Redux.subscribe
val f : (unit -> unit)
type unit = Unit

Full name: Microsoft.FSharp.Core.unit
val getState : store:IStore<'TState,'TAction> -> 'TState

Full name: Redux-todomvc.Redux.getState
module React

from Fable.Helpers
namespace Fable.Helpers
module Props

from Fable.Helpers.React
type Todo =
  {Text: string;
   Completed: bool;
   Id: int;}

Full name: Redux-todomvc.Todo
Multiple items
Todo.Text: string

--------------------
namespace System.Text
Todo.Completed: bool
type bool = Boolean

Full name: Microsoft.FSharp.Core.bool
Todo.Id: int
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
type TodoAction =
  | AddTodo of text: string
  | DeleteTodo of id: int
  | EditTodo of id: int * text: string
  | CompleteTodo of id: int
  | CompleteAll
  | ClearCompleted

Full name: Redux-todomvc.TodoAction
union case TodoAction.AddTodo: text: string -> TodoAction
union case TodoAction.DeleteTodo: id: int -> TodoAction
val id : x:'T -> 'T

Full name: Microsoft.FSharp.Core.Operators.id
union case TodoAction.EditTodo: id: int * text: string -> TodoAction
union case TodoAction.CompleteTodo: id: int -> TodoAction
union case TodoAction.CompleteAll: TodoAction
union case TodoAction.ClearCompleted: TodoAction
type TodoFilter =
  | ShowAll = 0
  | ShowActive = 1
  | ShowCompleted = 2

Full name: Redux-todomvc.TodoFilter
TodoFilter.ShowAll: TodoFilter = 0
TodoFilter.ShowActive: TodoFilter = 1
TodoFilter.ShowCompleted: TodoFilter = 2
Multiple items
type LiteralAttribute =
  inherit Attribute
  new : unit -> LiteralAttribute

Full name: Microsoft.FSharp.Core.LiteralAttribute

--------------------
new : unit -> LiteralAttribute
val ESCAPE_KEY : float

Full name: Redux-todomvc.ESCAPE_KEY
val ENTER_KEY : float

Full name: Redux-todomvc.ENTER_KEY
val classNames : ((string * bool) list -> string)

Full name: Redux-todomvc.classNames
Multiple items
union case HTMLAttr.List: string -> HTMLAttr

--------------------
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
  interface IEnumerable
  interface IEnumerable<'T>
  member GetSlice : startIndex:int option * endIndex:int option -> 'T list
  member Head : 'T
  member IsEmpty : bool
  member Item : index:int -> 'T with get
  member Length : int
  member Tail : 'T list
  static member Cons : head:'T * tail:'T list -> 'T list
  static member Empty : 'T list

Full name: Microsoft.FSharp.Collections.List<_>
val choose : chooser:('T -> 'U option) -> list:'T list -> 'U list

Full name: Microsoft.FSharp.Collections.List.choose
val txt : string
val add : bool
Multiple items
type String =
  new : value:char -> string + 7 overloads
  member Chars : int -> char
  member Clone : unit -> obj
  member CompareTo : value:obj -> int + 1 overload
  member Contains : value:string -> bool
  member CopyTo : sourceIndex:int * destination:char[] * destinationIndex:int * count:int -> unit
  member EndsWith : value:string -> bool + 2 overloads
  member Equals : obj:obj -> bool + 2 overloads
  member GetEnumerator : unit -> CharEnumerator
  member GetHashCode : unit -> int
  ...

Full name: System.String

--------------------
String(value: nativeptr<char>) : unit
String(value: nativeptr<sbyte>) : unit
String(value: char []) : unit
String(c: char, count: int) : unit
String(value: nativeptr<char>, startIndex: int, length: int) : unit
String(value: nativeptr<sbyte>, startIndex: int, length: int) : unit
String(value: char [], startIndex: int, length: int) : unit
String(value: nativeptr<sbyte>, startIndex: int, length: int, enc: Text.Encoding) : unit
val concat : sep:string -> strings:seq<string> -> string

Full name: Microsoft.FSharp.Core.String.concat
type TodoTextInputProps =
  interface
    abstract member OnSave : string -> unit
    abstract member Editing : bool
    abstract member NewTodo : bool
    abstract member Placeholder : string
    abstract member Text : string option
  end

Full name: Redux-todomvc.TodoTextInputProps
abstract member TodoTextInputProps.OnSave : string -> unit

Full name: Redux-todomvc.TodoTextInputProps.OnSave
Multiple items
abstract member TodoTextInputProps.Text : string option

Full name: Redux-todomvc.TodoTextInputProps.Text

--------------------
namespace System.Text
type 'T option = Option<'T>

Full name: Microsoft.FSharp.Core.option<_>
abstract member TodoTextInputProps.Placeholder : string

Full name: Redux-todomvc.TodoTextInputProps.Placeholder
abstract member TodoTextInputProps.Editing : bool

Full name: Redux-todomvc.TodoTextInputProps.Editing
abstract member TodoTextInputProps.NewTodo : bool

Full name: Redux-todomvc.TodoTextInputProps.NewTodo
type TodoTextInputState =
  {Text: string;}

Full name: Redux-todomvc.TodoTextInputState
Multiple items
TodoTextInputState.Text: string

--------------------
namespace System.Text
Multiple items
type TodoTextInput =
  inherit Component<TodoTextInputProps,TodoTextInputState>
  new : props:TodoTextInputProps * ctx:obj -> TodoTextInput
  member HandleBlur : e:SyntheticEvent -> unit
  member HandleChange : e:SyntheticEvent -> unit
  member HandleSubmit : e:KeyboardEvent -> unit
  member render : unit -> ReactElement<obj>

Full name: Redux-todomvc.TodoTextInput

--------------------
new : props:TodoTextInputProps * ctx:obj -> TodoTextInput
val props : TodoTextInputProps
val ctx : obj
val this : TodoTextInput
Multiple items
val React : React.Globals

Full name: Fable.Import.React_Extensions.React

--------------------
module React

from Fable.Helpers

--------------------
module React

from Fable.Import
Multiple items
type Component<'P,'S> =
  interface ComponentLifecycle<'P,'S>
  new : ?props:'P * ?context:obj -> Component<'P,'S>
  member forceUpdate : ?callBack:Func<unit,obj> -> unit
  member context : obj
  member props : 'P
  member refs : obj
  member state : 'S
  member render : unit -> ReactElement<'P>
  member setState : f:Func<'S,'P,'S> * ?callback:Func<unit,obj> -> unit
  member setState : state:'S * ?callback:Func<unit,obj> -> unit
  ...

Full name: Fable.Import.React.Component<_,_>

--------------------
new : ?props:'P * ?context:obj -> React.Component<'P,'S>
namespace System.Text
val defaultArg : arg:'T option -> defaultValue:'T -> 'T

Full name: Microsoft.FSharp.Core.Operators.defaultArg
member TodoTextInput.HandleSubmit : e:React.KeyboardEvent -> unit

Full name: Redux-todomvc.TodoTextInput.HandleSubmit
val e : React.KeyboardEvent
type KeyboardEvent =
  interface
    inherit SyntheticEvent
    abstract member getModifierState : key:string -> bool
    abstract member altKey : bool
    abstract member charCode : float
    abstract member ctrlKey : bool
    abstract member key : string
    abstract member keyCode : float
    abstract member locale : string
    abstract member location : float
    abstract member metaKey : bool
    ...
  end

Full name: Fable.Import.React.KeyboardEvent
property React.KeyboardEvent.which: float
val text : string
property React.SyntheticEvent.target: Browser.EventTarget
property React.Component.props: TodoTextInputProps
abstract member TodoTextInputProps.OnSave : string -> unit
property TodoTextInputProps.NewTodo: bool
member React.Component.setState : f:Func<'S,'P,'S> * ?callback:Func<unit,obj> -> unit
member React.Component.setState : state:'S * ?callback:Func<unit,obj> -> unit
member TodoTextInput.HandleChange : e:React.SyntheticEvent -> unit

Full name: Redux-todomvc.TodoTextInput.HandleChange
val e : React.SyntheticEvent
type SyntheticEvent =
  interface
    abstract member bubbles : bool
    abstract member cancelable : bool
    abstract member currentTarget : EventTarget
    abstract member defaultPrevented : bool
    abstract member eventPhase : float
    abstract member isTrusted : bool
    abstract member nativeEvent : Event
    abstract member target : EventTarget
    abstract member timeStamp : DateTime
    abstract member type : string
    ...
  end

Full name: Fable.Import.React.SyntheticEvent
member TodoTextInput.HandleBlur : e:React.SyntheticEvent -> unit

Full name: Redux-todomvc.TodoTextInput.HandleBlur
val not : value:bool -> bool

Full name: Microsoft.FSharp.Core.Operators.not
member TodoTextInput.render : unit -> React.ReactElement<obj>

Full name: Redux-todomvc.TodoTextInput.render
val internal input : b:IHTMLProp list -> c:React.ReactElement<obj> list -> React.ReactElement<obj>

Full name: Fable.Helpers.React.input
union case HTMLAttr.ClassName: string -> HTMLAttr
property TodoTextInputProps.Editing: bool
Multiple items
union case HTMLAttr.Type: string -> HTMLAttr

--------------------
type Type =
  inherit MemberInfo
  member Assembly : Assembly
  member AssemblyQualifiedName : string
  member Attributes : TypeAttributes
  member BaseType : Type
  member ContainsGenericParameters : bool
  member DeclaringMethod : MethodBase
  member DeclaringType : Type
  member Equals : o:obj -> bool + 1 overload
  member FindInterfaces : filter:TypeFilter * filterCriteria:obj -> Type[]
  member FindMembers : memberType:MemberTypes * bindingAttr:BindingFlags * filter:MemberFilter * filterCriteria:obj -> MemberInfo[]
  ...

Full name: System.Type
union case DOMAttr.OnBlur: (React.FocusEvent -> unit) -> DOMAttr
member TodoTextInput.HandleBlur : e:React.SyntheticEvent -> unit
union case DOMAttr.OnChange: (React.FormEvent -> unit) -> DOMAttr
member TodoTextInput.HandleChange : e:React.SyntheticEvent -> unit
union case DOMAttr.OnKeyDown: (React.KeyboardEvent -> unit) -> DOMAttr
member TodoTextInput.HandleSubmit : e:React.KeyboardEvent -> unit
union case HTMLAttr.AutoFocus: bool -> HTMLAttr
property React.Component.state: TodoTextInputState
TodoTextInputState.Text: string
property String.Length: int
union case HTMLAttr.Placeholder: string -> HTMLAttr
property TodoTextInputProps.Placeholder: string
type TodoItemProps =
  interface
    abstract member CompleteTodo : int -> unit
    abstract member DeleteTodo : int -> unit
    abstract member EditTodo : int * string -> unit
    abstract member Todo : Todo
  end

Full name: Redux-todomvc.TodoItemProps
Multiple items
abstract member TodoItemProps.Todo : Todo

Full name: Redux-todomvc.TodoItemProps.Todo

--------------------
type Todo =
  {Text: string;
   Completed: bool;
   Id: int;}

Full name: Redux-todomvc.Todo
abstract member TodoItemProps.EditTodo : int * string -> unit

Full name: Redux-todomvc.TodoItemProps.EditTodo
abstract member TodoItemProps.DeleteTodo : int -> unit

Full name: Redux-todomvc.TodoItemProps.DeleteTodo
abstract member TodoItemProps.CompleteTodo : int -> unit

Full name: Redux-todomvc.TodoItemProps.CompleteTodo
type TodoItemState =
  {Editing: bool;}

Full name: Redux-todomvc.TodoItemState
TodoItemState.Editing: bool
Multiple items
type TodoItem =
  inherit Component<TodoItemProps,TodoItemState>
  new : props:TodoItemProps * ctx:obj -> TodoItem
  member HandleDoubleClick : 'a -> unit
  member HandleSave : id:int * text:string -> unit
  member render : unit -> ReactElement<obj>

Full name: Redux-todomvc.TodoItem

--------------------
new : props:TodoItemProps * ctx:obj -> TodoItem
val props : TodoItemProps
val this : TodoItem
member TodoItem.HandleDoubleClick : 'a -> unit

Full name: Redux-todomvc.TodoItem.HandleDoubleClick
member TodoItem.HandleSave : id:int * text:string -> unit

Full name: Redux-todomvc.TodoItem.HandleSave
val id : int
property React.Component.props: TodoItemProps
abstract member TodoItemProps.DeleteTodo : int -> unit
abstract member TodoItemProps.EditTodo : int * string -> unit
member TodoItem.render : unit -> React.ReactElement<obj>

Full name: Redux-todomvc.TodoItem.render
val element : React.ReactElement<obj>
property React.Component.state: TodoItemState
val internal com<'T,'P,'S (requires 'T :> React.Component<'P,'S>)> : props:'P -> children:React.ReactElement<obj> list -> React.ReactElement<obj>

Full name: Fable.Helpers.React.com
member TodoItem.HandleSave : id:int * text:string -> unit
property TodoItemProps.Todo: Todo
val __ : TodoTextInputProps
property TodoTextInputProps.Text: string option
Todo.Text: string
val internal div : b:IHTMLProp list -> c:React.ReactElement<obj> list -> React.ReactElement<obj>

Full name: Fable.Helpers.React.div
Multiple items
union case HTMLAttr.Checked: bool -> HTMLAttr

--------------------
module Checked

from Microsoft.FSharp.Core.ExtraTopLevelOperators

--------------------
module Checked

from Microsoft.FSharp.Core.Operators
abstract member TodoItemProps.CompleteTodo : int -> unit
val internal label : b:IHTMLProp list -> c:React.ReactElement<obj> list -> React.ReactElement<obj>

Full name: Fable.Helpers.React.label
union case DOMAttr.OnDoubleClick: (React.MouseEvent -> unit) -> DOMAttr
member TodoItem.HandleDoubleClick : 'a -> unit
union case DOMAttr.OnClick: (React.MouseEvent -> unit) -> DOMAttr
val internal li : b:IHTMLProp list -> c:React.ReactElement<obj> list -> React.ReactElement<obj>

Full name: Fable.Helpers.React.li
type HeaderProps =
  {AddTodo: string -> unit;}

Full name: Redux-todomvc.HeaderProps
HeaderProps.AddTodo: string -> unit
val Header : props:HeaderProps -> React.ReactElement<obj>

Full name: Redux-todomvc.Header
val props : HeaderProps
val internal header : b:IHTMLProp list -> c:React.ReactElement<obj> list -> React.ReactElement<obj>

Full name: Fable.Helpers.React.header
val internal h1 : b:IHTMLProp list -> c:React.ReactElement<obj> list -> React.ReactElement<obj>

Full name: Fable.Helpers.React.h1
type FooterProps =
  interface
    abstract member OnClearCompleted : SyntheticEvent -> unit
    abstract member OnShow : TodoFilter -> unit
    abstract member ActiveCount : int
    abstract member CompletedCount : int
    abstract member Filter : TodoFilter
  end

Full name: Redux-todomvc.FooterProps
abstract member FooterProps.ActiveCount : int

Full name: Redux-todomvc.FooterProps.ActiveCount
abstract member FooterProps.CompletedCount : int

Full name: Redux-todomvc.FooterProps.CompletedCount
abstract member FooterProps.Filter : TodoFilter

Full name: Redux-todomvc.FooterProps.Filter
abstract member FooterProps.OnShow : TodoFilter -> unit

Full name: Redux-todomvc.FooterProps.OnShow
abstract member FooterProps.OnClearCompleted : React.SyntheticEvent -> unit

Full name: Redux-todomvc.FooterProps.OnClearCompleted
val Footer : (FooterProps -> React.ReactElement<obj>)

Full name: Redux-todomvc.Footer
val filterTitles : Collections.Generic.IDictionary<TodoFilter,string>
val dict : keyValuePairs:seq<'Key * 'Value> -> Collections.Generic.IDictionary<'Key,'Value> (requires equality)

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.dict
val renderTodoCount : (int -> React.ReactElement<obj>)
val activeCount : int
val internal span : b:IHTMLProp list -> c:React.ReactElement<obj> list -> React.ReactElement<obj>

Full name: Fable.Helpers.React.span
val sprintf : format:Printf.StringFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
val renderFilterLink : (TodoFilter -> TodoFilter -> (TodoFilter -> unit) -> React.ReactElement<obj>)
val filter : TodoFilter
val selectedFilter : TodoFilter
val onShow : (TodoFilter -> unit)
val internal a : b:IHTMLProp list -> c:React.ReactElement<obj> list -> React.ReactElement<obj>

Full name: Fable.Helpers.React.a
union case HTMLAttr.Style: ICSSProp list -> HTMLAttr
val renderClearButton : (int -> (React.MouseEvent -> unit) -> React.ReactElement<obj> option)
val completedCount : int
val onClearCompleted : (React.MouseEvent -> unit)
val internal button : b:IHTMLProp list -> c:React.ReactElement<obj> list -> React.ReactElement<obj>

Full name: Fable.Helpers.React.button
val props : FooterProps
val listItems : React.ReactElement<obj> list
val map : mapping:('T -> 'U) -> list:'T list -> 'U list

Full name: Microsoft.FSharp.Collections.List.map
property FooterProps.Filter: TodoFilter
abstract member FooterProps.OnShow : TodoFilter -> unit
union case Prop.Key: string -> Prop
val internal footer : b:IHTMLProp list -> c:React.ReactElement<obj> list -> React.ReactElement<obj>

Full name: Fable.Helpers.React.footer
property FooterProps.ActiveCount: int
val internal ul : b:IHTMLProp list -> c:React.ReactElement<obj> list -> React.ReactElement<obj>

Full name: Fable.Helpers.React.ul
property FooterProps.CompletedCount: int
abstract member FooterProps.OnClearCompleted : React.SyntheticEvent -> unit
type MainSectionProps =
  {Todos: Todo [];
   Dispatch: TodoAction -> unit;}

Full name: Redux-todomvc.MainSectionProps
MainSectionProps.Todos: Todo []
MainSectionProps.Dispatch: TodoAction -> unit
type MainSectionState =
  {Filter: TodoFilter;}

Full name: Redux-todomvc.MainSectionState
MainSectionState.Filter: TodoFilter
Multiple items
type MainSection =
  inherit Component<MainSectionProps,MainSectionState>
  new : props:MainSectionProps * ctx:obj -> MainSection
  member HandleClearCompleted : unit -> unit
  member HandleShow : filter:TodoFilter -> unit
  member render : unit -> ReactElement<obj>
  member renderFooter : completedCount:int -> ReactElement<obj> option
  member renderToggleAll : completedCount:int -> ReactElement<obj> option

Full name: Redux-todomvc.MainSection

--------------------
new : props:MainSectionProps * ctx:obj -> MainSection
val props : MainSectionProps
val this : MainSection
val todoFilters : Collections.Generic.IDictionary<TodoFilter,(Todo -> bool)>
val todo : Todo
property React.Component.state: MainSectionState
union case CSSProp.Filter: obj -> CSSProp
member MainSection.HandleClearCompleted : unit -> unit

Full name: Redux-todomvc.MainSection.HandleClearCompleted
property React.Component.props: MainSectionProps
member MainSection.HandleShow : filter:TodoFilter -> unit

Full name: Redux-todomvc.MainSection.HandleShow
member MainSection.renderToggleAll : completedCount:int -> React.ReactElement<obj> option

Full name: Redux-todomvc.MainSection.renderToggleAll
property Array.Length: int
member MainSection.renderFooter : completedCount:int -> React.ReactElement<obj> option

Full name: Redux-todomvc.MainSection.renderFooter
val internal fn : f:('Props -> #React.ReactElement<obj>) -> props:'Props -> children:React.ReactElement<obj> list -> React.ReactElement<obj>

Full name: Fable.Helpers.React.fn
val __ : FooterProps
member MainSection.HandleShow : filter:TodoFilter -> unit
member MainSection.HandleClearCompleted : unit -> unit
member MainSection.render : unit -> React.ReactElement<obj>

Full name: Redux-todomvc.MainSection.render
val filteredTodos : Todo list
type Array =
  member Clone : unit -> obj
  member CopyTo : array:Array * index:int -> unit + 1 overload
  member GetEnumerator : unit -> IEnumerator
  member GetLength : dimension:int -> int
  member GetLongLength : dimension:int -> int64
  member GetLowerBound : dimension:int -> int
  member GetUpperBound : dimension:int -> int
  member GetValue : [<ParamArray>] indices:int[] -> obj + 7 overloads
  member Initialize : unit -> unit
  member IsFixedSize : bool
  ...

Full name: System.Array
val filter : predicate:('T -> bool) -> array:'T [] -> 'T []

Full name: Microsoft.FSharp.Collections.Array.filter
val toList : array:'T [] -> 'T list

Full name: Microsoft.FSharp.Collections.Array.toList
val fold : folder:('State -> 'T -> 'State) -> state:'State -> array:'T [] -> 'State

Full name: Microsoft.FSharp.Collections.Array.fold
val count : int
val internal section : b:IHTMLProp list -> c:React.ReactElement<obj> list -> React.ReactElement<obj>

Full name: Fable.Helpers.React.section
member MainSection.renderToggleAll : completedCount:int -> React.ReactElement<obj> option
val __ : TodoItemProps
member MainSection.renderFooter : completedCount:int -> React.ReactElement<obj> option
type AppProps =
  {Store: IStore<Todo [],TodoAction>;}

Full name: Redux-todomvc.AppProps
AppProps.Store: Redux.IStore<Todo [],TodoAction>
module Redux

from Redux-todomvc
Multiple items
type App =
  inherit Component<AppProps,MainSectionProps>
  new : props:AppProps * ctx:obj -> App
  member render : unit -> ReactElement<obj>

Full name: Redux-todomvc.App

--------------------
new : props:AppProps * ctx:obj -> App
val props : AppProps
val this : App
val dispatch : (TodoAction -> unit)
val dispatch : store:Redux.IStore<'TState,'TAction> -> x:'TAction -> unit

Full name: Redux-todomvc.Redux.dispatch
val getState : (unit -> MainSectionProps)
val getState : store:Redux.IStore<'TState,'TAction> -> 'TState

Full name: Redux-todomvc.Redux.getState
property React.Component.state: MainSectionProps
val subscribe : store:Redux.IStore<'TState,'TAction> -> f:(unit -> unit) -> unit

Full name: Redux-todomvc.Redux.subscribe
member App.render : unit -> React.ReactElement<obj>

Full name: Redux-todomvc.App.render
val reducer : state:Todo [] -> _arg1:TodoAction -> Todo []

Full name: Redux-todomvc.reducer
val state : Todo []
val max : e1:'T -> e2:'T -> 'T (requires comparison)

Full name: Microsoft.FSharp.Core.Operators.max
val append : array1:'T [] -> array2:'T [] -> 'T []

Full name: Microsoft.FSharp.Collections.Array.append
union case HTMLAttr.Id: string -> HTMLAttr
val map : mapping:('T -> 'U) -> array:'T [] -> 'U []

Full name: Microsoft.FSharp.Collections.Array.map
val areAllMarked : bool
val forall : predicate:('T -> bool) -> array:'T [] -> bool

Full name: Microsoft.FSharp.Collections.Array.forall
val store : Redux.IStore<Todo [],TodoAction>

Full name: Redux-todomvc.store
val singleton : value:'T -> 'T []

Full name: Microsoft.FSharp.Collections.Array.singleton
val createStore : reducer:('TState -> 'TAction -> 'TState) -> initState:'TState -> Redux.IStore<'TState,'TAction>

Full name: Redux-todomvc.Redux.createStore
Multiple items
val ReactDom : ReactDom.Globals

Full name: Fable.Import.React_Extensions.ReactDom

--------------------
module ReactDom

from Fable.Import
member ReactDom.Globals.render : element:React.DOMElement<'P> * container:Browser.Element * ?callback:Func<Browser.Element,obj> -> Browser.Element
member ReactDom.Globals.render : element:React.ClassicElement<'P> * container:Browser.Element * ?callback:Func<React.ClassicComponent<'P,'S>,obj> -> React.ClassicComponent<'P,'S>
member ReactDom.Globals.render : element:React.ReactElement<'P> * container:Browser.Element * ?callback:Func<React.Component<'P,'S>,obj> -> React.Component<'P,'S>
val document : Browser.Document

Full name: Fable.Import.Browser.document
Fork me on GitHub