ウォーターセル株式会社の社内勉強会 https://water-cell.connpass.com/event/178648/ で発表したものです。
YouTube Liveアーカイブはこちら https://youtu.be/ZLUie-ndKgw
Hooks࣌ͷઃܭͷReact Hooksษڧձ #62020.6.25 Yukiya Nakagawa a.k.a Nkzn1
View Slide
Yukiya NakagawaΥʔλʔηϧࣾһ൪߸3൪ϞόΠϧΤϯδχΞٕज़ॻయWebΛReact.FCͱHooksͰϦϓϨΠεͨ͠ओ൜2
ΞδΣϯμ• ࠓճݴٴ͢Δʮઃܭʯ• Fat Controller͔Βߟ͑Δڽूͱ݁߹• React Hooksͱڽूɾ݁߹• ڽूɾ݁߹ʹӨڹ͢ΔϑοΫɺ͠ͳ͍ϑοΫ• ڽूɾ݁߹͕ѱ͘ͳΔϑοΫΛͲ͏ѻ͏͔• StorybookΛ͍͍ͨ߹͏Ұखؒ3
ࠓճݴٴ͢Δʮઃܭʯ• ઃܭͱݱ࣮ੈքͷ՝Λղܾ͢ΔͨΊʹ1ͭ·ͨز͔ͭͷ؍͔ΒଥͳࢪࡦΛݕ౼ɾܾఆ͢Δ͜ͱʢཁग़యʣ• ϑΝΠϧϑΥϧμҾͷ্खͳཧͷํɺઃܭͰͳ͘ߏͱݺΕΔ• ͲΜͳ՝Λղܾ͢ΔͨΊʹɺͲΜͳ؍ͰߏΛܾΊ͍͔ͯ͘• React Hooks͕ੜΈग़͢ߏɺڽूͱ݁߹ͷ՝Λղܾ͍͢͠ʢؾ͕͢ΔʣͷͰɺࠓճڽूͱ݁߹ͷΛ͠·͢4
Fat Controller͔Βߟ͑Δڽूͱ݁߹5
GUIϑϨʔϜϫʔΫͷӬԕͷ՝• Fat Controller• UIͷݟͨΛѻ͑ΔείʔϓʹɺશͯͷϩδοΫΛॻ͘ελΠϧ• ReactͷؔίϯϙʔωϯτͰ͍͏ͱɺreturnͷ্6
React Componentas aFat Controllerconst FatController = () => {return ({hoge}{fuga}{piyo})};ෳͷؔ৺ʹର͢Δॲཧ͕ίϯϙʔωϯτʹϕλॻ͖͞Ε͍ͯΔঢ়ଶ࠷ऴతʹදࣔ͢Δσʔλͷཧʢ6*ͷؔ৺ʣߦΫϦοΫΠϕϯτͷड͚औΓʢ6*ͷؔ৺ʣɿߦॳظԽ༻ͷ௨৴ʢ௨৴ͷؔ৺ʣߦσʔλૹ৴༻ͷ௨৴ʢ௨৴ͷؔ৺ʣɿߦ+496*ͷؔ৺௨৴݁ՌΛදࣔ༻σʔλʹՃ͢ΔʢՃͷؔ৺ʣɿߦ7
Fat ControllerͷԿ͕ѱ͍ͷ͔• WebϑϩϯτΤϯυͰ͋Εɺ΄ͱΜͲͷॲཧେผͯ͠ʮ௨৴ʯʮՃʯʮදࣔ/Πϕϯτड(UI)ʯͷͲΕ͔ͷؔ৺Λ࣋ͭ͜ͱ͕ଟ͍• Fat ControllerͰ͜ΕΒͷॲཧ͕Ұಉʹհ͢Δ• ࠷ॳʹ࣮ͨ࣌͠Ͱಈ͘͠ɺ࣮ऀͷهԱʹ࿈ଓੑ͕͋ΔݶΓϝϯςͰ͖ΔϏδωεϩδοΫΛؚΉ8
— Yukiya Nakagawa (1987- )“͔࣮͠͠ऀهԱΛࣦ͏”9
ɿڽू͕͘ɺ݁߹͕ߴ͍• ඦߦʹΔ༷ʑͳతΛ࣋ͬͨॲཧͷதͰɺվमରͷػೳʹ֘͢ΔߦΛݟ͚ͭΔ࡞ۀࠔΛۃΊΔʢڽू͕͍ʣ• ͋Δ1ߦΛमਖ਼ͨ͠߹ͷӨڹൣғΛҙਂ֬͘ೝ͠ͳ͚Ε͍͚ͳ͍ʢ݁߹͕ߴ͍ʣ• ಛఆͷेߦ͔ΒͳΔҰ෦ͷػೳ͚ͩΛଞͷίϯϙʔωϯτͰ࠶ར༻͍ͨ͠ͱࢥͬͨ߹ʹɺͲͷߦΛൈ͖ग़͍͍͔ͤΘ͔Βͳ͍ɺ͋Δ͍ίϐϖ͢Δ͔͠ͳ͍ʢ݁߹͕ߴ͍ʣ10
ཧ͜Ε• ڽू͕ߴ͍ߏ• ࣅͨతͷॲཧ͕·ͱΊΒΕ͍ͯΔ• ݁߹͕͍ߏ• ͋·Γؔͷͳ͍ॲཧಉ͕࢜ૄ݁߹Ͱ͋Δ11
ڽू͕ߴͯ݁͘߹͕͍ߏͮ͘Γ• ࣅͨతͷॲཧΛಉ͡ϑΝΠϧϑΥϧμʹ·ͱΊͯɺ͏ͱ͖Πϯϙʔτ͢Δ• ʮ௨৴ʯʮՃʯʮදࣔ/Πϕϯτडʯͯ͢ผͷతͷͨΊʹಈ͍͍ͯΔͷͰผϑΝΠϧʹ͢ΔͱΑ͍• ʮ͜ͷछྨͷॲཧͳΒ͍͍ͩͨ͜ͷϑΥϧμΛݟʹ͍͚͋Δͳʯ͕Ͱ͖Δͷ͍͍͜ͱ• ʮ͜ͷϑΝΠϧΛमਖ਼ͯ͠յΕΔͷ͜ͷϑΝΠϧͷॲཧ͚ͩͩͳʯ͕Ͱ͖Δͷ͍͍͜ͱ• ES Modules͕࣮͞ΕͯຊʹΑ͔ͬͨ(6*ΞϓϦέʔγϣϯͷϑΝΠϧɾϑΥϧμཧ͜Ε͕Ͱ͖Ε͍͍ͩͨউར12
Betterconst FatController = () => {return ({hoge}{fuga}{piyo})};UIҎ֎ͷॲཧͷৄࡉΛผϑΝΠϧʹ͍ग़͢ڽू͔ͳΓ্͕ͬͨ݁߹গ͠Լ͕͚ͬͨͩͰ·ͩڧ͍࠷ऴతʹදࣔ͢Δσʔλͷཧʢ6*ͷؔ৺ʣߦΫϦοΫΠϕϯτͷड͚औΓʢ6*ͷؔ৺ʣɿߦॳظԽ༻ͷ௨৴ʢ௨৴ͷؔ৺ʣߦʢผϑΝΠϧͷϞδϡʔϧΛݺͼग़͚ͩ͢ʣσʔλૹ৴༻ͷ௨৴ʢ௨৴ͷؔ৺ʣɿߦʢผϑΝΠϧͷϞδϡʔϧΛݺͼग़͚ͩ͢ʣ+496*ͷؔ৺ʢ௨৴ॲཧͷϞδϡʔϧ͔ΒݺΕ͍͚ͯͨͩͳͷͰফ໓ʣ13
React Hooksͱڽूɾ݁߹14
React.FCͷڽूɾ݁߹͕ߴ͍ʁ͍ʁ• propsʹج͍ͮͨදࣔͷΈΛߦ͏ίϯϙʔωϯτʢ͍ΘΏΔPresentationalComponentʣɺڽू͕ߴ͘ɺ݁߹͕͍• ϑοΫΛ1ͭͰͬͨίϯϙʔωϯτɺ݁߹͕গ্͕͠Δ• ωΨςΟϒͳͰͳ͘ɺࣗવͳ͜ͱͱͯ͠• ڽू͕Լ͕Δ͔Ͳ͏͔߹ʹΑΔ• ϑοΫΛͨ͘͞Μॻ͘ͱɺͦΕͳΓʹڽू͕Լ͕ͬͨΓ݁߹্͕͕ͬͨΓ͢Δ• Fat Controllerͷಓ15
ΧελϜϑοΫྑ͍ӨڹΛٴ΅͢• ॲཧΛΧελϜϑοΫʹ·ͱΊΔͱɺؔίϯϙʔωϯτ͔Βѻ͏ϑοΫͷ͕ݮΔͷͰɺ݁߹͕Լ͕Δ• ΧελϜϑοΫΛదͳཻʹ·ͱΊΔ͜ͱ͕Ͱ͖Εɺڽू্͕͕Δ16
More Betterconst FatController = () => {return ({hoge}{fuga}{piyo})};UIʹؔ৺ͷ͋Δॲཧ͔͠ѻΘͳ͍ΧελϜϑοΫʹͯ͢Λ͍ग़͢࠷ऴతʹදࣔ͢Δσʔλͱߋ৽ํ๏ͷΈΛఏڙ͢ΔΧελϜϑοΫͷݺͼग़͠ɿߦΫϦοΫΠϕϯτͷड͚औΓʢ6*ͷؔ৺ʣɿߦ+496*ͷؔ৺17
Real Worldconst Articles = () => {const { data, loading, error } = useArticlesService();if (loading) {return }if (error) {return }if (!data?.articles) {return }return (<>{props.articles.map(article => (title: {article.title}summary:{article.summary}))}>);};ݟͨ͘ͳ͍ͷӅṭ͢ΔΧελϜϑοΫͷதFatͰΑ͍ʢFat ModelOKʣGraphQL + Apollo ClientΛ͍ͬͯΔͱΧελϜϑοΫͷΘΓʹuseQueryͰߦ͚ͨΓ͢Δʢ௨৴ͷӅṭ͕Ͱ͖͍ͯΔˍՃॲཧ͕ݮΔʹ͋ΔͨΊʣ'BUʹͳΔཁҼΛΧελϜϑοΫʹด͡ࠐΊΔ18
ڽूɾ݁߹ʹӨڹ͢ΔϑοΫɺ͠ͳ͍ϑοΫ19
ϑοΫ10छྨ͔͠ͳ͍• ͍ΖΜͳΧελϜϑοΫΛோΊ͍ͯΔͱΕͦ͏ʹͳΔΜ͚ͩͲɺVirtual DOMͷࠩݕग़ॲཧΛ࢘Δreact-reconcilerʢௐΛͨΒ͢ऀʣͱ݁ͨ͠ʮຊͷʯϑοΫ10छྨ͔͠ͳ͍• ࣮masterΛݟʹߦ͘ͱɺConcurrent ModeͳͲͷབྷΈͰ15ݸʹ૿͍͑ͯΔ*1Μ͚ͩͲɺͻͱ·ͣݟͳ͔ͬͨ͜ͱʹͯ͠ཉ͍͠• ૿͑ͨʹڵຯ͕͋ΔਓConcurrent ModeͷυΩϡϝϯτ*2ΛಡΜͰ΄͍͠1. https://github.com/facebook/react/blob/30b47103d4354d9187dc0f1fb804855a5208ca9f/packages/react-reconciler/src/ReactFiberHooks.new.js#L110-L1242. https://ja.reactjs.org/docs/concurrent-mode-patterns.html 20
ϑοΫ10छྨ͔͠ͳ͍• υΩϡϝϯτ্ʮجຊͷϑοΫʯʮՃͷϑοΫʯͱ͍͏༻සʁʹΑΔྨ͕ߦΘΕ͍ͯΔ͚Ͳɺઃܭ্͏গ͠ผͷΓޱͰྨͯ͠ೝ͓͍ࣝͯͨ͠΄͏͕ྑ͍21
ঢ়ଶΛѻ͏ϑοΫʢ7%0.ߋ৽ͷىʹͳΕΔͷࣄ্࣮͜Ε͚ͩʣw VTF4UBUFw VTF3FEVDFSঢ়ଶΛ͢ΔϑοΫ w VTF$POUFYU෭࡞༻Λѻ͏ϑοΫw VTF&⒎FDUw VTF-BZPVU&⒎FDUϝϞԽΛѻ͏ϑοΫw VTF$BMMCBDLw VTF.FNPखଓ͖తϓϩάϥϛϯάΛѻ͏ϑοΫw VTF3FGw VTF*NQFSBUJWF)BOEMFಈ࡞ʹӨڹ͠ͳ͍ϑοΫ w VTF%FCVH7BMVF22
ڽूɾ݁߹ͷӨڹ• Ͳ͜ͷ֊ͷίϯϙʔωϯτʹஔ͍ͯڽूɾ݁߹ʹ΄΅Өڹ͠ͳ͍• ϝϞԽΛѻ͏ϑοΫ• खଓ͖తϓϩάϥϛϯάΛѻ͏ϑοΫ• ಈ࡞ʹӨڹ͠ͳ͍ϑοΫ• ڽूɾ݁߹ʹμΠϨΫτʹӨڹ͢Δ• ঢ়ଶΛѻ͏ϑοΫʢσʔλʹؔ৺͕͋Δʣ• ෭࡞༻Λѻ͏ϑοΫʢ෭࡞༻Ͱ֎քʹܨ͕Γ͕ͪʣ• ঢ়ଶΛ͢ΔϑοΫʢ֎ք͔ΒσʔλΛྲྀ͠ࠐΉʣ23
ҙ͖͢ͷ3ྨ͚ͩ• ঢ়ଶΛѻ͏ϑοΫʢ㲈 useStateʣ• ෭࡞༻Λѻ͏ϑοΫʢ㲈 useEffectʣ• ঢ়ଶΛ͢ΔϑοΫʢ㲈 useContextʣجຊͷϑοΫͩʜʜʂʁ24
ڽूɾ݁߹ʹӨڹ͕͋ΔϑοΫΛͲ͏ѻ͏͔25
ڽूɾ݁߹ʹӨڹ͕͋ΔϑοΫΛͲ͏ѻ͏͔• Ͱ͖Δ্͚ͩҐͷίϯϙʔωϯτͰར༻ͨ͠΄͏͕Α͍• Atomic DesignͰ͍͏ͱ͜ΖͷOrganisms͘Β͍ͷॴͰ͏Α͏ʹҙࣝ͢ΔͱΑ͍• MoleculesAtomsͰɺίϯϙʔωϯτͷ֎ଆʹӨڹΛٴ΅͞ͳ͍ͷͳΒOK• ֎քͱͷଓΛΧελϜϑοΫʹҰຊԽ͢Δ• Reactͷ֎ଆʢΠϯλʔωοτReduxʣͱΓͱΓ͢ΔॲཧΛΧελϜϑοΫʹ·ͱΊΔͱɺίϯϙʔωϯτͱͷ݁߹Λ࠷ݶʹ͑Δ͜ͱ͕Ͱ͖Δ• ࣗ࡞ͷΧελϜϑοΫ͚ͩͰͳ͘ɺαʔυύʔςΟͷΧελϜϑοΫʢuseQuery,useSWR, useFetch, useSelectorͳͲͳͲʣΛίϯϙʔωϯτʹઃஔͯ͠Α͍ϖΠϯ୯Ґ͘Β͍ʹͳΔΜ͡Όͳ͍͔ͳͱ26
StorybookΛ͍͍ͨ߹͏Ұखؒ27
Storybook + React Hooks͋Δ͋Δ• ֎ք͕བྷΉίϯϙʔωϯτStorybookͰಈ͔ͮ͠Β͍• ೝূ͕ඞཁͳ௨৴Λ͏useEffectΧελϜϑοΫ͕ઃஔ͞Ε͍ͯΔ߹͕ಛʹਏ͍• ֎քͱͷ݁߹Λθϩʹͨ͠ίϯϙʔωϯτ͚͕ͩଘࡏΛڐ͞ΕΔ• ྫ֎తʹͱͷີ݁߹ڐ͞ΕΔ28
Presentational and Container Components• 2015ͷهࣄ͕ॳग़*1• 2019ͷDan Abramovʮίʔυϕʔε্͜ͷΛߦ͏͜ͱ͕ࣗવͰ͋Εศརʹ͑Δ͚Ͳɺͦ͏Ͱͳ͍ͳΒ͜ͷํ๏ΛӏವΈʹ͢ΔͷΊͨ΄͏͕͍͍ɻෳࡶͳϩδοΫΛ͍͚ͨͩ͠Ͱ͋ΕɺϑοΫΛ͑ίϯϙʔωϯτΛΘ͟Θ͟͢Δඞཁͳ͍͔ΒͰ͢ʯͱݴ͍ͬͯΔ• େͯ͠ෳࡶͳϩδοΫ͍࣋ͬͯͳ͍ͷʹɺࢮͰContainerΛΓग़͍ͯ͠ΔϓϩδΣΫτΛ͔ͦ͜͜͠Ͱݟ͔͚ͯݏʹͳͬͨΒ͍͠• ͱ͍͑༗ޮͳͱ͖༗ޮͳͷͰɺྫྷ੩ʹ͍͖͍ͬͯͨ1. https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0 29
Presentational and Container Components1SFTFOUBUJPOBM$PNQPOFOUT$POUBJOFS$PNQPOFOUT༻్ ݟͨΛఆٛ͢Δ ৼΔ͍Λఆٛ͢Δ3FBDUͷ֎ଆΛ Βͳ͍ ͍ͬͯΔ4UPSZCPPLͰ දࣔͰ͖Δ දࣔͰ͖ͳ͍͜ͱ͕ଟ͍30
StorybookΛ͍͍ͨ߹͏Ұखؒ• ContainerʮReactͰͳ͍ੈքͱͷڥքʯͱͯ͠ಇ͘• ֎քͱͷ૭ޱʹͳΔΧελϜϑοΫContainer Componentsʹઃஔ͢ΔͱΑ͍31
Presentationaland ContainerComponents// src/components/articles.jsexport const ArticlesComponent = (props) => (<>{props.articles.map(article => (title: {article.title}summary:{article.summary}))}>);// src/containers/articles.jsexport const Articles = () => {const { data, loading, error } = useArticlesService();useErrorHandler(error);if (loading) {return }if (!data?.articles) {return }return };ContainerଆʹʢΧελϜʣϑοΫΛઃஔ͢Δ͜ͱͰɺPresentationalଆ͔Β֎քͷ݁߹ΛԼ͛Δ֎քͱͷΓͱΓ$POUBJOFSʹͤΔඇಉظঢ়ଶͷཧΤϥʔϋϯυϦϯάΛͲ͜ͰΔ͔ٞͷ༨͕͋Γͦ͏1SFTFOUBUJPOBMݟͨͷఆٛʹઐ೦͢Δ32
͓͠·͍