ããã«ã¡ã¯ãWebã¨ã³ã¸ãã¢ãã¼ã ã®@sat0yuã§ãã ããã¾ã§å¼ç¤¾ã®ã¤ãã³ãå ±åç³»è¨äºããæ¸ãã¦ããªãã£ãã®ã§ãããä»æ¥ã¯ãããæè¡å¯ãã®è©±ããã¾ãã
ç¾å¨ç§éã®ãã¼ã ã§ã¯ãçå¾åãå¦ç¿ãµã¼ãã¹ï¼æ¥æ¬ã§ã¯ã¹ã¿ãã£ãµããªã æµ·å¤ã§ã¯Quipper Videoã®å称ã§æä¾ï¼ã®ãªãã¥ã¼ã¢ã«ããã¸ã§ã¯ããæ å½ãã¦ãã¾ãã ãªãã¥ã¼ã¢ã«ãã¼ã¸ã§ã³ã®ãªãªã¼ã¹ã¯ä»¥ä¸ã®ã¨ããè¨ç»ããã¦ããããã§ã«åæ ¼ç¹è¨ã³ã¼ã¹ã®ã¦ã¼ã¶ã«ã¯æä¾ãéå§ãã¦ãã¾ãã
- 2018å¤: ã¹ã¿ãã£ãµããªåæ ¼ç¹è¨ã³ã¼ã¹ã¦ã¼ã¶
- 2019æ¥: ã¹ã¿ãã£ãµããªã®å ¨ã¦ã¼ã¶
- 2019æ¥ãå¤: Quipper Videoã®å ¨ã¦ã¼ã¶
æ¬æ稿ã§ã¯ããªãã¥ã¼ã¢ã«ããã¸ã§ã¯ãã®ä¸ã§çã¾ãããå°ããããã©ãé常ã«ä¾¿å©ãªç¶æ é·ç§»ç®¡çã³ã³ãã¼ãã³ãã®ç´¹ä»ããã¾ãã
TL; DR
- å¿å°ããUXã表ç¾ããããã«ï¼ã¤ã®ç¶æ é·ç§»ãèããå¿ è¦ããã
- ç¶æ
é·ç§»ãç°¡åã«å®è£
ããããã«
UiStackTemplate
ã³ã³ãã¼ãã³ããå°å ¥ãã - ç¾å¨ã§ã¯å
¨ã³ã³ãã¼ãã³ãã®10%+ã§
UiStackTemplate
ã使ããã¦ãã
ã¹ã¿ãã£ãµããªã»Quipper Videoã®ä¸¡ãã©ã³ãã§ã¯ããã®ããã°ã©ã ã³ã¼ãã®å¤§é¨åãå ±æããªããéçºããã¦ãããããã¨ã³ã¸ãã¢ã ãã§ãå½å å¤ãããã¦10+åç¨ãéçºã«æºãã£ã¦ãã¾ãã
ä»æ¥ã®è©±ã¯ããã¾ããåå¹´åãæ¬ãªãã¥ã¼ã¢ã«ããã¸ã§ã¯ãã«ã¯ã¾ã ï¼ãï¼äººã®ã¨ã³ã¸ãã¢ããããªãã£ãé ã«æ»ãã¾ãã ã¦ã¼ã¶ã«æ¬ãµã¼ãã¹ãå¿å°ãã使ã£ã¦ãããã¨åæã«ãå½å å¤ã®ã¨ã³ã¸ãã¢ãç°¡åã«åå ã§ããããã¸ã§ã¯ãã«ãã¦ããå¿ è¦ãããã¾ããã
å¿å°ããåå¼·ãã¦ããããã
ãããåã®è¨äºã«ãªãã¾ãããScott Hurffã¨ããæ¹ãUiStackã¨ããèãæ¹ãææ¡ãã¦ãã¾ããï¼POSTDã«æ¥æ¬èªè¨³ãããã¾ãã)
åã表示ã®ã¾ã¾ãã¤ã¾ã§ãå¾ ããããããã¨ã©ã¼ãªã®ã表示è¦ç´ ã空ï¼ã«ã©ï¼ãªã®ããä¸æçã ã£ããã¨ãã£ãä¸è¦ªåãªUIãåæ¥ãã¦ãã¦ã¼ã¶ãã¬ã³ããªã§å¿å°ã®è¯ãUIãæä¾ãããã¨ããèãæ¹ã§ãã
è¨äºä¸ã§ã¯ä¸è¨ã®ï¼ã¤ã®ç¶æ ãæ³å®ãã¦ãã¶ã¤ã³ã»å®è£ ãããã¨ã便å©ã§ããã¨èª¬æããã¦ãã¾ãã
ï¼å³ã¯å è¨äºããå¼ç¨ï¼
- Blank state
- 表示è¦ç´ ã空ã®ç¶æ
- å¤ãã®å ´åã§ã¦ã¼ã¶ã«å¯¾ãã¦ä½ããã®ã¢ã¯ã·ã§ã³ãè¦æ±ããã
- ã¦ã¼ã¶ã«ã¢ã¯ã·ã§ã³ãèµ·ãããã表示ãå¿ è¦
- Loading state
- 表示ã«å¿ è¦ãªæ å ±ãèªã¿è¾¼ãã§ããç¶æ
- ã¦ã¼ã¶ã¹ãã¬ã¹è»½æ¸ã®ããã«ã¹ã±ã«ãã³ã¹ã¯ãªã¼ã³ã«ããã¢ããã¼ããæå¹
- Partial state
- Ideal stateã«è³ãéç¨ã«ããç¶æ
- e.g. æªå®æã®ãããã£ã¼ã«ç»é¢
- Error state
- ä½ããã®ã¨ã©ã¼ã«ãã£ã¦ä»¥éã®è¡¨ç¤ºãä¸å¯è½ã«é¥ã£ãç¶æ
- ãä½ãèµ·ãã£ããããããã©ãããã°ãããããä¼ããå¿ è¦
- Ideal state
- ã¦ã¼ã¶ã«ä½é¨ãã¦ãããããçæ³ç¶æ
- ãã¹ã¦ã®å¿ è¦ãªæ å ±ãæããã¨ã©ã¼ãåå¨ãã¦ããªã
ã¢ããªã±ã¼ã·ã§ã³ã®ç¶æ é·ç§»ã«é¢ãã¦ããªãã¥ã¼ã¢ã«ä»¥åã®ï¼å³å¯ã«ã¯ä»ç¾å¨ãï¼æ¬ãµã¼ãã¹ã¯ãã¤ã®ã¨ããæ§ã ãªåé¡ãæ±ãã¦ãã¾ããã
- 表示ãããªã宿é¡ãªã¹ã
- æ¶ããªããã¼ãã£ã³ã°ã¤ã³ãã£ã±ã¼ã¿
- ãã£ãä¸ã¤ã®ãã¼ã¿ä¸æ´åã§ãµã¼ãã¹ãå©ç¨ä¸å¯è½ã«é¥ã
ä¾ãã°ãåæ ¼ç¹è¨ã³ã¼ã¹ã§ã¯æ¯é±æææ¥ã«çå¾ãã¨ã«ã«ã¹ã¿ãã¤ãºããã宿é¡ï¼ä»é±ã®èª²é¡ï¼ãé ä¿¡ãã¦ãã¾ãã ä»®ã«ãã®ã¦ã¼ã¶ãã表示ãããªã宿é¡ãªã¹ããã«ééãã¦ãã¾ã£ãå ´åã«ã¯ããä»é±ã¯ãããã¨ããªããã¨èª¤è§£ãæããããªãéè¦ãªåé¡ã«çºå±ãã¾ãã ããããªããããã¡ã¤ã³ç¥èã»ã¹ãã«ã»è¦æ±ãªã©ãèæ¯ã®ç°ãªã大å¢ã®ã¨ã³ã¸ãã¢ãåã ç¬èªã«å®è£ ãå ããããããããã®åé¡ã解æ¶ããã®ã¯æ¥µãã¦é£ãã課é¡ã¨ãªã£ã¦ãã¾ããã
ä»é±ã®èª²é¡
表示ãããªã宿é¡ãªã¹ã
ããã§ãªãã¥ã¼ã¢ã«ããã¸ã§ã¯ãã§ã¯ã(1)ãããã®åé¡ã解æ¶ããã¨ã¨ãã«ãåé¡ã®åçºãé²ãããã«ã(2)ã¢ããªã±ã¼ã·ã§ã³ã®ç¶æ é·ç§»ãèæ ®ã«å ¥ããçµ±ä¸çãªãã¬ã¼ã ã¯ã¼ã¯ãæ±ãããã¾ããã ã¾ããã¹ã¿ãã£ãµããªã»Quipper Videoã®ä¸¡ãã©ã³ãã®ä¸ã§ãåå½ããµã¼ãã¹ãæä¾ããã¦ããããã(3)ãã¬ã¼ã ã¯ã¼ã¯ã¯ã«ã¹ã¿ãã¤ãºæ§ã«åªãã¦ããå¿ è¦ãããã¾ããã
å¿å°ããéçºãã¦ããããã
æ¬ãããã¯ãã®ä¸»ãªæè¡ã¹ã¿ãã¯ã¯React + Redux + TypeScript + Workbox (Service Worker) ãæãããã¾ãã
Reactã触ã£ããã¨ã®ãã&&ã«ã³ã®è¯ãèªè
ã®æ¹ãªããããã¾ã§èªã¾ããã ã ãã§ããHigher-Order Component(HOC)ããªï¼Providerãã¿ã¼ã³ããªï¼ãã¨æ³åãããããããã¾ããã
çµè«ããå
ã«ãä¼ãããã¨ãProviderãã¿ã¼ã³ãæ¡ç¨ãã¦ãæ±ç¨ã®ç¶æ
é·ç§»ç®¡çã³ã³ãã¼ãã³ã UiStackTemplate
ãä½æãã¾ããã
ãªããæ¬æ稿å ã§ã¯HOCã¨Providerãã¿ã¼ã³ã®è©³è¿°ã¯ãã¾ãããããã¡ãã®è¨äºãé常ã«åèã«ãªãã¾ããã
import * as React from 'react'; import Spinner from 'SpinnerComponent'; import NetworkError from 'NetworkErrorComponent'; interface Props { isLoading?: boolean; isError?: boolean; isBlank?: boolean; LoadingComponent?: React.ReactChild; ErrorComponent?: React.ReactChild; BlankComponent?: React.ReactChild; children: React.ReactChild | React.ReactChild[]; } const UiStackTemplate: React.SFC<Props> = ({ isLoading, isError, isBlank, LoadingComponent, ErrorComponent, BlankComponent, children, }) => ( <> {isError ? ErrorComponent || <NetworkError /> : isLoading ? LoadingComponent || <Spinner /> : isBlank && !!BlankComponent ? BlankComponent : children} </> ); export default UiStackTemplate;
UiStackTemplate
ã¯Idealã¹ãã¼ãã«å¯¾å¿ããã³ã³ãã¼ãã³ããReact.Childrenã¨ãã¦åãåãã¾ãã
ã¾ããBlank, Error, Loadingã®3ç¶æ
ã«ã¤ãã¦ããããããææã®ã³ã³ãã¼ãã³ããPropsã¨ãã¦æ¸¡ããã¨ãã§ãã¾ãã
Propsã§æå®ãããªããã°ããããã«ç¨æãããæ±ç¨ã³ã³ãã¼ãã³ãã表示ãã¾ãã
é常ã«èãå®è£
ã§ãããããã ãã§è¤æ°ã®ç¶æ
é·ç§»ã管çãããã¨ãã§ãã¾ãã
UiStackTemplate
ãå©ç¨ããå´ã®ã³ã¼ããç°¡æ½ã«ã¾ã¨ã¾ã£ã¦ãã¾ãã
// isLoading: boolean // error?: error // list: Item[] <UiStackTemplate isLoading={isLoading} isError={!!error)} isBlank={list.length == 0)} > <ul> {list.map(item => ( <li key={item.id}> {item.name} </li> ))} </ul> </UiStackTemplate>
ãªããPartialã¹ãã¼ãã®ç®¡çã¯æ¬ã³ã³ãã¼ãã³ãã®æ©è½ã¨ãã¦æä¾ãã¦ãã¾ããã Partialã¹ãã¼ãã§ã¯ã¦ã¼ã¶ã«å¯¾ãã¦ãããªãã¢ã¯ã·ã§ã³ã®åæ©ã¥ããè¡ããIdealã¹ãã¼ãã«å°ããã¨ãæ±ãããã¾ããã¤ã¾ãPartialã¹ãã¼ãã«ã¯åå¥æ§ãè¦æ±ãããã¨ãããã¨ã§ãã ãããã£ã¦ãã¬ã¼ã ã¯ã¼ã¯ã®ãããªçµ±ä¸çãªè§£æ±ºçãæä¾ãããã¨ãé£ããã¨èãã¾ããã
Providerãã¿ã¼ã³
å ã«ãè¿°ã¹ãã¨ãããå¼ç¤¾ã§ã¯ã¹ãã«ã»ããã®ç°ãªã大å¢ã®ã¨ã³ã¸ãã¢ãæ¬ãããã¯ãã®éçºã«æºããã¾ãã ã¾ããå¤ãã®ï¼ã»ã¼ãã¹ã¦ã®ï¼ãããã¯ãã«ããã¦è©³è¿°ãããããã¥ã¡ã³ããåå¨ãã¦ããªããããåã¨ã³ã¸ãã¢ã¯å¿ è¦ã«å¿ãã¦github issueãPRãèªã¿è¿ãã¦ä»æ§ãææ¡ãã¾ãã ãã®ãããªç¶æ³ã®ããã社å ã©ã¤ãã©ãªã¨ãã¦ã¯ã§ããã ãä»æ§ãåç´æå¿«ãªãã®ã好ã¾ãã¾ãã
Providerãã¿ã¼ã³ã§å®è£ ããã³ã³ãã¼ãã³ãã¯ç´ æ´ãªDOMãæ±ããããªç´æçãªå®è£ ãå¯è½ã§ãã Reactã«æ £ãã¦ããªãã¨ã³ã¸ãã¢ã§ãã³ã¼ããèªãã°å¤§ä½ã®æåãç解ã§ããã¨ããå©ç¹ãããã¾ãã
HOCãåä½ã§å©ç¨ãããããã¯ä¸è¦åé¡ãªãããã«è¦ãã¾ãã
ã¨ããããè¤æ°ã®HOCãï¼withIntl
ã withRouter
ã¨ããå
·åã«...ï¼çµã¿åãããã¯ãããã¨é端ã«è¤éããå¢ãã¾ãã
å¾ã
ãã¡ã³ããã³ã¹ãèããå ´åã«ã¯ãProviderãã¿ã¼ã³ãé©ãã¦ããã¨å¤æãã¾ããã
type Props = ReturnType<typeof mapStateToProps> class SomeComponent extends React.Component<Props> { public render() { return (/* something */); } } const mapStateToProps = (state: RootState) => ({ courses: getCourses(state), }); // exportãããã³ã³ãã¼ãã³ãã®åã¯â¦ï¼ export default compose( withIntl, withRouter, withAuthenticate, withUser, connect(mapStateToProps) )(SomeComponent)
ã¾ããæè¦ã»æ½è±¡çãªè©±ã«ãªã£ã¦ãã¾ãæ縮ã§ãããHOCã«ããå®è£ ã®å ´åã«ã¯ãå¼æ°ã¨ãã¦æ¸¡ãã³ã³ãã¼ãã³ãã®ãå¤å´ã«æ©è½ãä»å ãã¦ãããã¤ã¡ã¼ã¸ã«ãªãã¾ãã ã³ã³ãã¼ãã³ãã®å é¨ç¶æ ã®é·ç§»ã表ç¾ã§ãããã¬ã¼ã ã¯ã¼ã¯ï¼å®æ ã¯ã³ã³ãã¼ãã³ãã§ããï¼ãæ³å®ãã¦ãããããããããå å´ã«å¯¾ãã¦æ©è½ãæä¾ãããå®è£ ã¢ããã¼ãã®ã»ããç¸æ§ãè¯ãã¨æãã¾ãã
ããã«å¯¾ãã¦Providerãã¿ã¼ã³ã«ããå®è£ ã®å ´åã«ã¯ãã³ã³ãã¼ãã³ãã®å é¨ã«å¦çãè¨è¿°ã§ãããããä»åã®ã±ã¼ã¹ã«åã£ã¦ããã¨è¨ãã¾ãã
ã¡ãªã¿ã« React17 React16.9ã§è¿½å ãããSuspenseã«é¢ãã¦ã§ããã UiStackTemplate
ãå®è£
ããæç¹ã§ã¯ç§èªèº«ãSuspenseã®åå¨ãèªèãã¦ããªãã£ãããã«åç´ã«èæ
®ããæ¼ãã¦ãã¾ããã
ï¼ã¾ã 詳ããæ¤è¨ã¯ã§ãã¦ãã¾ããããæ¬ãããã¯ãã§ã¯ã»ã¼å
¨ã¦ã®HTTPéä¿¡ãtypescript-fsa + reduxã§å¶å¾¡ãã¦ãããããç´ ç´ãªæ¹æ³ã§ã¯Suspenseã®å©ç¹ãæ´»ãããå®è£
ãã§ããªãã¨æãã¦ãã¾ãï¼
å¾ç¥æµã«ãªã£ã¦ãã¾ãã¾ããã Providerãã¿ã¼ã³ã«ããå®è£
ã«ãã UiStackTemplate
ã³ã³ãã¼ãã³ãèªä½ãããããå©ç¨ããå´ã®ã³ã¼ãããé常ã«ç°¡åã«è¨è¿°ã§ãã¦ãããçµæçã«ã¯ãã®æ¹éã§è¯ãã£ãã¨æãã¦ãã¾ãã
ã¾ããæ¬æ稿å·çæã«é¢é£ã©ã¤ãã©ãªã調ã¹ã¦ã¿ãã¨ãããé¡ä¼¼ã®å®è£ ãå ¬éããã¦ããæ¹ããã¾ããããã¡ãã§ã¯HOCã«ããå®è£ ãã¿ã¼ã³ãã©ã¤ãã©ãªã¨ãã¦æä¾ããã¦ããããã§ãã
å¿å°ããUX/DXã¯æä¾ã§ããã
å®éã«åæ ¼ç¹è¨ã³ã¼ã¹åè¬çã¸æä¾ãã¦ãããµã¼ãã¹ã®ç»é¢ããè¦ããã¾ãã ãªãã¥ã¼ã¢ã«å¾ã®ãä»é±ã®èª²é¡ãã¯ããããç¶æ ãã¨ã«ç°ãªãViewã表示ãã¾ãã ãã®ä¾ã§ã¯ãBlank, Loadingã®ï¼ç¶æ ã§å°ç¨ã«ç¨æããViewãå©ç¨ãã¦ãã¾ãããErrorã«ã¤ãã¦ã¯æ±ç¨ã®æ¥ç¶ã¨ã©ã¼ç¨ã®ã³ã³ãã¼ãã³ãã表示ããã¦ãã¾ãã
blank state
loading state
error state
Ideal state
ã¤ãã« UiStackTemplate
ã®å©ç¨ç¶æ³ãè¦ã¦ããã¾ãã
ç¾å¨ãæ¬ãããã¯ãã«ã¯500+ã®ã³ã³ãã¼ãã³ããåå¨ãããããããã®10%+ã«ãã㦠UiStackTemplate
ãå©ç¨ããã¦ããããã§ãã
å¼ç¤¾å
ã®ã¨ã³ã¸ãã¢ã«ã¯ååã«å©ç¨ããã¦ããã¨è¨ãã¾ãã
ããã«ãUiStackTemplate
ãå©ç¨ãã¦ããã³ã³ãã¼ãã³ãã®ãã¡67%ã (Error|Blank|Loading)Component
ãæå®ãã¦ãããã«ã¹ã¿ãã¤ãºæ§ãååã«æ´»ãããã¦ãã¾ãã
# ãªãã¸ããªä¸ã®.tsxãã¡ã¤ã«ã対象ã¨ãã¦æ¤ç´¢ bash-3.2$ find src | grep -e .tsx | grep -v __tests__ | wc -l 502 bash-3.2$ find src | grep -e .tsx | grep -v __tests__ | xargs grep -l -e "UiStackTemplate" | wc -l 52 bash-3.2$ find src | grep -e .tsx | grep -v __tests__ | xargs grep -l -e "UiStackTemplate" | xargs grep -l -e ErrorComponent -e BlankComponent -e LoadingComponent | wc -l 35
ã¾ã¨ã
- å¿å°ããUXã表ç¾ããããã«ï¼ã¤ã®ç¶æ é·ç§»ãèããå¿ è¦ããã
- ç¶æ
é·ç§»ãç°¡åã«å®è£
ããããã«
UiStackTemplate
ã³ã³ãã¼ãã³ããå°å ¥ãã - ã¹ãã«ã»ããã®ç°ãªã大å¢ã®ã¨ã³ã¸ãã¢ã触ããããããã«Providerãã¿ã¼ã³ãæ¡ç¨ãã
- æ¥æ¥ã®ãªãã¥ã¼ã¢ã«ãªãªã¼ã¹ã«åãã¦ãQuipperã§ã¯æ°ããªä»²éãåéãã¦ãã¾ã