elm에서 대국 타이머를 만들어 보았습니다.

17221 단어 Elm
Elm 입문이 와서, 최소한 움직이는 어플리케이션 만들어 보았습니다.
그것이 여기



유스 케이스로서는, 사고계의 대전에서
소비 시간을 측정하면서 하고 싶을 때 사용할 수 있으면.

앱 페이지
리포지토리

동기 부여



  • Elm-jp 에 일단 넣어 주고 있기 때문에 뭔가 아웃풋이 하고 싶다

  • Elm 입문 핸즈온 에서 실제로 손을 움직여 재미 있었기 때문에 열이 식지 않는 동안 공부하고 싶습니다

  • Elm 입문 핸즈온 에는 당일 사용한 자료도 공개되어 있으므로 개발 환경 정돈하고 싶은 분의 참고가 된다고 생각합니다

    코드



    이만큼, 매우 간단.
    내가 초보자이므로 상급자는 더 간단하게 쓸 것이라고 생각합니다만
    그래도 100행 정도로 끝났습니다.

    Main.elm
    
    module Main exposing (Model, Msg(..), init, main, subscriptions, update, view)
    
    import Browser
    import Html exposing (Html, div, h1, img, input, text)
    import Html.Attributes exposing (class, src, type_, value)
    import Html.Events exposing (onClick)
    import Task
    import Time
    
    
    
    -- MODEL
    
    
    type alias Model =
        { zone : Time.Zone
        , time : Time.Posix
        , counter : Int
        , limit : Int
        , isStart : Bool
        }
    
    
    init : () -> ( Model, Cmd Msg )
    init _ =
        ( Model Time.utc (Time.millisToPosix 0) 0 30 False
        , Task.perform AdjustTimeZone Time.here
        )
    
    
    
    -- UPDATE
    
    
    type Msg
        = Tick Time.Posix
        | AdjustTimeZone Time.Zone
        | DoTimer
        | ChangePlayer
    
    
    update : Msg -> Model -> ( Model, Cmd Msg )
    update msg model =
        case msg of
            Tick newTime ->
                let
                    c =
                        if model.isStart then
                            model.counter + 1
    
                        else
                            model.counter
                in
                ( { model | counter = c }
                , Cmd.none
                )
    
            AdjustTimeZone newZone ->
                ( { model | zone = newZone }
                , Cmd.none
                )
    
            DoTimer ->
                let
                    r =
                        if model.isStart then
                            False
    
                        else
                            True
                in
                ( { model | isStart = r }, Cmd.none )
    
            ChangePlayer ->
                ( { model | counter = 0 }
                , Cmd.none
                )
    
    
    
    -- SUBSCRIPTIONS
    
    
    subscriptions : Model -> Sub Msg
    subscriptions model =
        Time.every 1000 Tick
    
    
    
    -- VIEW
    
    
    view : Model -> Html Msg
    view model =
        let
            c =
                String.fromInt model.counter
    
            l =
                String.fromInt model.limit
    
            bt =
                if model.isStart then
                    "Stop!"
    
                else
                    "Start"
    
            btClass =
                if model.isStart then
                    "bt stop-bt"
    
                else
                    "bt"
        in
        div [ class "grid-container" ]
            [ div [ class "start" ]
                [ div
                    [ class "area-overlap start-bt" ]
                    [ input [ type_ "button", value bt, onClick DoTimer, class btClass ] []
                    ]
                ]
            , div
                [ class "counter" ]
                [ h1 [] [ text c ]
                ]
            , div
                [ class "change" ]
                [ input [ type_ "button", value "Change", onClick ChangePlayer, class "bt change-bt" ] []
                ]
            ]
    
    
    main =
        Browser.element
            { init = init
            , update = update
            , subscriptions = subscriptions
            , view = view
            }
    
    

    이것을 일부러 react-redux로 하려고 하면 힘들군요.
    store 만들어, reducer, action 나누어. . 등등

    공부회 LT 들었던 느낌이라면 실제로 응용 프로그램을 만든 분들도Main.elm 쓰레기를 썼다.
    이번에는 저도 그 방침에 따라 보았습니다.

    코드 대형 프레임 설명



    elm에서는, 어플리케이션으로 취급하는 상태를 Model 로 정의합니다.
    애플리케이션 내부에서 정의된 이벤트( Msg )는 update이 때 Model의 상태를 다시 씁니다.Modelview 를 사용해 렌더링 됩니다.

    redux의 구조를 이해하는 분은, 아-같은 구조라고 느끼는지 생각합니다
    원래 redux가 이 elm architecture를 참고로 만들어진 것 같기 때문에 그것도 그럴 것이군요.

    빠진 것



    if 사용법



    return이 필요하지 않으므로 다음과 같이 씁니다.
    bt =
            if model.isStart then
                "Stop!"
    
            else
                "Start"
    
    

    Model 초기화



    Model의 필드를 선언하지 않아도 되므로
    field의 순서에 값 건네주면 좋다
    type alias Model =
        { zone : Time.Zone
        , time : Time.Posix
        , counter : Int
        , limit : Int
        , isStart : Bool
        }
    
    init : () -> ( Model, Cmd Msg )
    init _ =
        ( Model Time.utc (Time.millisToPosix 0) 0 30 False
        , Task.perform AdjustTimeZone Time.here
        )
    

    좋은 곳


  • 약간의 것을 만들고 싶을 때 곧 시작된다
  • 데이터 흐름 정의되고 유형도 있으므로 확실하지 않습니다

  • 공식 linter가 있기 때문에 code format도 확실히 할 수 있다
  • 컴파일러가 똑똑하다.
  • 공식의 여기 보라든지 링크까지 내 주는, 대단한


  • 걱정되는 곳


  • 부작용 취급
  • spa라든가 하면 코드가 엉망이 되는지
  • oss 읽기

  • 좋은 웹페이지 즐겨찾기