Elm의 첫 10k LOC에서 배운 교훈
26331 단어 showdevelmfunctionalwebdev
나는 지금까지 배웠고 공유하고 싶은 몇 가지 반복되는 패턴을 발견했습니다. 그래서 여기에 제가 배운 5가지가 있습니다.
1. 빈 목록과 문자열 디코딩
처음 프로젝트를 시작했을 때 다음과 같이 선언된 필드가 있는 많은 유형이 있었습니다.
type alias Something =
{ name : Maybe String
, stuff : Maybe (List String)
}
내 API에서 이러한 값을 디코딩하는 방법은 다음과 같습니다.
import Json.Decode as D
import Json.Decode.Pipeline exposing (optional)
D.succeed Something
|> optional "name" (D.nullable D.string) Nothing
|> optional "stuff" (D.nullable (D.list D.string)) Nothing
처음에는 괜찮았지만 잠시 후
name
필드가 빈 문자열인지 또는 stuff
필드가 빈 목록인지 확인하기 위해 업데이트 함수 내에서 논리를 반복하고 있다는 것을 알아차렸습니다. 값을 Nothing
또는 Just ""
대신 Just []
로 변경하십시오.그래서 잠시 후 디코딩 단계에서 빈 문자열과 빈 목록을 확인하는 것이 더 효율적이라고 생각하여 두 개의 디코더 도우미 함수
decodeNonEmptyString
및 decodeNonEmptyList
를 생각해 냈습니다.decodeNonEmptyString : D.Decoder (Maybe String)
decodeNonEmptyString =
D.andThen (notEmptyString >> D.succeed) D.string
decodeNonEmptyList : D.Decoder a -> D.Decoder (Maybe (List a))
decodeNonEmptyList l =
D.andThen (notEmptyList >> D.succeed) (D.maybe (D.list l))
notEmptyList : Maybe (List a) -> Maybe (List a)
notEmptyList =
Maybe.andThen (\l ->
if List.isEmpty l then
Nothing
else
Just l
)
notEmptyString : String -> Maybe String
notEmptyString s =
if String.isEmpty s then
Nothing
else
Just a
이제 내 원래 디코더는 다음과 같은 새로운 유틸리티를 사용합니다.
D.succeed Something
|> optional "name" decodeNonEmptyString Nothing
|> optional "stuff" (decodeNonEmptyList D.string) Nothing
그리고 더 이상 업데이트 기능에서 공백을 확인할 필요가 없습니다 🎉
2. 플랫 패턴 매칭
이것은 정말 명백해 보일 수 있지만, 이렇게 하면 더 평평한 패턴 매칭 기능이 생긴다는 것을 깨닫는 데 시간이 걸렸습니다.
RD.WebData (Maybe (List Person))
와 같은 유형이 있는 경우 원래 일치하는 방식은 다음과 같습니다.case response of
RD.NotAsked -> div [] [text "Not Asked"]
RD.Loading -> div [] [text "Loading"]
RD.Failure e -> div [] [text "Error"]
RD.Success maybeData ->
case maybeData of
Nothing -> div [] [text "No data"]
Just data -> div [] [text "Data!"]
그러나 나는 이것을 다음과 같이 2개의 개별적인 진술 대신에 하나의 case 진술로 병합할 수 있다는 것을 깨달았습니다.
case response of
RD.NotAsked -> div [] [text "Not Asked"]
RD.Loading -> div [] [text "Loading"]
RD.Failure e -> div [] [text "Error"]
RD.Success Nothing -> div [] [text "No data"]
RD.Success (Just data) -> div [] [text "Data!"]
보다? 훨씬 더 멋져!
3. 래퍼 선택
이것은 쉽게 재사용할 수 있는 방법을 원했고
select [] []
Elm의 기본 방법이 기대만큼 좋지 않다는 것을 발견했기 때문에 시작할 때 만든 첫 번째 추상화 중 하나입니다.그래서
viewSelect
라는 작은 래퍼를 작성했습니다.viewSelect : Bool -> (String -> msg) -> List Option -> Html msg
viewSelect disabled changeMsg options =
select
[ onChange changeMsg
, Html.Styled.Attributes.disabled disabled
]
(List.map
(\opt ->
option
[ value opt.value
, Html.Styled.Attributes.selected opt.selected
, Html.Styled.Attributes.disabled opt.disabled
]
[ text opt.text ]
)
options
)
type alias Option =
{ text : String
, value : String
, selected : Bool
, disabled : Bool
}
onChange : (String -> msg) -> Html.Styled.Attribute msg
onChange tagger =
on "change" (D.map tagger Html.Styled.Events.targetValue)
이제 네이티브
viewSelect
대신 select
를 사용하고 대신 Option
목록을 전달하면 됩니다. 사용자가 선택 상자를 변경할 때마다 changeMsg는 새 값으로 실행됩니다.4. ChangeField 패턴
내 앱에는 많은 양식이 있으며 모든 양식은 내 모델 내부의 일부 필드를 수정해야 합니다. 원래 이 일을 시작한 방법은 다음과 같습니다.
type Msg
= ChangeName String
| ChangeAge String
| ChangeHeight String
| ChangeWeight String
update msg model =
case msg of
ChangeName name -> ({ model | name = name }, Cmd.none)
ChangeAge age -> ({ model | age = age }, Cmd.none)
ChangeHeight height -> ({ model | height = height }, Cmd.none)
ChangeWeight weight -> ({ model | weight = weight }, Cmd.none)
이것도 처음에는 괜찮았지만 내 앱에는 양식이 많고 확장이 잘 되지 않았습니다. 그래서 모든 필드에 대해 새로운 메시지 유형을 만드는 대신 모든 필드를 업데이트하기 위해 하나의 메시지 유형을 만들었습니다. 그리고 다음과 같이 작동합니다.
type Msg = ChangeField (String -> Person -> Person) String
update msg model =
case msg of
ChangeField setter content -> (setter content model, Cmd.none)
이제 필드를 변경해야 할 때마다
ChangeName "Jason"
를 호출하는 대신 setName이 다음과 같은 곳에서 ChangeField setName "Jason"
를 호출할 수 있습니다.setName : String -> Person -> Person
setName content person =
{ person | name = content }
이를 통해 각 필드에 대해 개별 setter 함수를 만들고 String에서 필요한 모든 유형으로 데이터 조작을 수행할 수 있습니다.
일치시킬 수 있는 메시지 유형의 양을 줄여 업데이트 기능을 단순화하고 각 필드에 대한 데이터 조작 논리를 멋지게 분리합니다.
5. 포식스 래퍼
이것은 정말 간단합니다. 저는 Elm
Time.Posix
유형을 앱 주변에 많이 사용하기 때문에 UI에서 Posix를 포맷, 조작 및 표시하는 기능이 필요했습니다. 이를 위해 몇 가지 도우미를 생각해 내 HumanTime 모듈이라고 부릅니다.humanDateTime : Zone -> Posix -> String
humanDateTime z p =
humanDate z p ++ " " ++ humanTime z p
humanTime : Zone -> Posix -> String
humanTime z p =
humanHour z p ++ ":" ++ humanMinute z p
humanDate : Zone -> Posix -> String
humanDate z p =
humanMonth z p ++ " " ++ humanDay z p ++ ", " ++ humanYear z p
humanYear : Zone -> Posix -> String
humanYear z =
toYear z >> String.fromInt
humanDay : Zone -> Posix -> String
humanDay z =
toDay z >> String.fromInt
humanHour : Zone -> Posix -> String
humanHour z =
toHour z >> String.fromInt
humanMinute : Zone -> Posix -> String
humanMinute z =
toMinute z
>> (\m ->
if m > 9 then
String.fromInt m
else
"0" ++ String.fromInt m
)
humanMonth : Zone -> Posix -> String
humanMonth z p =
case toMonth z p of
Jan -> "Jan"
Feb -> "Feb"
Mar -> "Mar"
Apr -> "Apr"
May -> "May"
Jun -> "Jun"
Jul -> "Jul"
Aug -> "Aug"
Sep -> "Sep"
Oct -> "Oct"
Nov -> "Nov"
Dec -> "Dec"
그리고 그것이 지금까지입니다! 나는 내가 작성하는 다음 10k LOC에서 더 많은 것을 배우고 방금 언급한 이러한 작업을 수행하는 더 나은 방법을 알아낼 수 있을 것이라고 확신합니다.
Elm은 프론트엔드를 만들기 위한 놀라운 도구이며 개인 프로젝트를 위해 React나 다른 비기능적 프레임워크로 되돌아가는 것을 본 적이 없습니다.
이 기사가 마음에 들면 피드에 새 기사를 게시할 때마다 확인하십시오 ✨ 또는
Reference
이 문제에 관하여(Elm의 첫 10k LOC에서 배운 교훈), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/rametta/lessons-learned-from-my-first-10k-loc-in-elm-40m텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)