React: ๋ฒ„๊ทธ: ์ˆ˜์ •ํ•˜๊ธฐ ๋„ˆ๋ฌด ์–ด๋ ต์Šต๋‹ˆ๋‹ค. "๋‹ค๋ฅธ ๊ตฌ์„ฑ ์š”์†Œ์˜ ํ•จ์ˆ˜ ๋ณธ๋ฌธ ๋‚ด๋ถ€์—์„œ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."

์— ๋งŒ๋“  2020๋…„ 02์›” 28์ผ  ยท  109์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: facebook/react

# ์ฐธ๊ณ : React 16.13.1์€ ์ด๊ฒƒ์ด ์˜ค๋ฒ„ํŒŒ์ด์–ด๋˜๋Š” ์ผ๋ถ€ ๊ฒฝ์šฐ๋ฅผ ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. React ๋ฐ ReactDOM์„ 16.13.1๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ•ด๋„ ๊ฒฝ๊ณ ๊ฐ€ ์ˆ˜์ •๋˜์ง€ ์•Š์œผ๋ฉด https://github.com/facebook/react/issues/18178#issuecomment -595846312๋ฅผ ์ฝ์–ด๋ณด์„ธ์š”.

๋ฐ˜์‘ ๋ฒ„์ „:

16.13.0

๋ฒˆ์‹ ๋‹จ๊ณ„

  1. ํƒ€์ž„๋จธ์‹ ์„ ๋งŒ๋“œ์„ธ์š”.
  2. 2017๋…„์œผ๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.
  3. 10,000์ค„์˜ ์ฝ”๋“œ๋กœ ๊ตฌ์„ฑ๋œ ๊ฑฐ๋Œ€ํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋นŒ๋“œํ•˜์‹ญ์‹œ์˜ค.
  4. ๋” ์ด์ƒ ์œ ์ง€ ๊ด€๋ฆฌ๋˜์ง€ ์•Š๋Š” ์ข…์†์„ฑ์„ ํฌํ•จํ•˜์—ฌ package.json ํŒŒ์ผ์—์„œ 80(!) ์ข…์†์„ฑ์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
  5. 2020๋…„ 2์›” 27์ผ React๋ฅผ ์ตœ์‹  ๋ฒ„์ „์œผ๋กœ ์—…๋ฐ์ดํŠธํ•˜์„ธ์š”.
  6. ์ˆ˜์ • ๋ฐฉ๋ฒ•์„ ๋ชจ๋ฅด๋Š” ์ˆ˜๋งŽ์€ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
  7. ์ˆ˜์ •์—๋Š” ์•Œ ์ˆ˜ ์—†๋Š” ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฌ๊ณ  $$$ + ์ˆ˜์ผ ๋˜๋Š” ๋ช‡ ์ฃผ๊ฐ€ ์†Œ์š”๋  ๊ฒƒ์ด๋ผ๊ณ  ๊ณ ๊ฐ์—๊ฒŒ ์•Œ๋ฆฌ์‹ญ์‹œ์˜ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด $$$ ๋น„์šฉ์ด ๋” ๋งŽ์ด ๋“œ๋Š” ์˜ค๋ž˜๋œ ๋ฒ„์ „์˜ React ๋ฐ ๊ด€๋ จ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์˜์›ํžˆ ๊ณ ์ฐฉ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋‚˜์ค‘์—.

์ง„์ง€ํ•˜๊ฒŒ, ๋‚ด๊ฐ€ ์ผํ•˜๋Š” ํšŒ์‚ฌ๋Š” ๊ทธ๊ฒƒ์— ์ „ํ˜€ ๊ด€์‹ฌ์ด ์—†์Šต๋‹ˆ๋‹ค. ๋ถ„๋ช…ํžˆ ๋‚˜๋Š” โ€‹โ€‹์ผ์ฐ์ด ๊ฒฝ๊ณ ๋ฅผ ๋ฐ›์•˜๋‹ค๋ฉด ๊ทธ๋Ÿฌํ•œ ๊ฒฝ๊ณ ๊ฐ€ ๋‚˜ํƒ€๋‚˜๋„๋ก ํ•œ ์ ์ด ์—†์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ๋กœ์„œ๋Š” ์˜ค๋ฅ˜๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ์ด ๋ถˆ๊ฐ€๋Šฅํ•  ์ •๋„๋กœ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ์ €๋Š” ๋งŽ์€ ๋‹ค๋ฅธ ๊ฒฝ์šฐ์™€ ๊ฑฐ๋Œ€ํ•œ ์Šคํƒ ์ถ”์ ์œผ๋กœ ์˜ค๋ฅ˜๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋‚˜ํƒ€๋‚˜๋Š” ์˜ค๋ฅ˜ ์ค‘ ํ•˜๋‚˜ ์ด์ƒ์„ ์ˆ˜์ •ํ•˜๋ ค๊ณ  ์‹œ๋„ํ–ˆ์ง€๋งŒ ์ด๋ฏธ ๋งŽ์€ ์‹œ๊ฐ„์ด ๊ฑธ๋ ธ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ค‘ ์ผ๋ถ€๋ฅผ ๋””๋ฒ„๊ทธํ•˜๋ ค๊ณ  ์‹œ๋„ํ–ˆ์ง€๋งŒ ์šด์ด ์—†์—ˆ์Šต๋‹ˆ๋‹ค.

ํ•œ ๊ฐ€์ง€ ์˜ˆ:

image

๊ฑฐ๊ธฐ์—์„œ ์šฐ๋ฆฌ๋Š” ๊ตฌ์‹ ๋ฐ˜์‘ ๋ผ์šฐํ„ฐ, ๊ตฌ์‹ redux-connect(๊ตฌ์‹ componentWillReceiveProps ๋ฉ”์†Œ๋“œ์˜ ์˜ค๋ฅ˜๋ฅผ ์ˆ˜์ •ํ•˜๊ธฐ ์œ„ํ•ด ํ”„๋กœ์ ํŠธ ์†Œ์Šค์— ๋„ฃ์–ด์•ผ ํ•จ), recompose ๋“ฑ์— ์˜ํ•ด ์ƒ์„ฑ๋œ ์ผ๋ถ€ HOC์˜ ์‚ฌ์šฉ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ๊ฐœ๋ฐœํ•œ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์‚ดํŽด๋ณด๊ณ  ๋ฒ„๊ทธ๋ฅผ ์ˆ˜์ •ํ•˜๊ธฐ ์œ„ํ•ด setState ๋ฌธ์ž์—ด๋กœ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ๋Š” ๋‹จ์ˆœํ•œ ๊ฐ€์ƒ DOM ํŠธ๋ฆฌ๊ฐ€ ์•„๋‹ˆ๋ผ ํ›จ์”ฌ ๋” ๋ณต์žกํ•ฉ๋‹ˆ๋‹ค.

์ด ์˜ค๋ฅ˜๋ฅผ ๋น„ํ™œ์„ฑํ™”ํ•˜๊ฑฐ๋‚˜ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ์œ„์น˜๋ฅผ ์ฐพ๋Š” ๋” ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•˜๋ ค๋ฉด "UNSAFE" ์˜ต์…˜์„ ๋งŒ๋“œ์„ธ์š” ๐Ÿ™

Discussion

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

๋ฏธ๋ž˜์˜ ๋Œ“๊ธ€ ์ž‘์„ฑ์ž์—๊ฒŒ. ์ƒˆ๋กœ์šด ๊ฒฝ๊ณ ๊ฐ€ ํ‘œ์‹œ๋˜๋Š” ๊ฒƒ์ด ๋‹ต๋‹ตํ•˜๋‹ค๋Š” ์ ์„ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ฝ”๋“œ์— ๋ฒ„๊ทธ๋ฅผ ์ผ์œผํ‚ฌ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋Š” ํ•ฉ๋ฒ•์ ์ธ ๋ฌธ์ œ๋ฅผ ์ง€์ 

์Šคํƒ ์ถ”์ ์—์„œ ์ถœ์ฒ˜๋ฅผ ์ดํ•ดํ•  ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ ์Šคํฌ๋ฆฐ์ƒท์„ ๊ฒŒ์‹œํ•˜๊ฑฐ๋‚˜ ์žฌ์ƒ์‚ฐ ์ƒŒ๋“œ๋ฐ•์Šค๋ฅผ ์ƒ์„ฑํ•ด ์ฃผ์‹œ๋ฉด ๋„์™€๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค. ๋Œ€๋ถ€๋ถ„์€ ๋ช‡ ๊ฐœ์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ๊ฐ€์ ธ์˜จ ๊ฒƒ์ผ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๊ฐ€์žฅ ์ƒ์‚ฐ์ ์ธ ๋ฐฉ๋ฒ•์€ ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ๋ฅผ ์ค„์ด๊ณ  ํ•ด๋‹น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋ฌธ์ œ๋ฅผ ์ œ๊ธฐํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋‹ค๋“ค ๊ฐ์‚ฌ ํ•ด์š”.

๋ชจ๋“  109 ๋Œ“๊ธ€

์ด ๊ฒฝ๊ณ ๋ฅผ ๋” ์ผ์ฐ ์ถ”๊ฐ€ํ–ˆ์œผ๋ฉด ํ•ฉ๋‹ˆ๋‹ค. ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. Hooks์˜ ๋„์ž…์œผ๋กœ ์ธํ•œ ์‹ค์ˆ˜์˜€์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ด๊ฒƒ์ด Hooks๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ƒˆ๋กœ์šด ์ฝ”๋“œ๋กœ ์ธํ•ด ๋ฐœ์ƒํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ํ›จ์”ฌ ๋” ์ผ์ฐ ํด๋ž˜์Šค์— ๋Œ€ํ•ด ์ด๋ฏธ ๋™์ผํ•œ ๊ฒฝ๊ณ ๊ฐ€ ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด์ „ ์ฝ”๋“œ์—์„œ๋Š” ์ด๋ฏธ ์ด ๊ฒฝ๊ณ ๋ฅผ ๋ณด์•˜์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ํ–ฅํ›„ ๋ฒ„์ „์˜ React์—์„œ ํ•˜๋“œ ์‹คํŒจ๋ฅผ ์‹œ์ž‘ํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์Šต๋‹ˆ๋‹ค. ์˜๋„์ ์ด๋“  ์•„๋‹ˆ๋“ (์ด ํŒจํ„ด์—๋Š” ๋งŽ์€ ๋ฒ„๊ทธ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค). ๋”ฐ๋ผ์„œ ๊ด€๊ณ„์—†์ด ๊ฒฐ๊ตญ ์ด์ „ ๋ฒ„์ „์— ๊ฐ‡ํž ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ณ ์น  ์ˆ˜ ์—†๋‹ค๋ฉด ์ด์ „ ๋ฒ„์ „์˜ React์— ๊ณ ์ •ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ž‘์„ฑ์ž์˜ ๋„์›€์„ ๋ฐ›์•„ ์ˆ˜์ • ๋ฒ„์ „์„ ๊ฒŒ์‹œํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ฒƒ์„ ํฌํ•จํ•˜์—ฌ ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ์ตœ๋Œ€ํ•œ ์‰ฝ๊ฒŒ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€๋“œ๋ฆฌ๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.

Chrome ์ฝ˜์†”์—์„œ ์ž‘์€ > ํ™”์‚ดํ‘œ๋ฅผ ํ™•์žฅํ•˜๋ฉด ์ถ”๊ฐ€ ์Šคํƒ ์ถ”์ ๋„ ํ‘œ์‹œ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค(์Šคํฌ๋ฆฐ์ƒท์˜ ๊ตฌ์„ฑ ์š”์†Œ ์Šคํƒ ์™ธ์—). ๊ทธ๊ฒƒ๋„ ์˜ฌ๋ ค์ฃผ์‹ค ์ˆ˜ ์žˆ๋‚˜์š”? ๊ทธ๋Ÿฌ๋ฉด ๋ Œ๋”๋ง์—์„œ ๋ถ€์ž‘์šฉ์„ ์ผ์œผํ‚ค๋Š” ์ •ํ™•ํ•œ ํ˜ธ์ถœ ์‚ฌ์ดํŠธ๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

๋‚˜์™€ ํ•จ๊ป˜ ์ด๊ฒƒ์€ ๋‚ด๊ฐ€ formik์„ ์‚ฌ์šฉํ•  ๋•Œ ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค.

image

@sebmarkbage ๋‹ต๋ณ€ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. >๋ฅผ ํด๋ฆญํ•œ ํ›„ ๋‚˜ํƒ€๋‚˜๋Š” ์Šคํƒ ์ถ”์ ์€ ์šฐ์Šค๊ฝ์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค. 200๊ฐœ ์ด์ƒ์˜ ํ•ญ๋ชฉ์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค!

๊ฑฐ๊ธฐ์— ๋ถ™์—ฌ ๋„ฃ๊ฑฐ๋‚˜ pastebin์— ๋Œ€ํ•œ ๋งํฌ๋ฅผ ์ œ๊ณตํ•˜๋ ค๊ณ ํ–ˆ์ง€๋งŒ ๋‹ค๋ฅธ ๋ฐฉํ–ฅ์œผ๋กœ ์‹œ๋„ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ผ๋ถ€ ์‚ฌ์šฉ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ Github ๋ฌธ์ œ๋ฅผ ์‚ดํŽด๋ณด๊ณ  ์šฉ์˜์ž ์ค‘ ํ•˜๋‚˜๊ฐ€ redux-form( https://github.com/redux-form/redux-form/issues/4619) ์ด๋ผ๋Š” ๊ฒƒ์„ ์•Œ์•˜์Šต๋‹ˆ๋‹ค

๊ทธ๋Ÿฌ๋‚˜ ์—ฌ์ „ํžˆ ์ด ๋ฌธ์ œ๋ฅผ ๋‹ซ์ง€ ์•Š๋„๋ก ์š”์ฒญํ•˜๊ณ  ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž์—๊ฒŒ ์ด๋Ÿฌํ•œ ์˜ค๋ฅ˜๋ฅผ ์ผ์œผํ‚ค๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์—ฌ๊ธฐ์—์„œ ์–ธ๊ธ‰ํ•  ๊ฒƒ์„ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค.

@RodolfoSilva ๊ทธ๊ฒƒ์ด formik์— ์˜ํ•œ ๊ฒƒ์ด๋ผ๊ณ  ํ™•์‹ ํ•ฉ๋‹ˆ๊นŒ? ๊ทธ๋ ‡๋‹ค๋ฉด ๊ฑฐ๊ธฐ์—์„œ ๋ฌธ์ œ๋ฅผ ๋งŒ๋“ค๊ณ  ์—ฌ๊ธฐ์— ๋Œ€ํ•œ ๋งํฌ๋ฅผ ๊ฒŒ์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? ๋ชฉ๋ก์— ํ•ญ๋ชฉ์ด ๋‘ ๊ฐœ ์ด์ƒ ํฌํ•จ๋  ๊ฒฝ์šฐ ์ฒซ ๋ฒˆ์งธ ๋ฉ”์‹œ์ง€ ์‹œ์ž‘ ๋ถ€๋ถ„์— ์ด๋Ÿฌํ•œ ๋ฌธ์ œ ๋ชฉ๋ก์„ ๋งŒ๋“ค๊ฒ ์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ์ •๋ง ๋นจ๋ฆฌ ํ•ด๊ฒฐ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์—…๊ทธ๋ ˆ์ด๋“œ๋ฅผ ๋ถˆ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์˜ค๋ฅ˜ ์ถ”์ ์€ ๊ฑฐ์˜ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

ํ . ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€์—์„œ ์ฐพ์„ ์ค„์„ ์„ค๋ช…ํ•˜๋Š” ๊ฒƒ์ด ๋„์›€์ด ๋ ์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค.

์ด ๊ฒฝ์šฐ ์ฐพ์„ ์ฒซ ๋ฒˆ์งธ ์ค„์€ dispatchAction ๋’ค์˜ ์ค„์ž…๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์ด React๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

@RodolfoSilva ๊ณต์œ  ํ•  ์ˆ˜ ์žˆ๋Š” FormItemInput.js ์˜ ์ถœ์ฒ˜๋ฅผ ๊ฒŒ์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? 71ํ–‰์—์„œ dispatch ๋˜๋Š” setState๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์˜ค๋ฅ˜๋ฅผ ์ผ์œผํ‚ค๋Š” ์ฝ”๋“œ ํ–‰์„ ์ •ํ™•ํžˆ ํฌํ•จํ•˜๋„๋ก ์ด ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ์ด ํ•„์ˆ˜์ ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋ฌธ์ œ์˜ ์›์ธ์ด ๋กœ์ปฌ ์ฝ”๋“œ์ธ์ง€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ฝ”๋“œ์ธ์ง€ ์ •ํ™•ํžˆ ํŒŒ์•…ํ•˜๋Š” ๊ฒƒ์€ ๊ฑฐ์˜ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. react-redux ๋ฐ react-router์™€ ๊ฐ™์€ ์™ธ๋ถ€ ๊ฐ€๋Šฅ์„ฑ ์ด ๋งค์šฐ

๋‚˜๋Š” ์—ฌ๊ธฐ์„œ React-Redux๊ฐ€ ์ž˜๋ชป์ด ์•„๋‹ˆ๋ผ๊ณ  ํ™•์‹ ํ•˜์ง€๋งŒ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€์™€ ๊ตฌ์„ฑ ์š”์†Œ ์Šคํƒ์ด ์‹ค์ œ๋กœ ๋ฌด์Šจ ์ผ์ด ์ผ์–ด๋‚˜๊ณ  ์žˆ๋Š”์ง€ ์•Œ๊ธฐ ์–ด๋ ต๊ฒŒ ๋งŒ๋“ ๋‹ค๋Š” ๋ฐ ๋™์˜ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” Redux-form๊ณผ ๊ฐ™์€ ๋ฌธ์ œ์— ์ง๋ฉดํ•ด ์žˆ์Šต๋‹ˆ๋‹ค!

๋‚˜๋Š” ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์žˆ์œผ๋ฉฐ ๋‚ด redux ํ•„๋“œ ์— ์ฒ˜์Œ์œผ๋กœ ์“ธ ๋•Œ ๋˜๋Š” ๋ชจ๋“  ๊ฒƒ์„

๋‚˜๋Š” ๊ทธ ๋ฌธ์ œ๋„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋‚ด ๊ตฌ์„ฑ ์š”์†Œ์ž…๋‹ˆ๋‹ค.

`const [๊ฐ€๊ฒฉ, setPrice] = useState(0);

const updatePrice = (newPrice) => {
setPrice(newPrice)
}
< CardContainer onPriceUpdated={updatePrice} > CardContainer >
`

๋”ฐ๋ผ์„œ ์ด ๊ฒฝ์šฐ ๋‚ด CardContainer ๊ตฌ์„ฑ ์š”์†Œ๋Š” ๊ฐ€๊ฒฉ์ด ์—…๋ฐ์ดํŠธ๋˜๊ณ  ์ƒ์œ„ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์ƒˆ ์™•์ž๋ฅผ ๋ Œ๋”๋งํ•  ๋•Œ ์ƒ์œ„ ๊ตฌ์„ฑ ์š”์†Œ์— ์•Œ๋ฆฝ๋‹ˆ๋‹ค.
๊ทธ๋ž˜์„œ React๋Š” ๋‚ด๊ฐ€ ๋‹ค๋ฅธ ๊ตฌ์„ฑ ์š”์†Œ์˜ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌ์„ฑ ์š”์†Œ์˜ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋ ค๊ณ  ํ•œ๋‹ค๊ณ  ๊ฒฝ๊ณ ํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.
๋‚˜๋Š” ๋ฐ˜์‘์— ์ต์ˆ™ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์ด๊ฒƒ์ด React ํŒจํ„ด์ธ์ง€ ๋˜๋Š” ์‹ค์ œ๋กœ ๋ฒ„๊ทธ์ธ์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ด ๊ฒฝ๊ณ ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ์ œ์•ˆ ์‚ฌํ•ญ์ด ์žˆ์œผ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.``

@l0gicgate

์˜ค๋ฅ˜๋ฅผ ์ผ์œผํ‚ค๋Š” ์ฝ”๋“œ ํ–‰์„ ์ •ํ™•ํžˆ ํฌํ•จํ•˜๋„๋ก ์ด ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ์ด ํ•„์ˆ˜์ ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

JavaScript๋กœ ํ•  ์ˆ˜ ์žˆ๋Š” ์ผ์—๋Š” ํ•œ๊ณ„๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋ชจ๋“  ์ •๋ณด๋Š” ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋Š” ์Šคํƒ์— ์žˆ์Šต๋‹ˆ๋‹ค . React ๋‚ด๋ถ€์— ์žˆ๋Š” ์ค„์„ ๊ฑด๋„ˆ๋›ฐ๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

JavaScript ์Šคํƒ์„ ๋ณด๋ ค๋ฉด ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ์˜†์— ์žˆ๋Š” ์ž‘์€ ํ™”์‚ดํ‘œ ๋ฅผ ํ•ฉ๋‹ˆ๋‹ค .

์˜ˆ๋ฅผ ๋“ค์–ด ์ด์ „์˜ ์ด ์Šคํฌ๋ฆฐ์ƒท์„ ๋ณด์‹ญ์‹œ์˜ค.

75614021-cb812980-5b12-11ea-8a6e-a38f4cd6aeef

์Šคํƒ์„ ํŒŒํ—ค์น˜๋Š” ๊ฒƒ์ด ์•ฝ๊ฐ„ ์„ฑ๊ฐ€์‹  ์ผ์ด์ง€๋งŒ ์ฒ˜์Œ ๋ช‡ ํ”„๋ ˆ์ž„์„ ๊ฑด๋„ˆ๋›ฐ๋Š” ๊ฒƒ์€ ๊ทธ๋ ‡๊ฒŒ ์–ด๋ ต์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ”๋กœ ๋‹ค์Œ ํ”„๋ ˆ์ž„์ด ๋ฌธ์ œ์˜ ์›์ธ์ž…๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ Formik ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋‚ด๋ถ€์— ์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋ฌธ์ œ๋ฅผ ์ œ๊ธฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@martinezwilmer ์ด ์˜ˆ์ œ๋Š” ๋„ˆ๋ฌด ์ž‘์•„์„œ ๋„์›€์ด ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ƒŒ๋“œ๋ฐ•์Šค๋ฅผ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”.

๋ฏธ๋ž˜์˜ ๋Œ“๊ธ€ ์ž‘์„ฑ์ž์—๊ฒŒ. ์ƒˆ๋กœ์šด ๊ฒฝ๊ณ ๊ฐ€ ํ‘œ์‹œ๋˜๋Š” ๊ฒƒ์ด ๋‹ต๋‹ตํ•˜๋‹ค๋Š” ์ ์„ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ฝ”๋“œ์— ๋ฒ„๊ทธ๋ฅผ ์ผ์œผํ‚ฌ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋Š” ํ•ฉ๋ฒ•์ ์ธ ๋ฌธ์ œ๋ฅผ ์ง€์ 

์Šคํƒ ์ถ”์ ์—์„œ ์ถœ์ฒ˜๋ฅผ ์ดํ•ดํ•  ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ ์Šคํฌ๋ฆฐ์ƒท์„ ๊ฒŒ์‹œํ•˜๊ฑฐ๋‚˜ ์žฌ์ƒ์‚ฐ ์ƒŒ๋“œ๋ฐ•์Šค๋ฅผ ์ƒ์„ฑํ•ด ์ฃผ์‹œ๋ฉด ๋„์™€๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค. ๋Œ€๋ถ€๋ถ„์€ ๋ช‡ ๊ฐœ์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ๊ฐ€์ ธ์˜จ ๊ฒƒ์ผ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๊ฐ€์žฅ ์ƒ์‚ฐ์ ์ธ ๋ฐฉ๋ฒ•์€ ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ๋ฅผ ์ค„์ด๊ณ  ํ•ด๋‹น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋ฌธ์ œ๋ฅผ ์ œ๊ธฐํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋‹ค๋“ค ๊ฐ์‚ฌ ํ•ด์š”.

์—ฌ๊ธฐ์—์„œ ๊ฒฝ๊ณ ์™€ ๊ด€๋ จ๋œ ์ •ํ™•ํ•œ ๋ผ์ธ์„ ์ฐพ๊ธฐ๊ฐ€ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.

@gaearon ๋˜ ํ•œ๊ฐ€์ง€ ํŒ์ด ์žˆ๋‚˜์š”?

image

์—ฌ๊ธฐ์—์„œ ๊ฒฝ๊ณ ์™€ ๊ด€๋ จ๋œ ์ •ํ™•ํ•œ ๋ผ์ธ์„ ์ฐพ๊ธฐ๊ฐ€ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.

๋ฌด์—‡์ด ๊ทธ๊ฒƒ์„ ์–ด๋ ต๊ฒŒ ๋งŒ๋“œ๋Š”๊ฐ€? ์œ„์—์„œ ์–ธ๊ธ‰ํ–ˆ๋“ฏ์ด ์˜ค๋ฅธ์ชฝ์— "๋ฐ˜์‘"์ด๋ผ๊ณ  ํ‘œ์‹œ๋˜์ง€ ์•Š์€ ์ฒซ ๋ฒˆ์งธ ์ค„์ž…๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ Redux์˜ connectAdvanced ์ž…๋‹ˆ๋‹ค. ๊ด€๋ฆฌ์ž๊ฐ€ ์ด๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋„๋ก React Redux์— ๋ฌธ์ œ๋ฅผ ์ œ์ถœํ•˜์„ธ์š”.

๋‚ด๊ฐ€ upthread์— ๋Œ€ํ•ด ๋งํ–ˆ๋“ฏ์ด ์—ฌ๊ธฐ์„œ ์ผ์–ด๋‚˜๋Š” ๋ชจ๋“  ๊ฒƒ์ด React-Redux์˜ ๋ฌธ์ œ๋ผ๋ฉด _๋งค์šฐ_ ๋†€๋ž„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ฆ‰, ์ฒ˜์Œ์— ์ด ๋ฉ”์‹œ์ง€๋ฅผ ํŠธ๋ฆฌ๊ฑฐํ•œ ์›์ธ์ด ์ •ํ™•ํžˆ ๋ฌด์—‡์ธ์ง€์กฐ์ฐจ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๊ฐ€ ๋ฌด์—‡์„ ๋งํ•˜๋Š”์ง€ ๋ฐ˜์ฏค ์ดํ•ดํ–ˆ์ง€๋งŒ ์‹ค์ œ๋กœ ์–ด๋–ค ์ข…๋ฅ˜์˜ ์•ฑ ์ฝ”๋“œ ํŒจํ„ด์ด ํ•ด๋‹น ๋™์ž‘์˜ ์˜ˆ๊ฐ€ ๋ ๊นŒ์š”?

๋‚˜๋Š” ์ตœ๊ทผ์ด์— ๋‹ฌ๋ ค ์ˆ˜์ •์€ ํฌ์žฅ ๋œ setState ์—์„œ ํ•ธ๋“ค๋Ÿฌ ํ˜ธ์ถœ ์‚ฌ์ดํŠธ useEffect ๊ณผ ๊ฐ™์ด : https://github.com/airbnb/lunar/commit/db08613d46ea21089ead3e7b5cfff995f15c69a7#diff (-1c3bdd397b1ce5064142488877045306R56 onChange ๋ฐ onSubmit ๋Š” ์ฒด์ธ ์œ„์ชฝ์—์„œ setState ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

@martinezwilmer onPriceUpdated ๋Š” ์–ด๋””์—์„œ ํ˜ธ์ถœ๋˜๋‚˜์š”? useEffect ํฌ์žฅํ•ด ๋ณผ๊นŒ์š”?

urql ๋Œ€ํ•ด ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” ์—…๋ฐ์ดํŠธ๋ฅผ ์กฐ์ •ํ•˜๊ธฐ ์œ„ํ•ด use-subscription + wonka(์ŠคํŠธ๋ฆผ์šฉ)๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์ง€๋งŒ ์—…๋ฐ์ดํŠธ๋Š” ๋™๊ธฐ์ ์œผ๋กœ ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์šฐ๋ฆฌ๋Š” ์ด๋ฏธ todos ๊ฐ€์ ธ์™”์œผ๋ฏ€๋กœ Open ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ๊ฒฐ๊ณผ๊ฐ€ ์ฆ‰์‹œ ํŒ์—…๋˜์–ด์•ผ ํ•˜์ง€๋งŒ ์ด๊ฒƒ์ด ๋‹ค์Œ ์˜ค๋ฅ˜๋ฅผ ์œ ๋ฐœํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

image

์šฐ๋ฆฌ์˜ ๊ฒฝ์šฐ ์ด๊ฒƒ์ด ์˜๋„๋œ ๊ฒƒ์ด๋ฏ€๋กœ ๋™๊ธฐํ™” ๊ฒฐ๊ณผ์— ๋Œ€ํ•ด fetching: true ๋ฅผ ํ‘œ์‹œํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ๋ถˆ์•ˆ์ •ํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ urql , Apollo ์™€ ๊ฐ™์€ ํƒ€์‚ฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์ ์  ๋” ๋งŽ์ด ๋‚˜ํƒ€๋‚˜๊ธฐ ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ด๊ฒƒ์— ๋ถ€๋”ช์ณค๊ณ  ๋ช‡ ์‹œ๊ฐ„ ๋™์•ˆ ๋ฌธ์ œ๊ฐ€ ๋‚ด ์ฝ”๋“œ์— ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ์••์ถ•๋œ ์Šคํƒ ์ถ”์ ์€ ๋‚ด ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋ฉฐ, ๋‚ด๊ฐ€ ๋ช…์‹œ์ ์œผ๋กœ ์˜ค๋ฅ˜๋ฅผ _did_ ํ–ˆ์„ ๋•Œ ํ™•์žฅ๋œ ์Šคํƒ ์ถ”์ ์—์„œ ํƒ€์‚ฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋ณด๋Š” ๊ฒƒ์€ ๋“œ๋ฌธ ์ผ์ด ์•„๋‹™๋‹ˆ๋‹ค. ์ด ํŠน์ • ๊ฒฝ๊ณ ์— ๋Œ€ํ•œ (์ œํ•œ์ ์ด์ง€๋งŒ) ์—ฐ๊ตฌ์—์„œ ๋Œ€๋ถ€๋ถ„์˜ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ด ๋ฌธ์ œ๋ฅผ ์Šค์Šค๋กœ ์ผ์œผํ‚ค๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๋ฐœ์ƒํ•˜๋Š” ์ฝ”๋“œ์— ์˜์กดํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ์—…์ŠคํŠธ๋ฆผ ๋ฒ„๊ทธ๊ฐ€ ์•„๋‹ˆ๋ผ๊ณ  ๊ฐ€์ •ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์ง€๋งŒ ์—…์ŠคํŠธ๋ฆผ ๋ฒ„๊ทธ์ธ ๊ฒฝ์šฐ ์ฝ”๋“œ์—์„œ ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๋ฌธ์ œ๋ฅผ ์ถ”์ ํ•˜๋Š” ๋ฐ ์‹œ๊ฐ„์„ ๋‚ญ๋น„ํ•˜๋Š” ๊ฒƒ์€ ๋‹ค์†Œ ์‹ค๋ง์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค. ์ผ๋ฐ˜ ์‚ฌ์šฉ์ž๊ฐ€ ์ž์‹ ์ด ์ž‘์„ฑํ•œ ์ฝ”๋“œ์ธ์ง€, ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚จ ์ฝ”๋“œ์— ์˜์กดํ•˜๋Š”์ง€ ํŒ๋‹จํ•  ์ˆ˜ ์žˆ๋„๋ก React๊ฐ€ ํ•  ์ˆ˜ ์žˆ๋Š” ์ผ์ด ์žˆ์Šต๋‹ˆ๊นŒ?

Apollo ๋ฌธ์ œ์—์„œ ๋‚ด๊ฐ€ ์ฃผ๋ชฉํ•˜๋Š” ํ•œ ๊ฐ€์ง€๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๊ฒฝ๊ณ ์˜ ์Šคํƒ ์ถ”์ ์€ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์— ์˜ํ•ด ๋‹ค์‹œ ๋ Œ๋”๋ง๋˜๋Š” ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์•„๋‹ˆ๋ผ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ดˆ๊ธฐํ™”ํ•œ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ๋งž๋‹ค๋ฉด React๊ฐ€ ์—ฌ๊ธฐ์— ๋” ๋งŽ์€ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? ์‹œ์ž‘ ๊ตฌ์„ฑ ์š”์†Œ์™€ ์—…๋ฐ์ดํŠธ๋ฅผ ์œ ๋ฐœํ•œ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋ชจ๋‘ ์•Œ๋ ค์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?

@hugo ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ƒˆ๋กœ์šด Ionic ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ํ…Œ์ŠคํŠธํ•  ๋•Œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฌธ์ œ ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

  1. npx ionic start demo sidemenu --type=react
  2. react-scripts test

์‹ค์ œ๋กœ ์›์ธ์€ ์Šคํƒ ํŠธ๋ ˆ์ด์Šค์˜ ์ค‘๊ฐ„๊ณผ ํ•˜๋‹จ์— ๋ฌปํ˜€ ์žˆ์Šต๋‹ˆ๋‹ค.

console.error node_modules/react-dom/cjs/react-dom.development.js:88
    Warning: Cannot update a component from inside the function body of a different component.
        in Route (at App.tsx:37)
        in View (created by StackManagerInner)
        in ViewTransitionManager (created by StackManagerInner)
        in ion-router-outlet (created by IonRouterOutlet)
        in IonRouterOutlet (created by ForwardRef(IonRouterOutlet))
        in ForwardRef(IonRouterOutlet) (created by StackManagerInner)
        in StackManagerInner (created by Context.Consumer)
        in Unknown (created by Component)
        in Component (created by ForwardRef(IonRouterOutlet))
        in ForwardRef(IonRouterOutlet) (at App.tsx:36)
        in ion-split-pane (created by IonSplitPane)
        in IonSplitPane (created by ForwardRef(IonSplitPane))
        in ForwardRef(IonSplitPane) (at App.tsx:34)
        in NavManager (created by RouteManager)
        in RouteManager (created by Context.Consumer)
        in RouteManager (created by IonReactRouter)
        in Router (created by BrowserRouter)
        in BrowserRouter (created by IonReactRouter)
        in IonReactRouter (at App.tsx:33)
        in ion-app (created by IonApp)
        in IonApp (created by ForwardRef(IonApp))
        in ForwardRef(IonApp) (at App.tsx:32)
        in App (at App.test.tsx:6)

์ด ๋ฌธ์ œ๋Š” ์ด ๋ฌธ์ œ์™€ ๊ด€๋ จํ•˜์—ฌ ๋‚ด๊ฐ€ ์ฐพ์„ ์ˆ˜ ์žˆ๋Š” ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ๋ฌธ์ œ์˜€์Šต๋‹ˆ๋‹ค.

https://github.com/mobxjs/mobx-react/issues/846 ์—์„œ mobx์—์„œ ์ด ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ค๋Š” ํŠน์ • ํŒจํ„ด์„ ์ฐพ์•˜์Šต๋‹ˆ๋‹ค.

@sebmarkbage ๋” ์ด์ƒ ์ด ๋ฌธ์ œ๋ฅผ ์žฌํ˜„ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ผ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ๋ฌธ์ œ๋ฅผ ์—…๋ฐ์ดํŠธํ–ˆ์Šต๋‹ˆ๋‹ค.

@jgoux ์šฐ๋ฆฌ๋„ ๊ฐ™์€ ๋ฌธ์ œ์— ์ง๋ฉดํ•ด ์žˆ๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค @Clovis ^^ Spotted

react 16.13.0 ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ ๋ฐ˜์‘ ์ดํ›„์— ์ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค. ํŠน์ • ์ž‘์—…์ด ์™„๋ฃŒ๋œ ํ›„ ๋‚ด ๊ตฌ์„ฑ ์š”์†Œ ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋‹ค๋ฅธ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฌธ์ œ๋Š” ๋งค์šฐ ๋ช…ํ™•ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด๊ฒƒ์ด ์™œ ๊ฒฝ๊ณ ๋ฅผ ๋˜์ง€๋Š”์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์ œ์•ˆ์ด ์žˆ์Šต๋‹ˆ๊นŒ?

@gaearon ์Šคํƒ์—์„œ ๋””๋ฒ„๊น…ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์„ธ๋ถ€ ์ •๋ณด์— ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค. ํ•ด๋‹น ์˜ˆ์ œ๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด ๊ฐœ์ธ์ ์œผ๋กœ ์ด ์˜ค๋ฅ˜๊ฐ€ ์–ด๋””์—์„œ ์™”๋Š”์ง€ ์•Œ ์ˆ˜ ์—†์—ˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๐Ÿ™

๋‚ด ๋ฌธ์ œ๊ฐ€ ๊ด€๋ จ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์ง€๋งŒ ๋‚ด ์–‘์‹ ๊ตฌ์„ฑ ์š”์†Œ์˜ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋ ค๊ณ  ์‹œ๋„ํ•˜๊ณ  ์žˆ์ง€๋งŒ onChange ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ ค๊ณ  ํ•  ๋•Œ๋งˆ๋‹ค ์ด ์˜ค๋ฅ˜๊ฐ€ ๊ณ„์† ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” react-jsonschema-form์„ ์‚ฌ์šฉํ•˜๊ณ  Form ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๊ฐ€์ ธ์™”๊ณ  ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ ์œ„ํ•ด onChange ์†์„ฑ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚˜์—๊ฒŒ ์ด๊ฒƒ์€ ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ค๋Š” ํŒจํ„ด์ž…๋‹ˆ๋‹ค.

image

์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ฝ˜์†” ๋กœ๊ทธ๋Š” ๋‚˜๋ฅผ 385ํ–‰์œผ๋กœ ๋ฐ”๋กœ ๊ฐ€๋ฆฌ์ผฐ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ฒ˜์Œ ๋ฐ˜์‘ํ•˜์ง€๋งŒ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฝ”๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

import React, { useState } from "react";

function Home() {
  const [mobileNavOpen, setMobileNavOpen] = useState(false);
  return (
    <div>
      <button
        onClick={(): void => setMobileNavOpen(true)}
        type="button"
        className="btn"
      >
        X
      </button>
      {mobileNavOpen && <MobileNav setMobileNavOpen={setMobileNavOpen}/>}
    </div>
  );
}

function MobileNav() {
  return (
    <div>
      <button
        onClick={setMobileNavOpen(false)} //problem here
        type="button"
        className="btn"
      >
        X
      </button>
    </div>
  );
}

export default Home;

๊ฒฐ๊ณผ: Cannot update a component from inside the function body of a different component.

๋‹ค์Œ๊ณผ ๊ฐ™์ด MobileNav์˜ setMobileNavOpen์— ํ™”์‚ดํ‘œ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

import React, { useState } from "react";

function Home() {
  const [mobileNavOpen, setMobileNavOpen] = useState(false);
  return (
    <div>
      <button
        onClick={(): void => setMobileNavOpen(true)}
        type="button"
        className="btn"
      >
        X
      </button>
      {mobileNavOpen && <MobileNav setMobileNavOpen={setMobileNavOpen}/>}
    </div>
  );
}

function MobileNav() {
  return (
    <div>
      <button
        onClick={(): void => setMobileNavOpen(false)} //fixes problem
        type="button"
        className="btn"
      >
        X
      </button>
    </div>
  );
}

export default Home;

๊ทธ๋ฆฌ๊ณ  ๊ทธ๊ฒƒ์€ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ๋ˆ„๊ตฐ๊ฐ€๋ฅผ ๋•๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค!

๋‚˜๋Š” ์ฒ˜์Œ ๋ฐ˜์‘ํ•˜์ง€๋งŒ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฝ”๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

import React, { useState } from "react";

function Home() {
  const [mobileNavOpen, setMobileNavOpen] = useState(false);
  return (
    <div>
      <button
        onClick={(): void => setMobileNavOpen(true)}
        type="button"
        className="btn"
      >
        X
      </button>
      {mobileNavOpen && <MobileNav setMobileNavOpen={setMobileNavOpen}/>}
    </div>
  );
}

function MobileNav() {
  return (
    <div>
      <button
        onClick={setMobileNavOpen(false)} //problem here
        type="button"
        className="btn"
      >
        X
      </button>
    </div>
  );
}

export default Home;

๊ฒฐ๊ณผ: Cannot update a component from inside the function body of a different component.

๋‹ค์Œ๊ณผ ๊ฐ™์ด MobileNav์˜ setMobileNavOpen์— ํ™”์‚ดํ‘œ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

import React, { useState } from "react";

function Home() {
  const [mobileNavOpen, setMobileNavOpen] = useState(false);
  return (
    <div>
      <button
        onClick={(): void => setMobileNavOpen(true)}
        type="button"
        className="btn"
      >
        X
      </button>
      {mobileNavOpen && <MobileNav setMobileNavOpen={setMobileNavOpen}/>}
    </div>
  );
}

function MobileNav() {
  return (
    <div>
      <button
        onClick={(): void => setMobileNavOpen(false)} //fixes problem
        type="button"
        className="btn"
      >
        X
      </button>
    </div>
  );
}

export default Home;

๊ทธ๋ฆฌ๊ณ  ๊ทธ๊ฒƒ์€ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ๋ˆ„๊ตฐ๊ฐ€๋ฅผ ๋•๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค!

๋‹น์‹ ์˜ ์˜ˆ๋Š” ์‹ค์ œ๋กœ ์‚ฌ๋žŒ๋“ค์ด react์—์„œ ์ €์ง€๋ฅด๋Š” ์ดˆ๊ธฐ ์‹ค์ˆ˜ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ ๋…ผ์˜๋˜๋Š” ๊ฒƒ๊ณผ ์ •ํ™•ํžˆ ๊ฐ™์€ ๋ฌธ์ œ์ธ์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ท€ํ•˜์˜ ๋ผ์ธ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. onClick={setMobileNavOpen(false) ๋Š” ํด๋ฆญ์ด ์•„๋‹ˆ๋ผ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋Š” ๋™์•ˆ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋กœ ๋ž˜ํ•‘ํ•˜๋ฉด ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋ฉ๋‹ˆ๋‹ค.

<Redirect> ์‚ฌ์šฉ์ž๋ฅผ ๋‹ค๋ฅธ ์œ„์น˜๋กœ ๋ณด๋‚ด๊ธฐ ์ „์— Redux ์ž‘์—…์„ ์ „๋‹ฌํ•ด์•ผ ํ•˜๋Š” React Router์—์„œ ์ด ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ฌธ์ œ๋Š” ๋ฐœ์†ก์ด ์™„๋ฃŒ๋˜๊ธฐ ์ „์— ๋ฆฌ๋””๋ ‰์…˜์ด ๋ฐœ์ƒํ•œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๋‚ด ํ–‰๋™์„ ์•ฝ์†ํ•จ์œผ๋กœ์จ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.

์ „์—:

<Route
  render={routeProps => {
    setRedirectionTarget(somePath(routeProps));
    return <Redirect to={someOtherPath} />;
  }}
/>;

function mapDispatchToProps(dispatch: ThunkDispatch) {
  return {
    setRedirectionTarget: (target: string | null) => dispatch(setRedirectionTarget(target))
  };
}

export const setRedirectionTarget = (location: string | null): SetRedirectionTarget => {
  return {
    type: SET_REDIRECTION_TARGET,
    location
  };
};

ํ›„์—:

function mapDispatchToProps(dispatch: ThunkDispatch) {
  return {
    setRedirectionTarget: async (target: string | null) => dispatch(await setRedirectionTarget(target))
  };
}

export const setRedirectionTarget = (location: string | null): Promise<SetRedirectionTarget> => {
  return Promise.resolve({
    type: SET_REDIRECTION_TARGET,
    location
  });
};

์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€์— ํ˜„์žฌ ๋ Œ๋”๋ง ์ค‘์ธ ๊ตฌ์„ฑ ์š”์†Œ์™€ setState๊ฐ€ ํ˜ธ์ถœ๋˜๋Š” ๊ตฌ์„ฑ ์š”์†Œ ๋ชจ๋‘์— ๋Œ€ํ•œ ์ด๋ฆ„์ด ํฌํ•จ๋˜๋„๋ก ๋งŒ๋“ค ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋ˆ„๊ตฌ๋“ ์ง€ ์ด๊ฒƒ์„ ์œ„ํ•ด PR์„ ๋ณด๋‚ด๊ณ  ์‹ถ์Šต๋‹ˆ๊นŒ?

์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€์— ํ˜„์žฌ ๋ Œ๋”๋ง ์ค‘์ธ ๊ตฌ์„ฑ ์š”์†Œ์™€ setState๊ฐ€ ํ˜ธ์ถœ๋˜๋Š” ๊ตฌ์„ฑ ์š”์†Œ ๋ชจ๋‘์— ๋Œ€ํ•œ ์ด๋ฆ„์ด ํฌํ•จ๋˜๋„๋ก ๋งŒ๋“ค ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋ˆ„๊ตฌ๋“ ์ง€ ์ด๊ฒƒ์„ ์œ„ํ•ด PR์„ ๋ณด๋‚ด๊ณ  ์‹ถ์Šต๋‹ˆ๊นŒ?

์ด ๋ถ€๋ถ„์„ ์‚ดํŽด๋ณด๊ฒŒ ๋˜์–ด ๊ธฐ์ฉ๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ์•Œ์•„์•ผ ํ•  ํฌ์ธํ„ฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

@samcooke98 ์ด PR์„ ์—ด์—ˆ์Šต๋‹ˆ๋‹ค https://github.com/facebook/react/pull/18316

๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์ด ์ง€์ ํ–ˆ๋“ฏ์ด Apollo์™€ ๊ฐ™์€ ํ›„ํฌ์— ๊ตฌ๋…์ด ์žˆ๊ณ  ๋ฐ์ดํ„ฐ ์ˆ˜์‹  ์‹œ ์ €์žฅ์†Œ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋ ค๊ณ  ์‹œ๋„ํ•˜๋Š” ๊ฒฝ์šฐ ์ด ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•œ ์ˆ˜์ •์€ useEffect ์˜ˆ

export const useOrderbookSubscription = marketId => {
  const { data, error, loading } = useSubscription(ORDERBOOK_SUBSCRIPTION, {
    variables: {
      marketId,
    },
  })

  const formattedData = useMemo(() => {
    // Don't dispatch in here.
  }, [data])

  // Don't dispatch here either

  // Dispatch in a useEffect
  useEffect(() => {
    orderbookStore.dispatch(setOrderbookData(formattedData))
  }, [formattedData])

  return { data: formattedData, error, loading }
}

Hyper ์—์„œ ์ด ๋ฌธ์ œ์— ์ง๋ฉดํ–ˆ์ง€๋งŒ ํ›„ํฌ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์žˆ์œผ๋ฉฐ ํ˜ธ์ถœ ์Šคํƒ์˜ ๋ Œ๋”๋ง ํ˜ธ์ถœ์—์„œ ์•„๋ฌด ๊ฒƒ๋„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์Šคํƒ์— UNSAFE_componentWillReceiveProps ํ˜ธ์ถœ์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. componentDidUpdate ์—…๋ฐ์ดํŠธํ•˜๋ฉด ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. https://github.com/zeit/hyper/pull/4382
๋ˆ„๊ตฐ๊ฐ€์—๊ฒŒ ๋„์›€์ด ๋  ์ˆ˜ ์žˆ์œผ๋ฉด ์—ฌ๊ธฐ์— ๊ฒŒ์‹œํ•˜์‹ญ์‹œ์˜ค.

์—ฌ๊ธฐ์—์„œ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ UNSAFE_componentWillMount ํ˜ธ์ถœ์ด ์žˆ์—ˆ๊ณ  ์ด๋ฅผ ๋ณ€๊ฒฝ/์ œ๊ฑฐํ•˜๋ฉด ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์šฐ๋ฆฌ๋Š” ํ›„ํฌ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉฐ ํ˜ธ์ถœ ์Šคํƒ์˜ ๋ Œ๋”๋ง ํ˜ธ์ถœ์—์„œ ์•„๋ฌด ๊ฒƒ๋„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์ด์ƒํ•˜๊ฒŒ ๋“ค๋ฆฝ๋‹ˆ๋‹ค. ๋‚˜๋Š” ๋‹น์‹ ์ด ์–ด๋–ป๊ฒŒ ์ด ๊ฒฝ๊ณ ๋ฅผ ๋ฐ›๋Š”์ง€ ์ž˜ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. setState๊ฐ€ ํ•จ์ˆ˜ ๊ตฌ์„ฑ ์š”์†Œ์— ์†ํ•˜๋Š” ๊ฒฝ์šฐ์—๋งŒ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋‹น์‹ ์˜ ์Šคํƒ์€ ์–ด๋–ป์Šต๋‹ˆ๊นŒ?

๊ทธ๋Ÿฌ๋‚˜ ์šฐ๋ฆฌ๋Š” ํ›„ํฌ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉฐ ํ˜ธ์ถœ ์Šคํƒ์˜ ๋ Œ๋”๋ง ํ˜ธ์ถœ์—์„œ ์•„๋ฌด ๊ฒƒ๋„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์ด์ƒํ•˜๊ฒŒ ๋“ค๋ฆฝ๋‹ˆ๋‹ค. ๋‚˜๋Š” ๋‹น์‹ ์ด ์–ด๋–ป๊ฒŒ ์ด ๊ฒฝ๊ณ ๋ฅผ ๋ฐ›๋Š”์ง€ ์ž˜ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. setState๊ฐ€ ํ•จ์ˆ˜ ๊ตฌ์„ฑ ์š”์†Œ์— ์†ํ•˜๋Š” ๊ฒฝ์šฐ์—๋งŒ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋‹น์‹ ์˜ ์Šคํƒ์€ ์–ด๋–ป์Šต๋‹ˆ๊นŒ?

Redux๋กœ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋ถ„๋ฅ˜ํ•˜๊ณ  ๊ตฌ์„ฑ ์š”์†Œ์— ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์ ์šฉํ•˜๋Š” ๊ธฐ๋Šฅ. ์•„๋งˆ๋„ ๊ทธ๊ฒƒ์ด ๊ธฐ๋Šฅ ๊ตฌ์„ฑ ์š”์†Œ๋กœ ๊ฐ„์ฃผ๋˜๋Š” ์ด์œ  ์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์™œ ์ˆ˜๋ช… ์ฃผ๊ธฐ ํ›„ํฌ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋ฉด ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋ฉ๋‹ˆ๊นŒ?

์ž˜ ๋ชจ๋ฅด๊ฒ ์–ด. ์ƒŒ๋“œ๋ฐ•์Šค์—์„œ ๊ฒฉ๋ฆฌ๋œ ์˜ˆ์ œ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? ๋‹น์‹ ์ด ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋‹ค๋ฅธ ์ผ์„ ํ•˜๊ณ  ์žˆ์„์ง€๋„ ๋ชจ๋ฅธ๋‹ค๋Š” ์˜ˆ๊ฐ์ด ๋“ญ๋‹ˆ๋‹ค.

๊ฒฉ๋ฆฌ๋œ ์˜ˆ์—์„œ ๋ณต์ œํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์›์ธ์„ ์ฐพ๋Š” ๋ฐ ์–ด๋ ค์›€์„ ๊ฒช์—ˆ๊ณ  ์Šคํƒ ์ถ”์ ์— ์žˆ๊ณ  ์—…๋ฐ์ดํŠธ ๋Œ€๊ธฐ ์ค‘์ด์—ˆ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ ์ˆ˜๋ช… ์ฃผ๊ธฐ ํ›„ํฌ๋ฅผ ๋ฐฉ๊ธˆ ์—…๋ฐ์ดํŠธํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ์–ด๋–ป๊ฒŒ ๋“  ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.
์—ฌ๊ธฐ ์—์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ ์‹œ์ ์˜ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ ๋‹น์‹œ UNSAFE_componentWillReceiveProps ์™€ componentDidUpdate ๊ฐ€ ๋ชจ๋‘ ๊ตฌ์„ฑ ์š”์†Œ์— ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ(์•ˆ์ „ํ•˜์ง€ ์•Š์€ ๊ฒƒ์ด ์‹ค์ˆ˜๋กœ ๊ฑฐ๊ธฐ์— ๋‚จ์•„ ์žˆ์Œ)?

๋˜ํ•œ ์ด ๊ฒฝ๊ณ ๊ฐ€ ํ‘œ์‹œ๋˜๋ฉฐ ์Šคํƒ ์ถ”์ ์€ setScriptLoaded ํ›„ํฌ๋ฅผ ๊ฐ€๋ฆฌํ‚ต๋‹ˆ๋‹ค(๋‚ด ์‚ฌ์šฉ์ž ์ง€์ • ํ›„ํฌ๋Š” ์•„๋ž˜ ์ฐธ์กฐ). ๋”ฐ๋ผ์„œ useEffect ์‚ฌ์šฉํ•˜๋”๋ผ๋„ setState ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ๋‹ค๋ฅธ ๋น„๋™๊ธฐ ์ฝœ๋ฐฑ ๋‚ด์— ์ค‘์ฒฉ๋˜๋ฉด React๋Š” ์—ฌ์ „ํžˆ ๊ฒฝ๊ณ ๋ฅผ ๋˜์งˆ ๊ฒƒ์ž…๋‹ˆ๋‹ค(์ œ ๊ฒฝ์šฐ์—๋Š” setTimeout ๋ฐ load ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ)? Hooks๋ฅผ ์ฒ˜์Œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๋ฏ€๋กœ ์กฐ์–ธ์„ ์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

/**
 * Detect when 3rd party script is ready to use
 * 
 * <strong i="11">@param</strong> {function} verifyScriptLoaded Callback to verify if script loaded correctly
 * <strong i="12">@param</strong> {string} scriptTagId 
 */

export const useScriptLoadStatus = (verifyScriptLoaded, scriptTagId) => {
    let initLoadStatus = true; // HTML already includes script tag when rendered server-side
    if (__BROWSER__) {
        initLoadStatus = typeof verifyScriptLoaded === 'function' ? verifyScriptLoaded() : false;
    }
    const [isScriptLoaded, setScriptLoaded] = useState(initLoadStatus); 

    useEffect(() => {
        if (!isScriptLoaded) {
            // need to wrap in setTimeout because Helmet appends the script tags async-ly after component mounts (https://github.com/nfl/react-helmet/issues/146)
            setTimeout(() => {
                let newScriptTag = document.querySelector(`#${scriptTagId}`);
                if (newScriptTag && typeof verifyScriptLoaded === 'function') {
                    newScriptTag.addEventListener('load', () => { 
                        return verifyScriptLoaded() ? setScriptLoaded(true) : null;
                    });
                    // double check if script is already loaded before the event listener is added
                    return verifyScriptLoaded() ? setScriptLoaded(true) : null;
                }
            }, 100);
        }
    });

    return isScriptLoaded;
};

@LabhanshAgrawal

๊ทธ ๋‹น์‹œ UNSAFE_componentWillReceiveProps์™€ componentDidUpdate๊ฐ€ ๋ชจ๋‘ ๊ตฌ์„ฑ ์š”์†Œ์— ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ(์•ˆ์ „ํ•˜์ง€ ์•Š์€ ํ•ญ๋ชฉ์ด ์‹ค์ˆ˜๋กœ ๊ฑฐ๊ธฐ์— ๋‚จ์•„ ์žˆ์Œ)?

๋ผ์ดํ”„ ์‚ฌ์ดํด ๋ฐฉ๋ฒ•์ด ์—ฌ๊ธฐ์— ์ „ํ˜€ ๊ด€๋ จ์ด ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ๋‚ด๊ฐ€ ๋‹น์‹ ์˜ ์˜ˆ์—์„œ ๋ญ”๊ฐ€ ์ด์ƒํ•˜๋‹ค๊ณ  ๋งํ•˜๋Š” ์ด์œ ์ž…๋‹ˆ๋‹ค.

@suhanw CodeSandbox ์—์„œ ์™„์ „ํ•œ ์˜ˆ์ œ๋ฅผ ์ œ๊ณตํ•˜์‹ญ์‹œ์˜ค. ์ด ๊ฒฝ๊ณ ๋ฅผ ์œ ๋ฐœํ•ด์•ผ ํ•˜๋Š” ์ฝ”๋“œ์—๋Š” ๋ฌธ์ œ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

@LabhanshAgrawal ์ „์ฒด ์Šคํƒ์„ ๊ฒŒ์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? ๋‚˜๋Š” UNSAFE_componentWillReceiveProps (๋ Œ๋”๋ง๊ณผ ๋™์ผ)๊ฐ€ ๋‹ค๋ฅธ ๊ตฌ์„ฑ ์š”์†Œ์—์„œ setState ๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

backend.js:6 Warning: Cannot update a component from inside the function body of a different component.
    in Term                           (created by _exposeDecorated(Term))
    in _exposeDecorated(Term)         (created by DecoratedComponent)
    in DecoratedComponent             (created by TermGroup_)
    in TermGroup_                     (created by ConnectFunction)
    in ConnectFunction                (created by Connect(TermGroup_))
    in Connect(TermGroup_)            (created by _exposeDecorated(TermGroup))
    in _exposeDecorated(TermGroup)    (created by DecoratedComponent)
    in DecoratedComponent             (created by Terms)
    in div                            (created by Terms)
    in div                            (created by Terms)
    in Terms                          (created by _exposeDecorated(Terms))
    in _exposeDecorated(Terms)        (created by DecoratedComponent)
    in DecoratedComponent             (created by ConnectFunction)
    in ConnectFunction                (created by Connect(DecoratedComponent))
    in Connect(DecoratedComponent)    (created by Hyper)
    in div                            (created by Hyper)
    in div                            (created by Hyper)
    in Hyper                          (created by _exposeDecorated(Hyper))
    in _exposeDecorated(Hyper)        (created by DecoratedComponent)
    in DecoratedComponent             (created by ConnectFunction)
    in ConnectFunction                (created by Connect(DecoratedComponent))
    in Connect(DecoratedComponent)
    in Provider
r                                     @ backend.js:6
printWarning                          @ react-dom.development.js:88
error                                 @ react-dom.development.js:60
warnAboutRenderPhaseUpdatesInDEV      @ react-dom.development.js:23260
scheduleUpdateOnFiber                 @ react-dom.development.js:21196
dispatchAction                        @ react-dom.development.js:15682
checkForUpdates                       @ connectAdvanced.js:88
handleChangeWrapper                   @ Subscription.js:97
(anonymous)                           @ Subscription.js:23
batchedUpdates$1                      @ react-dom.development.js:21887
notify                                @ Subscription.js:19
notifyNestedSubs                      @ Subscription.js:92
checkForUpdates                       @ connectAdvanced.js:77
handleChangeWrapper                   @ Subscription.js:97
(anonymous)                           @ Subscription.js:23
batchedUpdates$1                      @ react-dom.development.js:21887
notify                                @ Subscription.js:19
notifyNestedSubs                      @ Subscription.js:92
handleChangeWrapper                   @ Subscription.js:97
dispatch                              @ redux.js:222
e                                     @ VM64:1
(anonymous)                           @ effects.ts:11
(anonymous)                           @ write-middleware.ts:14
(anonymous)                           @ index.js:11
(anonymous)                           @ plugins.ts:538
(anonymous)                           @ plugins.ts:540
(anonymous)                           @ index.js:11
dispatch                              @ redux.js:638
(anonymous)                           @ sessions.ts:124
(anonymous)                           @ index.js:8
dispatch                              @ VM64:1
onResize                              @ terms.ts:62
(anonymous)                           @ term.js:179
e.fire                                @ xterm.js:1
t.resize                              @ xterm.js:1
e.resize                              @ xterm.js:1
e.fit                                 @ xterm-addon-fit.js:1
fitResize                             @ term.js:291
UNSAFE_componentWillReceiveProps      @ term.js:408
callComponentWillReceiveProps         @ react-dom.development.js:12998
updateClassInstance                   @ react-dom.development.js:13200
updateClassComponent                  @ react-dom.development.js:17131
beginWork                             @ react-dom.development.js:18653
beginWork$1                           @ react-dom.development.js:23210
performUnitOfWork                     @ react-dom.development.js:22185
workLoopSync                          @ react-dom.development.js:22161
performSyncWorkOnRoot                 @ react-dom.development.js:21787
(anonymous)                           @ react-dom.development.js:11111
unstable_runWithPriority              @ scheduler.development.js:653
runWithPriority$1                     @ react-dom.development.js:11061
flushSyncCallbackQueueImpl            @ react-dom.development.js:11106
flushSyncCallbackQueue                @ react-dom.development.js:11094
batchedUpdates$1                      @ react-dom.development.js:21893
notify                                @ Subscription.js:19
notifyNestedSubs                      @ Subscription.js:92
handleChangeWrapper                   @ Subscription.js:97
dispatch                              @ redux.js:222
e                                     @ VM64:1
(anonymous)                           @ effects.ts:11
(anonymous)                           @ write-middleware.ts:14
(anonymous)                           @ index.js:11
(anonymous)                           @ plugins.ts:538
(anonymous)                           @ plugins.ts:540
(anonymous)                           @ index.js:11
dispatch                              @ redux.js:638
effect                                @ ui.ts:60
(anonymous)                           @ effects.ts:13
(anonymous)                           @ write-middleware.ts:14
(anonymous)                           @ index.js:11
(anonymous)                           @ plugins.ts:538
(anonymous)                           @ plugins.ts:540
(anonymous)                           @ index.js:11
dispatch                              @ redux.js:638
(anonymous)                           @ ui.ts:54
(anonymous)                           @ index.js:8
dispatch                              @ VM64:1
(anonymous)                           @ index.tsx:162
emit                                  @ events.js:210
(anonymous)                           @ rpc.ts:31
emit                                  @ events.js:210
onMessage                             @ init.ts:50

@gaearon ๋น ๋ฅธ ๋‹ต๋ณ€ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. CodeSandbox์—์„œ ์˜ˆ์ œ๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋‚ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค(์–ด๋ ค์›€). ํ•˜์ง€๋งŒ ๊ทธ ๋™์•ˆ์€ ์ด๊ฒƒ์ด ์ œ ์ „์ฒด ์Šคํƒ ์ถ”์ ์ž…๋‹ˆ๋‹ค.

๋‚ด ์‚ฌ์šฉ์ž ์ •์˜ ํ›„ํฌ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ์ค„์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. https://gist.github.com/suhanw/bcc2688bba131df8301dae073977654f#file -stack-trace-L144

๋‚ด ์‚ฌ์šฉ์ž ์ง€์ • ํ›„ํฌ ์ „ํ›„์— ์Šคํƒ ์ถ”์ ์— ๋” ์กฐ์‚ฌํ•ด์•ผ ํ•  ๊ฒƒ์ด ์žˆ๋Š”์ง€ ์‚ดํŽด๋ณด๊ณ  ์•Œ๋ ค์ฃผ๋ฉด ์ •๋ง ์ข‹์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ฐ์‚ฌ ํ•ด์š”!

@LabhanshAgrawal ์Šคํƒ์—์„œ UNSAFE_componentWillReceiveProps ๋Š” Redux ์ž‘์—…์„ ์ „๋‹ฌํ•˜๋Š” fitResize ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ์ด๋Š” ์ฐจ๋ก€๋กœ ๋งŽ์€ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋ฌธ์ œ. ์˜ˆ, componentDidUpdate ๋ณ€๊ฒฝํ•˜๋ฉด ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

@suhanw ์Šคํƒ์—์„œ ModuleImpressionTracker ๋ผ๋Š” ๊ฒƒ์ด ์ƒ์„ฑ์ž ๋™์•ˆ Redux ์ž‘์—…์„ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค. ์ƒ์„ฑ์ž๋Š” ๋ถ€์ž‘์šฉ์ด ์—†์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๊ฒƒ์ด ๋‹น์‹ ์˜ Hook์ด ์•„๋‹ˆ๋ผ ๋ฌธ์ œ์˜ ์›์ธ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์•Œ๊ฒ ์Šต๋‹ˆ๋‹ค. UNSAFE_componentWillReceiveProps ๋Š” ๋ Œ๋”๋ง์œผ๋กœ ๊ณ„์‚ฐ๋˜์ง€๋งŒ componentDidUpdate ๋Š” ๊ณ„์‚ฐ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
setState๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ธฐ๋Šฅ ๊ตฌ์„ฑ ์š”์†Œ ๋ฐ ํ›„ํฌ์˜ ๋ฌธ์ œ์ธ ์ ์„ ์œ„์˜ ๋…ผ์˜์—์„œ ๋ช…ํ™•ํ•˜๊ฒŒ ํŒŒ์•…ํ•  ์ˆ˜ ์—†๋‹ค๋Š” ์ ์„ ์กฐ๊ธˆ ๋ช…ํ™•ํžˆ ํ•ด์ฃผ์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?
์งˆ๋ฌธ์ด ์•ฝ๊ฐ„ ์–ด๋ฆฌ์„๊ฑฐ๋‚˜ ์ œ ํ…Œ์ดํฌ ์•„์›ƒ์ด ์ž˜๋ชป๋œ ๊ฒฝ์šฐ ์‚ฌ๊ณผ๋“œ๋ฆฝ๋‹ˆ๋‹ค. ์ €๋Š” ์ด๊ฒƒ์— ์•ฝ๊ฐ„ ์ต์ˆ™ํ•ฉ๋‹ˆ๋‹ค.

@LabhanshAgrawal

setState๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ธฐ๋Šฅ ๊ตฌ์„ฑ ์š”์†Œ ๋ฐ ํ›„ํฌ์˜ ๋ฌธ์ œ์ธ ์ ์„ ์œ„์˜ ๋…ผ์˜์—์„œ ๋ช…ํ™•ํ•˜๊ฒŒ ํŒŒ์•…ํ•  ์ˆ˜ ์—†๋‹ค๋Š” ์ ์„ ์กฐ๊ธˆ ๋ช…ํ™•ํžˆ ํ•ด์ฃผ์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

์†”์งํžˆ ๋‚˜๋Š” ์ค‘๊ฐ„์— ์žˆ๋Š” ๋ชจ๋“  Redux ๊ฒƒ๋“ค ๋•Œ๋ฌธ์— ๋‚˜ ์ž์‹ ์„ ํ™•์‹ ํ•˜์ง€ ๋ชปํ•œ๋‹ค. React Redux๊ฐ€ ๊ตฌํ˜„๋˜๋Š” ๋ฐฉ์‹ ๋•Œ๋ฌธ์— ๊ฝค ํ˜ผ๋ž€์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค. ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ React ๋‹จ๋…์œผ๋กœ ๊ด€๋ จ๋˜์ง€๋งŒ ํด๋ž˜์Šค ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ช…ํ™•ํ•œ ์žฌํ˜„์„ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค๋ฉด ์ข‹์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์—ฌ์ „ํžˆ ๋งŽ์€ ์Šคํƒ ์ถ”์ ์ด ๋ถ™์—ฌ๋„ฃ์–ด์ง€๊ณ  ์œ ํ˜•์— ๊ด€๊ณ„์—†์ด ์‹ค์ œ ๋ฌธ์ œ์— ๋Œ€ํ•œ ์‹ค์ œ ์žฌํ˜„์ด ๋งŽ์ง€ ์•Š์€ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

urql ๊ฒƒ์€ @gaearon์„ ์˜๋„ํ•œ

  • ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€์„œ ๋ Œ๋”๋งํ•˜๋Š” <Todos /> ๋งˆ์šดํŠธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ด์ „์— urqlClient ์—ฐ๊ฒฐ๋œ ์ŠคํŠธ๋ฆผ์„ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋‘ ๋ฒˆ์งธ <Todos /> ๋ Œ๋”๋งํ•˜๋ฉด ๋™์ผํ•œ ์ฟผ๋ฆฌ + ๋ณ€์ˆ˜ ์กฐํ•ฉ์ด ์ƒ์„ฑ๋˜๋ฏ€๋กœ ์ฒซ ๋ฒˆ์งธ ๋‹จ๊ณ„์˜ <Todos /> ์— ๋Œ€ํ•œ ๊ฒฐ๊ณผ๊ฐ€ ์ƒˆ๋กœ ๊ณ ์ณ์ง‘๋‹ˆ๋‹ค.
  • use-subscription ๋Š” ๋‘˜ ๋‹ค์— ๋Œ€ํ•ด ํŠธ๋ฆฌ๊ฑฐ๋ฉ๋‹ˆ๋‹ค.

์žฌํ˜„ - ์ฒซ ๋ฒˆ์งธ ์ฟผ๋ฆฌ๊ฐ€ ๋ Œ๋”๋ง๋  ๋•Œ ์ƒ๋‹จ์—์„œ "์—ด๊ธฐ"๋ฅผ ๋ˆ„๋ฆ…๋‹ˆ๋‹ค.

์—…๋ฐ์ดํŠธ๋ฅผ ๋Œ€๊ธฐ์—ด์— ๋„ฃ์„ ์ˆ˜๋Š” ์žˆ์ง€๋งŒ top-down ์ปจํ…์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ Œ๋”๋งํ•˜์ง€ ์•Š๋Š” ํ•œ ์ด ๋ฌธ์ œ๋ฅผ ์šฐํšŒํ•  ์ˆ˜ ์—†๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๊นŒ? ์ด๋ก ์ ์œผ๋กœ ์ด๊ฒƒ์€ ์ด ๊ฒฝ์šฐ์— ์˜๋„๋œ ๋™์ž‘์ž…๋‹ˆ๋‹ค. Relay๊ฐ€ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค.

ํŽธ์ง‘: use-subscription ์˜ getCurrentValue ๋™์•ˆ ๋ชจ๋“  ๊ตฌ๋…์ž๊ฐ€ ์—…๋ฐ์ดํŠธํ•˜๋„๋ก ํŠธ๋ฆฌ๊ฑฐํ•˜์ง€ ์•Š์Œ์œผ๋กœ์จ urql์˜ ๊ฒฝ์šฐ์— ๋Œ€ํ•œ ์†”๋ฃจ์…˜์„ ์ฐพ์•˜์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

https://github.com/FormidableLabs/urql/commit/3a597dd92587ef852c18139e9781e853f763e930

์ž, ๋” ์ž์„ธํžˆ ์‚ดํŽด๋ณด๋ฉด ์›๋ž˜ ์˜๋„ํ–ˆ๋˜ ๊ฒƒ๋ณด๋‹ค ๋” ๋งŽ์€ ๊ฒฝ์šฐ(์˜ˆ: ํด๋ž˜์Šค์˜ ๊ฒฝ์šฐ)์—์„œ ๊ฒฝ๊ณ ํ•˜๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๊ทธ ์ค‘ ์ผ๋ถ€๋ฅผ ์นจ๋ฌต์‹œํ‚ฌ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@JoviDeCroock ์ด ์˜ˆ์ œ๋Š” urql ๊ฐ€ ๋ฌด์—‡์„ ํ•˜๋Š”์ง€ ๋ชจ๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ๋ณ„๋กœ ๋„์›€์ด ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. :-) ํŒจํ„ด์— ๋Œ€ํ•œ ํ”ผ๋“œ๋ฐฑ์„ ์›ํ•˜์‹ ๋‹ค๋ฉด ํ•ด๋‹น ํŒจํ„ด์„ ๊ฐœ๋ณ„์ ์œผ๋กœ ๋ณด์—ฌ์ฃผ๋Š” ์ƒŒ๋“œ๋ฐ•์Šค๋ฅผ ์ค€๋น„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

@JoviDeCroock ์˜ˆ, getCurrentValue ๋Š” ํ™•์‹คํžˆ ๋ถ€์ž‘์šฉ์ด ์—†์Šต๋‹ˆ๋‹ค . ์šฐ๋ฆฌ๋Š” ๊ทธ ์ด๋ฆ„์ด ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ๊ฝค ๋ช…์‹œ์ ์ด๋ผ๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž ์ง€์ • ํ›„ํฌ์˜ ๋ฃจํŠธ ๋ฒ”์œ„ ๋‚ด์—์„œ redux ์ž‘์—…์„ ๋””์ŠคํŒจ์น˜ํ•  ๋•Œ ์ด ๊ฒฝ๊ณ ๊ฐ€ ํ‘œ์‹œ๋˜๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

function useCustomHook() {
   const dispatch = useDispatch()
   const value = useSomeOtherHook()
   dispatch(action(value))
}

useEffect ๋””์ŠคํŒจ์น˜๋ฅผ โ€‹โ€‹๋ž˜ํ•‘ํ•˜์—ฌ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.

@Glinkis ์ƒ๊ด€์—†์ด ๋‚˜์œ ํŒจํ„ด์ฒ˜๋Ÿผ ๋“ค๋ฆฝ๋‹ˆ๋‹ค. ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋ Œ๋”๋ง๋  ๋•Œ๋งˆ๋‹ค ์ „์ฒด ํŠธ๋ฆฌ์— ์•Œ๋ฆฌ๊ณ  ์‹ถ์€ ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

@gaearon ๋„ค, ์šฐ๋ฆฌ๊ฐ€ ํ•ด๊ฒฐํ•˜๋ ค๊ณ  ํ–ˆ๋˜ ๋ฌธ์ œ๋Š” ์—ฌ๊ธฐ์— ๋” ์ž˜ ์„ค๋ช…๋˜์–ด ์žˆ๊ณ  ๊ฝค ํ”ํ•œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

@Glinkis ์ƒ๊ด€์—†์ด ๋‚˜์œ ํŒจํ„ด์ฒ˜๋Ÿผ ๋“ค๋ฆฝ๋‹ˆ๋‹ค. ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋ Œ๋”๋ง๋  ๋•Œ๋งˆ๋‹ค ์ „์ฒด ํŠธ๋ฆฌ์— ์•Œ๋ฆฌ๊ณ  ์‹ถ์€ ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

๋‚ด redux ์ƒํƒœ๋ฅผ ๋‚ด ๊ฒฝ๋กœ์˜ ID์™€ ๋™๊ธฐํ™”๋œ ์ƒํƒœ๋กœ ์œ ์ง€ํ•ด์•ผ ํ•˜๋ฏ€๋กœ ๊ฒฝ๋กœ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ์ด ์—…๋ฐ์ดํŠธ๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ์ฐจ์„ ์ฑ…์ผ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์ง€๋งŒ ์„ ํƒ๊ธฐ ๋‚ด๋ถ€์˜ ๋ผ์šฐํŒ… ๋ฐ์ดํ„ฐ์— ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์„ ์ฐพ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.

@Glinkis ๊ฒฝ๋กœ ๋ฐ์ดํ„ฐ๋Š” ์–ด๋””์—์„œ

@JoviDeCroock ์ด ํŒจํ„ด์— ๋Œ€ํ•œ ์ตœ์‹  ๊ถŒ์žฅ ์‚ฌํ•ญ์€ ์‚ฌ์šฉ์ž ์ง€์ • ์˜ˆ์•ฝ ๊ฐ€๋น„์ง€ ์ˆ˜์ง‘ ํŒจ์Šค๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

@Glinkis ๊ฒฝ๋กœ ๋ฐ์ดํ„ฐ๋Š” ์–ด๋””์—์„œ

์ด๊ฒƒ์ด ๋‚ด๊ฐ€ ์ด ํ† ๋ก ์— ์†ํ•˜๋Š”์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์ง€๋งŒ ์ด๊ฒƒ์€ ๋‚ด ํ›„ํฌ์ž…๋‹ˆ๋‹ค.

export function useOpenFolder() {
  const dispatch = useDispatch()
  const match = useRouteMatch('/:workspace/(super|settings)?/:type?/:id?/(item)?/:item?')
  const { id, item } = match?.params || {}

  useEffect(() => {
    dispatch(
      openFolder({
        id: id || '',
        item: item || '',
      })
    )
  }, [dispatch, item, id])
}

๋‚˜์ค‘์— ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์„ ํƒ๊ธฐ์— ์ด ์ƒํƒœ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

export const getActiveItem = createSelector(
  [getActiveFolderItem, getItems],
  (activeItem, items) => items.all[activeItem]
)

@Glinkis ์˜ˆ, ์—ฌ๊ธฐ์—์„œ ๋งˆ๋ฌด๋ฆฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ œ ์ œ์•ˆ์€ ์ƒ์œ„ ๊ตฌ์„ฑ ์š”์†Œ์—์„œ useRouteMatch ๋ฅผ ์ฝ๊ณ  ID๋ฅผ ์†Œํ’ˆ์œผ๋กœ ์ „๋‹ฌํ•œ ๋‹ค์Œ ํ‰์†Œ์ฒ˜๋Ÿผ ์„ ํƒ๊ธฐ์—์„œ ์†Œํ’ˆ์„ ์ฝ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. (์‚ฌ์‹ค ์ด๊ฒƒ์ด ์š”์ฆ˜ Redux์—์„œ ์–ด๋–ป๊ฒŒ ์ˆ˜ํ–‰๋˜๋Š”์ง€ ๋ชจ๋ฅด์ง€๋งŒ ์–ด๋–ค ๋ฐฉ๋ฒ•์ด ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.)

์•Œ๊ฒ ์Šต๋‹ˆ๋‹ค. UNSAFE_componentWillReceiveProps ๋Š” ๋ Œ๋”๋ง์œผ๋กœ ๊ณ„์‚ฐ๋˜์ง€๋งŒ componentDidUpdate ๋Š” ๊ณ„์‚ฐ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

@LabhanshAgrawal ๋งž์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์— ๋ช‡ ๊ฐ€์ง€ ๋ฐฐ๊ฒฝ ์„ค๋ช… :

๊ฐœ๋…์ ์œผ๋กœ React๋Š” ๋‘ ๋‹จ๊ณ„๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

  • ๋ Œ๋”๋ง ๋‹จ๊ณ„๋Š” ์˜ˆ๋ฅผ ๋“ค์–ด DOM์— ์–ด๋–ค ๋ณ€๊ฒฝ์ด ํ•„์š”ํ•œ์ง€ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋‹จ๊ณ„์—์„œ React๋Š” render ํ˜ธ์ถœํ•œ ๋‹ค์Œ ๊ฒฐ๊ณผ๋ฅผ ์ด์ „ ๋ Œ๋”๋ง๊ณผ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค.
  • ์ปค๋ฐ‹ ๋‹จ๊ณ„๋Š” React๊ฐ€ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ ์šฉํ•˜๋Š” ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค. (React DOM์˜ ๊ฒฝ์šฐ React๊ฐ€ DOM ๋…ธ๋“œ๋ฅผ ์‚ฝ์ž…, ์—…๋ฐ์ดํŠธ ๋ฐ ์ œ๊ฑฐํ•  ๋•Œ์ž…๋‹ˆ๋‹ค.) React๋Š” ์ด ๋‹จ๊ณ„์—์„œ componentDidMount ๋ฐ componentDidUpdate ์™€ ๊ฐ™์€ ์ˆ˜๋ช… ์ฃผ๊ธฐ๋„ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

์ปค๋ฐ‹ ๋‹จ๊ณ„๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ๋งค์šฐ ๋น ๋ฅด์ง€๋งŒ ๋ Œ๋”๋ง์ด ๋Š๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ด์œ ๋กœ ์˜ˆ์ •๋œ ๋™์‹œ ๋ชจ๋“œ(์•„์ง ๊ธฐ๋ณธ์ ์œผ๋กœ ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์ง€ ์•Š์Œ)๋Š” ๋ Œ๋”๋ง ์ž‘์—…์„ ์กฐ๊ฐ์œผ๋กœ ๋‚˜๋ˆ„๊ณ  ๋ธŒ๋ผ์šฐ์ € ์ฐจ๋‹จ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด ์ž‘์—…์„ ์ผ์‹œ ์ค‘์ง€ํ–ˆ๋‹ค๊ฐ€ ๋‹ค์‹œ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ React๊ฐ€ ์ปค๋ฐ‹ํ•˜๊ธฐ ์ „์— ๋ Œ๋”๋ง ๋‹จ๊ณ„ ์ˆ˜๋ช… ์ฃผ๊ธฐ๋ฅผ ๋‘ ๋ฒˆ ์ด์ƒ ํ˜ธ์ถœํ•˜๊ฑฐ๋‚˜ ์ปค๋ฐ‹ํ•˜์ง€ ์•Š๊ณ  ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค(์˜ค๋ฅ˜ ๋˜๋Š” ๋” ๋†’์€ ์šฐ์„  ์ˆœ์œ„ ์ค‘๋‹จ์œผ๋กœ ์ธํ•ด).

๋ Œ๋”๋ง ๋‹จ๊ณ„ ์ˆ˜๋ช… ์ฃผ๊ธฐ์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํด๋ž˜์Šค ๊ตฌ์„ฑ ์š”์†Œ ๋ฉ”์„œ๋“œ๊ฐ€ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

  • constructor
  • componentWillMount (๋˜๋Š” UNSAFE_componentWillMount )
  • componentWillReceiveProps (๋˜๋Š” UNSAFE_componentWillReceiveProps )
  • componentWillUpdate (๋˜๋Š” UNSAFE_componentWillUpdate )
  • getDerivedStateFromProps
  • shouldComponentUpdate
  • render
  • setState ์—…๋ฐ์ดํ„ฐ ํ•จ์ˆ˜(์ฒซ ๋ฒˆ์งธ ์ธ์ˆ˜)

์œ„์˜ ๋ฉ”์„œ๋“œ๋Š” ๋‘ ๋ฒˆ ์ด์ƒ ํ˜ธ์ถœ๋  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋ถ€์ž‘์šฉ์ด ํฌํ•จ๋˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ทœ์น™์„ ๋ฌด์‹œํ•˜๋ฉด ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐ ์ž˜๋ชป๋œ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ ์ƒํƒœ๋ฅผ ๋น„๋กฏํ•œ ๋‹ค์–‘ํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ถˆํ–‰ํžˆ๋„ ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋Š” ์ข…์ข… ๋น„๊ฒฐ์ •์  ์ผ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๊ฐ์ง€ํ•˜๊ธฐ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ž, ๋” ์ž์„ธํžˆ ์‚ดํŽด๋ณด๋ฉด ์›๋ž˜ ์˜๋„ํ–ˆ๋˜ ๊ฒƒ๋ณด๋‹ค ๋” ๋งŽ์€ ๊ฒฝ์šฐ(์˜ˆ: ํด๋ž˜์Šค์˜ ๊ฒฝ์šฐ)์—์„œ ๊ฒฝ๊ณ ํ•˜๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๊ทธ ์ค‘ ์ผ๋ถ€๋ฅผ ์นจ๋ฌต์‹œํ‚ฌ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์˜๋„ํ•œ ๊ฒƒ ์ด์ƒ์ด๋ผ ํ• ์ง€๋ผ๋„ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋กœ์ ํŠธ๋ฅผ ๊ฐœ์„ ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฒƒ์ด ์ข‹๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์ด ์ž์ฒด ์„ค๋ช… ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ "๋‹ค๋ฅธ ๊ตฌ์„ฑ ์š”์†Œ์˜ ํ•จ์ˆ˜ ๋ณธ๋ฌธ ๋‚ด๋ถ€์—์„œ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค"

์ฆ‰, ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋Œ€์‹  ํ•จ์ˆ˜๋กœ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์€ ์˜ˆ:

const App = () => {  
  const fetchRecords = () => { 
    return <div>Loading..</div>;
  };  
  return fetchRecords() // and not like <FetchRecords /> unless it is functional component.
};
export default App;

@rpateld ๋‚˜๋Š” ๋‹น์‹ ์ด ๋ณด์—ฌ์ฃผ๋Š” ์˜ˆ๊ฐ€ ์ด ๊ฒฝ๊ณ ์™€ ๊ด€๋ จ์ด ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

https://github.com/facebook/react/pull/18330 ์€ ์šฐ๋ฆฌ๊ฐ€ ๋ฐœ์‚ฌ๋ฅผ ์‹œ์ž‘ํ•  ์˜๋„๊ฐ€ ์—†์—ˆ๋˜ ๊ฒฝ์šฐ๋ฅผ ํ•ด๊ฒฐํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋‚˜๋Š” ๋˜ํ•œ react@experimental + react-redux + redux ์ด ๋ฌธ์ œ์— ์ง๋ฉดํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
image

์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

import React, { Suspense } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { Redirect } from 'react-router-dom'
import PropTypes from 'prop-types'
import { userActions, cabinetActions, tokensActions } from '../../actions'
import { CircularProgress } from '@material-ui/core'
import { api } from './api'

const Cabinet = ({ resource }) => {
    resource.read()
    return <h1>cabinet</h1>
}

Cabinet.propTypes = {
    resource: PropTypes.shape({
        read: PropTypes.func
    })
}

const CabinetPage = ({
    failedToLoad,
    tokensRefreshFailed,
    logout,
    loadCabinet,
    clearTokens,
    clearCabinet
}) => {
    if (tokensRefreshFailed || failedToLoad) {
        clearTokens()
        clearCabinet()
        logout()
        return <Redirect to='/login' />
    }

    return (
        <Suspense fallback={<CircularProgress />}>
            <Cabinet resource={loadCabinet()} />
        </Suspense>
    )
}

CabinetPage.propTypes = {
    loadCabinet: PropTypes.func,
    failedToLoad: PropTypes.bool,
    tokensRefreshFailed: PropTypes.bool,
    logout: PropTypes.func,
    clearTokens: PropTypes.func,
    clearCabinet: PropTypes.func
}

const mapStateToProps = ({
    alert,
    tokens: { tokensRefreshFailed },
    cabinet: { failedToLoad }
}) => ({
    alert,
    tokensRefreshFailed,
    failedToLoad
})

const mapDispatchToProps = dispatch =>
    bindActionCreators(
        {
            cabinetLoad: cabinetActions.load,
            logout: userActions.logoutWithoutRedirect,
            loadCabinet: api.loadCabinet,
            clearCabinet: cabinetActions.clear,
            clearTokens: tokensActions.clear
        },
        dispatch
    )

export default connect(mapStateToProps, mapDispatchToProps)(CabinetPage)

loadCabinet() ๋Š” ๋™์‹œ ๋ฌธ์„œ์—์„œ ๋งํ–ˆ๋“ฏ์ด 3๋‹จ๊ณ„ ๋ Œ๋”๋ง์˜ ๋น„๋™๊ธฐ ์ž‘์—…์„ ์ „๋‹ฌํ•˜๊ณ  read() prop์ด ์žˆ๋Š” ๊ฐœ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
๊ทธ๋Ÿฌ๋‚˜ ์—ฌ๊ธฐ์—๋Š” ๋ถ€๋ชจ ์—…๋ฐ์ดํŠธ๊ฐ€ ํ‘œ์‹œ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

@h0tw4t3r ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋ Œ๋”๋งํ•˜๋Š” ๋™์•ˆ Redux ์ž‘์—…์„ ์ „๋‹ฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ง€์›๋˜์ง€ ์•Š์œผ๋ฉฐ ์ด๊ฒƒ์ด ๊ฒฝ๊ณ ์— ๋Œ€ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ(๋ฆฌ๋””๋ ‰์…˜)์„ ์ •์ƒ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด React Router ์ „๋ฌธ๊ฐ€์—๊ฒŒ ๋ฌธ์˜ํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ์ข‹์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ๋ถ€๋ถ„์€ ๋„์›€์ด ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋™์‹œ ๋ชจ๋“œ์™€ ๊ด€๋ จํ•˜์—ฌ Redux๋Š” ํ˜„์žฌ ์ผ๋ฐ˜์ ์œผ๋กœ ํ˜ธํ™˜๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ CM์„ ์‹คํ—˜ํ•˜๋Š” ๊ฒฝ์šฐ ํ”ผํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

์ด ์Šค๋ ˆ๋“œ์— ๋Œ€ํ•œ ์ž‘์€ ์—…๋ฐ์ดํŠธ

ํด๋ž˜์Šค์— ๋Œ€ํ•œ ์ด ๊ฒฝ๊ณ ์˜ ๊ณผ๋„ํ•œ ์‹คํ–‰์„ ์ˆ˜์ •ํ•˜๋Š” React ํŒจ์น˜๋ฅผ ๊ณง ์ถœ์‹œํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋ฉฐ์น  ๊ธฐ๋‹ค๋ ธ๋‹ค๊ฐ€ ์ข…๋ฃŒ๋˜๋ฉด 16.13.1์„ ์‹œ๋„ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

์›์ธ์„ ๊ณ„์† ์ฐพ๊ณ  ์‹ถ๋‹ค๋ฉด https://github.com/facebook/react/issues/18178#issuecomment -595846312์—์„œ ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•˜๊ธฐ๋ฅผ

๋™์ผํ•œ ๋…ผ๋ฆฌ๊ฐ€ ํด๋ž˜์Šค ๊ตฌ์„ฑ ์š”์†Œ์—์„œ ์‚ฌ์šฉ๋  ๋•Œ ๊ธฐ๋Šฅ(ํ›„ํฌ)์ด ์ˆ˜ํ–‰ํ•˜๋Š” ๋™์•ˆ ๊ฒฝ๊ณ ๋ฅผ ํ‘œ์‹œํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ๋งค์šฐ ์ด์ƒํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•˜์Šต๋‹ˆ๋‹ค.

๊ธฐ๋Šฅ ๊ตฌ์„ฑ ์š”์†Œ(ํ›„ํฌ):

import React, { Component } from "react"
import SortableTree from "react-sortable-tree"
import "react-sortable-tree/style.css"

const data = [
  {
    title: "Windows 10",
    subtitle: "running",
    children: [
      {
        title: "Ubuntu 12",
        subtitle: "halted",
        children: [
          {
            title: "Debian",
            subtitle: "gone"
          }
        ]
      },
      {
        title: "Centos 8",
        subtitle: "hardening"
      },
      {
        title: "Suse",
        subtitle: "license"
      }
    ]
  }
]

const nodeInfo = row => console.log(row)

export default class App extends Component {
  constructor(props) {
    super(props)

    this.state = {
      searchString: "",
      searchFocusIndex: 0,
      searchFoundCount: null,
      treeData: data
    }
  }

  render() {
    const { searchString, searchFocusIndex, searchFoundCount } = this.state

    const customSearchMethod = ({ node, searchQuery }) =>
      searchQuery &&
      ((node.title &&
        node.title.toLowerCase().indexOf(searchQuery.toLowerCase()) > -1) ||
        (node.subtitle &&
          node.subtitle.toLowerCase().indexOf(searchQuery.toLowerCase()) > -1))

    const selectPrevMatch = () =>
      this.setState({
        searchFocusIndex:
          searchFocusIndex !== null
            ? (searchFoundCount + searchFocusIndex - 1) % searchFoundCount
            : searchFoundCount - 1
      })

    const selectNextMatch = () =>
      this.setState({
        searchFocusIndex:
          searchFocusIndex !== null
            ? (searchFocusIndex + 1) % searchFoundCount
            : 0
      })

    return (
      <div>
        <h2>Find the needle!</h2>
        <form
          style={{ display: "inline-block" }}
          onSubmit={event => {
            event.preventDefault()
          }}
        >
          <input
            id="find-box"
            type="text"
            placeholder="Search..."
            style={{ fontSize: "1rem" }}
            value={searchString}
            onChange={event =>
              this.setState({ searchString: event.target.value })
            }
          />
          &nbsp;
          <button
            type="button"
            disabled={!searchFoundCount}
            onClick={selectPrevMatch}
          >
            &lt;
          </button>
          &nbsp;
          <button
            type="submit"
            disabled={!searchFoundCount}
            onClick={selectNextMatch}
          >
            &gt;
          </button>
          &nbsp;
          <span>
            &nbsp;
            {searchFoundCount > 0 ? searchFocusIndex + 1 : 0}
            &nbsp;/&nbsp;
            {searchFoundCount || 0}
          </span>
        </form>

        <div style={{ height: 300 }}>
          <SortableTree
            treeData={this.state.treeData}
            onChange={treeData => this.setState({ treeData })}
            searchMethod={customSearchMethod}
            searchQuery={searchString}
            searchFocusOffset={searchFocusIndex}
            searchFinishCallback={matches =>
              this.setState({
                searchFoundCount: matches.length,
                searchFocusIndex:
                  matches.length > 0 ? searchFocusIndex % matches.length : 0
              })
            }
            generateNodeProps={row => {
              return {
                title: row.node.title,
                subtitle: (
                  <div style={{ lineHeight: "2em" }}>{row.node.subtitle}</div>
                ),
                buttons: [
                  <button
                    type="button"
                    className="btn btn-outline-success"
                    style={{
                      verticalAlign: "middle"
                    }}
                    onClick={() => nodeInfo(row)}
                  >
                    โ„น
                  </button>
                ]
              }
            }}
          />
        </div>
      </div>
    )
  }
}

image

ํด๋ž˜์Šค ๊ตฌ์„ฑ ์š”์†Œ:

import React from "react";
import SortableTree from "react-sortable-tree";
import "react-sortable-tree/style.css";

const data = [
  {
    title: "Windows 10",
    subtitle: "running",
    children: [
      {
        title: "Ubuntu 12",
        subtitle: "halted",
        children: [
          {
            title: "Debian",
            subtitle: "gone"
          }
        ]
      },
      {
        title: "Centos 8",
        subtitle: "hardening"
      },
      {
        title: "Suse",
        subtitle: "license"
      }
    ]
  }
];

const nodeInfo = row => console.log(row);

export default class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      searchString: "",
      searchFocusIndex: 0,
      searchFoundCount: null,
      treeData: data
    };
  }

  render() {
    const { searchString, searchFocusIndex, searchFoundCount } = this.state;

    const customSearchMethod = ({ node, searchQuery }) =>
      searchQuery &&
      ((node.title &&
        node.title.toLowerCase().indexOf(searchQuery.toLowerCase()) > -1) ||
        (node.subtitle &&
          node.subtitle.toLowerCase().indexOf(searchQuery.toLowerCase()) > -1));

    const selectPrevMatch = () =>
      this.setState({
        searchFocusIndex:
          searchFocusIndex !== null
            ? (searchFoundCount + searchFocusIndex - 1) % searchFoundCount
            : searchFoundCount - 1
      });

    const selectNextMatch = () =>
      this.setState({
        searchFocusIndex:
          searchFocusIndex !== null
            ? (searchFocusIndex + 1) % searchFoundCount
            : 0
      });

    return (
      <div>
        <h2>Find the needle!</h2>
        <form
          style={{ display: "inline-block" }}
          onSubmit={event => {
            event.preventDefault();
          }}
        >
          <input
            id="find-box"
            type="text"
            placeholder="Search..."
            style={{ fontSize: "1rem" }}
            value={searchString}
            onChange={event =>
              this.setState({ searchString: event.target.value })
            }
          />
          &nbsp;
          <button
            type="button"
            disabled={!searchFoundCount}
            onClick={selectPrevMatch}
          >
            &lt;
          </button>
          &nbsp;
          <button
            type="submit"
            disabled={!searchFoundCount}
            onClick={selectNextMatch}
          >
            &gt;
          </button>
          &nbsp;
          <span>
            &nbsp;
            {searchFoundCount > 0 ? searchFocusIndex + 1 : 0}
            &nbsp;/&nbsp;
            {searchFoundCount || 0}
          </span>
        </form>

        <div style={{ height: 300 }}>
          <SortableTree
            treeData={this.state.treeData}
            onChange={treeData => this.setState({ treeData })}
            searchMethod={customSearchMethod}
            searchQuery={searchString}
            searchFocusOffset={searchFocusIndex}
            searchFinishCallback={matches =>
              this.setState({
                searchFoundCount: matches.length,
                searchFocusIndex:
                  matches.length > 0 ? searchFocusIndex % matches.length : 0
              })
            }
            generateNodeProps={row => {
              return {
                title: row.node.title,
                subtitle: (
                  <div style={{ lineHeight: "2em" }}>{row.node.subtitle}</div>
                ),
                buttons: [
                  <button
                    type="button"
                    className="btn btn-outline-success"
                    style={{
                      verticalAlign: "middle"
                    }}
                    onClick={() => nodeInfo(row)}
                  >
                    โ„น
                  </button>
                ]
              };
            }}
          />
        </div>
      </div>
    );
  }
}

@radulle ์ด๊ฒƒ์€ ๊ทธ ์ž์ฒด๋กœ ๋งค์šฐ ์œ ์šฉํ•œ ์˜ˆ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. CodeSandbox์— ๋„ฃ์œผ๋ ค๊ณ  ์‹œ๋„ํ–ˆ์ง€๋งŒ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค: https://codesandbox.io/s/clever-taussig-9xixs. ์šฐ๋ฆฌ๊ฐ€ ์‹œ๋„ํ•  ์ˆ˜ ์žˆ๋Š” ์˜ˆ๋ฅผ ์ค€๋น„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

@gaearon ์ฝ”๋“œ์ƒŒ๋“œ๋ฐ•์Šค ๋ฅผ ๋งŒ๋“ค๊ณ  ์‹ถ์—ˆ์ง€๋งŒ ์ตœ์‹  ๋ฒ„์ „์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์ „ ๋ฒ„์ „์—์„œ๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ๋กœ์„œ๋Š” ์ด๋ฅผ ์žฌํ˜„ํ•˜๋Š” ์œ ์ผํ•œ ๋ฐฉ๋ฒ•์€ Create React App์—์„œ ๋กœ์ปฌ๋กœ ํšŒ์ „ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@radulle CodeSandbox์—์„œ ์ž‘๋™ํ•˜๋Š” ๋ฒ„์ „์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

@gaearon 2.6.2๊ฐ€ ์ž‘๋™ํ•˜๊ณ  ๋‹ค์Œ ์„ค์ •์œผ๋กœ ์˜ค๋ฅ˜/๊ฒฝ๊ณ ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
image
๋”ฐ๋ผ์„œ ๋™์ผํ•œ ์„ค์ •์˜ ๊ฒฝ์šฐ:
๊ธฐ๋Šฅ ๊ตฌ์„ฑ ์š”์†Œ: ์˜ค๋ฅ˜/๊ฒฝ๊ณ 
ํด๋ž˜์Šค ๊ตฌ์„ฑ ์š”์†Œ: ์˜ค๋ฅ˜/๊ฒฝ๊ณ  ์—†์Œ
์–ด์ฉŒ๋ฉด ๋‚ด๊ฐ€ ๋†“์นœ ๊ฒƒ์ด ์žˆ๊ณ  ๊ทธ๊ฒƒ๋“ค์ด ๋™๋“ฑํ•˜์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๋„ค, ์ด๊ฒƒ์€ ์ œ๊ฐ€ https://github.com/facebook/react/issues/18178#issuecomment -600369392์—์„œ ์–ธ๊ธ‰ํ•œ ์‚ฌ๋ก€ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ๊ฒฝ๊ณ ๋ฅผ ์Œ์†Œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. ๊ฒฝ๊ณ  ์ž์ฒด๋Š” ํ•ฉ๋ฒ•์ ์ด๋ฉฐ ๋‹น์‹ ์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋งํ–ˆ๋“ฏ์ด ๊ฐœ๋…์ ์œผ๋กœ ์ˆ˜์—…์—์„œ๋„ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์ง€๊ธˆ์€ (์ด ์˜ˆ์—์„œ๋Š”, ๊ทธ๊ฒƒ์ด) ํด๋ž˜์Šค์—์„œ ์˜ค๋Š” ๋•Œ๋ฅผ ์œ„ํ•ด ๋‘ ๊ฒฝ์šฐ ๋ชจ๋‘ ๊ทธ๊ฒƒ์„ ์Œ์†Œ๊ฑฐ ๊ฒƒ ๋•Œ๋ฌธ์— ๊ทธ๋Ÿฌ๋‚˜, ์ฐจ์ด๋Š” ์ดํ•ด๊ฐ€๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํšจ๊ณผ๊ฐ€ ์•„๋‹Œ ๋ Œ๋”๋ง ๊ธฐ๋Šฅ์—์„œ ์ƒํƒœ ์—…๋ฐ์ดํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ํ•ฉ๋ฒ•์ ์ธ ์‚ฌ์šฉ ์‚ฌ๋ก€๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ์‹คํ–‰ ์ˆœ์„œ๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์‹ค์šฉ์ ์ธ ์˜ˆ๋ฅผ ๋“ค์–ด ์„ค๋ช…ํ•˜์ž๋ฉด: ์šฐ๋ฆฌ ์•ฑ์—๋Š” ์ด๋™ ๊ฒฝ๋กœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋…ํŠนํ•œ ์‹œ์Šคํ…œ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์šฐ๋ฆฌ๋Š” ๋ชจ๋“  ์ด๋™ ๊ฒฝ๋กœ๋ฅผ ๋ณด์œ ํ•˜๋Š” ๊ธ€๋กœ๋ฒŒ ์ปจํ…์ŠคํŠธ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ด ์ปจํ…์ŠคํŠธ์— ์ด๋™ ๊ฒฝ๋กœ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋Š” ํ›„ํฌ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

    const addBreadcrumb = useAddBreadcrumb();
    addBreadcrumb(<Breadcrumb>{item.name}</Breadcrumb>, [item.name]);
    
  • ์ปจํ…์ŠคํŠธ์—์„œ ์ •๋ณด๋ฅผ ์ฝ๊ณ  ๋ชจ๋“  ์ด๋™ ๊ฒฝ๋กœ๋ฅผ ๋ Œ๋”๋งํ•˜๋Š” ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ด๋™ ๊ฒฝ๋กœ๋ฅผ ๋ Œ๋”๋งํ•  ๋•Œ๋งˆ๋‹ค ๋งค๊ฐœ๋ณ€์ˆ˜ ์—†์ด ํ˜ธ์ถœํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ์ด๋™ ๊ฒฝ๋กœ ๊ตฌ์กฐ๋ฅผ ์œ ์ง€ํ•  ํ•„์š”๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋งค์šฐ ์‹ค์šฉ์ ์ž…๋‹ˆ๋‹ค. ์ด ๊ตฌ์กฐ๋Š” ์ฝ”๋“œ ์ž์ฒด์—์„œ ์„ ์–ธ๋ฉ๋‹ˆ๋‹ค. ๊ฒฝ๋กœ๋ฅผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋‹ค๋ฅธ ๋ถ€๋ถ„์œผ๋กœ ์ด๋™ํ•˜๋ ค๋Š” ๊ฒฝ์šฐ ์ด๋™ ๊ฒฝ๋กœ ์‹œ์Šคํ…œ์€ ๊ณ„์† ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ react-router ์™€ ๊ฒฐํ•ฉํ•˜์—ฌ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// Main/index.tsx
import { useAddBreadcrumb } from 'components/Breadcrumbs';
import React from 'react';
import Home from './Home';
import Movies from './Movies';

const Main = () => {
    const addBreadcrumb = useAddBreadcrumb();
    addBreadcrumb(<Breadcrumb to="/">Home</Breadcrumb>, []);

    return <Switch>
        <Route path="/movies">
            <Movies />
        </Route>
        <Route path="/" />
            <Home />
        </Route>
    </Switch>
}

// Movies/index.tsx
import { useAddBreadcrumb } from 'components/Breadcrumbs';
import React from 'react';
import Detail from './Detail';
import Master from './Master';

const Movies = ({ url }) => {
    const addBreadcrumb = useAddBreadcrumb();
    addBreadcrumb(<Breadcrumb to={url}>Movies</Breadcrumb>, [url]);

    return <Switch>
        <Route path="/:id">
            <Detail />
        </Route>
        <Route path="/" />
            <Master />
        </Route>
    </Switch>
}

// Movies/Detail/index.tsx
import Breadcrumbs, { useAddBreadcrumb } from 'components/Breadcrumbs';
import React from 'react';
import { useRouteMatch } from 'react-router-dom';

const MovieDetail = ({ url }) => {
    const addBreadcrumb = useAddBreadcrumb();
    const { params: { id } } = useRouteMatch<{ id: string; }>();
    const movie = useMovie(id);

    addBreadcrumb(
        <Breadcrumb to={url}>{movie?.name}</Breadcrumb>,
        [movie?.name, url]
    );

    return <div>
        <Breadcrumbs />
    </div>
}

์ด์ œ /movies/gone-with-the-wind ๋กœ ์ด๋™ํ•˜๋ฉด ์ด๋™ ๊ฒฝ๋กœ๊ฐ€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

Home > Movies > Gone with the wind

์ž, ์—ฌ๊ธฐ ๋‚ด ์š”์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ์ž‘๋™ํ•˜๋ ค๋ฉด ์‹คํ–‰ ์ˆœ์„œ๊ฐ€ ๋ณด์žฅ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ์‹คํ–‰ ์ˆœ์„œ๋Š” ๋ถ„๋ช…ํ•ฉ๋‹ˆ๋‹ค. ๋จผ์ € Main ๋ Œ๋”๋ง๋œ ๋‹ค์Œ Movies ๋ฅผ ํฌํ•จํ•˜๋Š” ์ž์‹์ด ๋ Œ๋”๋ง๋˜๊ณ  ๋งˆ์ง€๋ง‰์œผ๋กœ MovieDetail ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ addBreadcrumb ํ˜ธ์ถœ์ด ์˜ฌ๋ฐ”๋ฅธ ์ˆœ์„œ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

์ด์ œ ๋ณ€๊ฒฝ ๋กœ๊ทธ์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‚˜์™€ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ Œ๋”๋ง ๊ฒฐ๊ณผ๋กœ ๋‹ค๋ฅธ ๊ตฌ์„ฑ ์š”์†Œ์˜ ์ƒํƒœ๋ฅผ ์˜๋„์ ์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋ ค๋Š” ๋“œ๋ฌธ ๊ฒฝ์šฐ์— setState ํ˜ธ์ถœ์„ useEffect๋กœ ๋ž˜ํ•‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ์‹ค์ œ๋กœ ๋‹ค๋ฅธ ๊ตฌ์„ฑ ์š”์†Œ์˜ ์ƒํƒœ๋ฅผ ์˜๋„์ ์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋ ค๋Š” ๋“œ๋ฌธ ๊ฒฝ์šฐ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋ณ€๊ฒฝ ๋กœ๊ทธ๊ฐ€ ์ œ์•ˆํ•˜๊ณ  addBreadcrumb (๊ฒฐ๊ตญ setState ) ๋ฅผ useEffect ๋กœ ๊ฐ์‹ธ๋ฉด ์‹คํ–‰ ์ˆœ์„œ๊ฐ€ ๋” ์ด์ƒ ๋ณด์žฅ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์„ธ ๊ฐ€์ง€ setStates ๋ชจ๋‘ ๋ Œ๋”๋ง์ด ์™„๋ฃŒ๋œ ํ›„ ์‹คํ–‰๋˜์–ด ๊ฒฝ์Ÿ ์กฐ๊ฑด์ด ์ƒ์„ฑ๋˜๊ณ  ์‹œ์Šคํ…œ์ด ์ค‘๋‹จ๋ฉ๋‹ˆ๋‹ค.

์ด ๋…ํŠนํ•œ ์‹œ์Šคํ…œ์ด ์•ˆํ‹ฐ ํŒจํ„ด์œผ๋กœ ๊ฐ„์ฃผ๋˜๋Š”์ง€ ์—ฌ๋ถ€๋Š” ๋ชจ๋ฅด๊ฒ ์ง€๋งŒ ๋‚˜์—๊ฒŒ๋Š” ์˜๋ฏธ๊ฐ€ ์žˆ์œผ๋ฉฐ ๋” ๊ฐ„๋‹จํ•œ ๋Œ€์•ˆ์„ ์ฐพ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ๊ฒฐ๋ก ์ ์œผ๋กœ ๋‚˜๋Š” ์ด ์ƒˆ๋กœ์šด ๊ฒฝ๊ณ ๋ฅผ ํ™˜์˜ํ•˜์ง€๋งŒ, ์šฐ๋ฆฌ์—๊ฒŒ ์ตœ์ ์˜ ํ•ด๊ฒฐ์ฑ…์€ ๊ทธ๊ฒƒ์„ ์–ต์ œํ•  ๋ฐฉ๋ฒ•์„ ๊ฐ–๋Š” ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. setState ๋Œ€ํ•œ ๋‘ ๋ฒˆ์งธ ์„ ํƒ์  ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ํŠธ๋ฆญ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@MeLlamoPablo

ํ˜•์ œ ๋˜๋Š” ๋ถ€๋ชจ/์ž์‹ ๋ Œ๋”๋ง ์ˆœ์„œ์˜ ๋ Œ๋” ํ˜ธ์ถœ์— ์˜์กดํ•˜๋Š” ๊ฒƒ์€ ๋งค์šฐ ์ทจ์•ฝํ•˜๊ฒŒ ๋“ค๋ฆฝ๋‹ˆ๋‹ค. React๋Š” ์‹ค์ œ๋กœ ์ด๊ฒƒ์„ ๋ณด์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์–ด๋ฆฐ์ด๋Š” ๋ถ€๋ชจ ์—†์ด ๋‹ค์‹œ ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๊ทธ ๋ฐ˜๋Œ€๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค. ์ž์‹์ด ์ง€์—ฐ์œผ๋กœ ๋ Œ๋”๋ง๋˜๋ฉด(์˜ˆ: ์ฝ”๋“œ ๋ถ„ํ• ) ์ด ์ฝ”๋“œ๋„ ์ค‘๋‹จ๋ฉ๋‹ˆ๋‹ค. ๋˜๋Š” ์ผ๋ถ€ ์ž์‹์ด ๋™์ ์œผ๋กœ ์‚ฝ์ž…๋˜๊ฑฐ๋‚˜ ์‚ญ์ œ๋˜๋Š” ๊ฒฝ์šฐ. ๋งํ•  ๊ฒƒ๋„ ์—†์ด, ์ด๊ฒƒ์€ ๊ณ„๋‹จ์‹ ๋ Œ๋”๊ฐ€ ๋„ˆ๋ฌด ๋งŽ๊ธฐ ๋•Œ๋ฌธ์— ์„ฑ๋Šฅ ๋ฉด์—์„œ ์ƒ๋‹นํžˆ ์ข‹์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ๋‹น์‹ ์ด ํ•ด๊ฒฐํ•˜๋ ค๋Š” ๋ฌธ์ œ์— ๊ณต๊ฐํ•ฉ๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ์˜ค๋Š˜๋‚  React์—๋Š” ๊ด€์šฉ์  ์†”๋ฃจ์…˜์ด ์—†์Šต๋‹ˆ๋‹ค. ์ด์— ๋Œ€ํ•œ ๋ช‡ ๊ฐ€์ง€ ์•„์ด๋””์–ด๊ฐ€ ์žˆ์ง€๋งŒ ์ ์ ˆํ•œ ์†”๋ฃจ์…˜์€ ์ƒ๋‹นํžˆ ๋‹ฌ๋ผ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋™์•ˆ ์ด ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

@gaearon , ํ†ต์ฐฐ๋ ฅ ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ํ•œ ๊ฐ€์ง€ ์งˆ๋ฌธ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ ๋ Œ๋”๋ง์˜ ์ˆœ์„œ๊ฐ€ ๋ณด์žฅ๋ฉ๋‹ˆ๊นŒ? ๊ทธ๊ฒƒ์ด ์šฐ๋ฆฌ๊ฐ€ ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ์ „๋ถ€์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค(ํ•œ ๋ฒˆ ์ด๋™ ๊ฒฝ๋กœ๋ฅผ ์•Œ๊ณ  ๋‚˜๋ฉด ํ›„์† ๋ Œ๋”๋ง์˜ ์ˆœ์„œ๋Š” ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค).

์ฒซ ๋ฒˆ์งธ ๋ Œ๋”๋ง์˜ ์ˆœ์„œ๊ฐ€ ๋ณด์žฅ๋œ๋‹ค๋Š” ๊ฒƒ์ด ๋…ผ๋ฆฌ์ ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด React๊ฐ€ ๋ถ€๋ชจ ๊ตฌ์„ฑ ์š”์†Œ์— ์ž์‹์ด ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์–ด๋–ป๊ฒŒ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

๊ณ„๋‹จ์‹ ๋ Œ๋”์˜ ์„ฑ๋Šฅ์— ๋Œ€ํ•ด์„œ๋Š” ์ „์ ์œผ๋กœ ๋งž์Šต๋‹ˆ๋‹ค. ์‹œ์Šคํ…œ์„ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ์ฐพ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@MeLlamoPablo

ํ•œ ๊ฐ€์ง€ ์งˆ๋ฌธ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ ๋ Œ๋”๋ง์˜ ์ˆœ์„œ๊ฐ€ ๋ณด์žฅ๋ฉ๋‹ˆ๊นŒ? ๊ทธ๊ฒƒ์ด ์šฐ๋ฆฌ๊ฐ€ ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ์ „๋ถ€์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค(ํ•œ ๋ฒˆ ์ด๋™ ๊ฒฝ๋กœ๋ฅผ ์•Œ๊ณ  ๋‚˜๋ฉด ํ›„์† ๋ Œ๋”๋ง์˜ ์ˆœ์„œ๋Š” ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค).

๊ฐ•๋ ฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๊ฒƒ์ด ๋Œ€๋ถ€๋ถ„ ํ˜„์žฌ ๋ฒ„์ „์˜ React์—์„œ ์ž‘๋™ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€๋งŒ ๋ฏธ๋ž˜์—๋Š” ๋ฐ”๋€” ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ค๋Š˜๋‚ ์—๋„ lazy ๋ฐ Suspense ์™€ ๊ฐ™์€ ๊ธฐ๋Šฅ๊ณผ ๊ฒฐํ•ฉํ•˜์—ฌ ๋ณด์žฅ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด React๊ฐ€ ๋ถ€๋ชจ ๊ตฌ์„ฑ ์š”์†Œ์— ์ž์‹์ด ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์–ด๋–ป๊ฒŒ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

ํ˜•์ œ์ž๋งค ์ˆœ์„œ๋Š” ๋ณด์žฅ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ถ€๋ชจ/์ž์‹ ์ˆœ์„œ์˜ ๊ฒฝ์šฐ ๋ถ€๋ชจ ๊ฐ€ ๋จผ์ € ๋ Œ๋”๋ง ํ•˜๊ธฐ ์ „์— ๋˜๋Š” ์ฒซ ๋ฒˆ์งธ ์ž์‹์„ ๋ Œ๋”๋งํ•œ ํ›„์—๋„ ๋‘ ๋ฒˆ์งธ ์ž์‹๋ณด๋‹ค ๋จผ์ € ๋ถ€๋ชจ๋ฅผ ๋‹ค์‹œ ๋ Œ๋”๋งํ•ด์•ผ ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ๋งํ•˜์ง€๋งŒ, ์ฝ”๋“œ ๋ถ„ํ• ๊ณผ ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๋ฉด ๋” ๋นจ๋ฆฌ ๋ณด์žฅ์„ ์žƒ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ๊นจ์ง€๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.

@gaearon๋‹˜ , ๋‹ค์‹œ ํ•œ๋ฒˆ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋งค์šฐ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

๋ Œ๋”๋ง ๋ณธ๋ฌธ ๋‚ด์—์„œ useState mutators ํ˜ธ์ถœ์— ๋Œ€ํ•ด ๊ฒฝ๊ณ ํ•˜๋Š” ESLint ๊ทœ์น™์ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๊นŒ?

(์ด ์•„๊ปด์„œ ์‚ฌ์šฉํ•ด์•ผํ•˜์ง€๋งŒ) ๋ Œ๋”๋งํ•˜๋Š” ๋™์•ˆ ์ž์‹ ์˜ ๊ตฌ์„ฑ ์š”์†Œ์™€ setState๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์ง€์›๋˜๋Š” ํŒจํ„ด์ด๋‹ค. ๋‚˜์œ ๋‹ค๋ฅธ ๊ตฌ์„ฑ ์š”์†Œ์˜ setState์ž…๋‹ˆ๋‹ค. ์ •์ ์œผ๋กœ ๊ฐ์ง€ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์ด๋ก ์ ์œผ๋กœ ts-eslint๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ฌ๋ฐ”๋ฅธ ์—…์ŠคํŠธ๋ฆผ React ์œ ํ˜•์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•  ๋•Œ ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์•„๋งˆ๋„ ๊ฐ€์น˜๊ฐ€ ์žˆ๋Š” ๊ฒƒ๋ณด๋‹ค ๋” ๋งŽ์€ ๋…ธ๋ ฅ์ด ํ•„์š”ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ผ์ข…์˜ ํšจ๊ณผ ์ถ”์  ์—†์ด๋Š” ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ค‘๊ฐ„์— ๊ธฐ๋Šฅ์ด ํ•˜๋‚˜ ์žˆ์œผ๋ฉด ์ •๋ณด๋ฅผ ์žƒ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ๋˜ํ•œ react@experimental + react-redux + redux ์ด ๋ฌธ์ œ์— ์ง๋ฉดํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
image

์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

import React, { Suspense } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { Redirect } from 'react-router-dom'
import PropTypes from 'prop-types'
import { userActions, cabinetActions, tokensActions } from '../../actions'
import { CircularProgress } from '@material-ui/core'
import { api } from './api'

const Cabinet = ({ resource }) => {
  resource.read()
  return <h1>cabinet</h1>
}

Cabinet.propTypes = {
  resource: PropTypes.shape({
      read: PropTypes.func
  })
}

const CabinetPage = ({
  failedToLoad,
  tokensRefreshFailed,
  logout,
  loadCabinet,
  clearTokens,
  clearCabinet
}) => {
  if (tokensRefreshFailed || failedToLoad) {
      clearTokens()
      clearCabinet()
      logout()
      return <Redirect to='/login' />
  }

  return (
      <Suspense fallback={<CircularProgress />}>
          <Cabinet resource={loadCabinet()} />
      </Suspense>
  )
}

CabinetPage.propTypes = {
  loadCabinet: PropTypes.func,
  failedToLoad: PropTypes.bool,
  tokensRefreshFailed: PropTypes.bool,
  logout: PropTypes.func,
  clearTokens: PropTypes.func,
  clearCabinet: PropTypes.func
}

const mapStateToProps = ({
  alert,
  tokens: { tokensRefreshFailed },
  cabinet: { failedToLoad }
}) => ({
  alert,
  tokensRefreshFailed,
  failedToLoad
})

const mapDispatchToProps = dispatch =>
  bindActionCreators(
      {
          cabinetLoad: cabinetActions.load,
          logout: userActions.logoutWithoutRedirect,
          loadCabinet: api.loadCabinet,
          clearCabinet: cabinetActions.clear,
          clearTokens: tokensActions.clear
      },
      dispatch
  )

export default connect(mapStateToProps, mapDispatchToProps)(CabinetPage)

loadCabinet() ๋Š” ๋™์‹œ ๋ฌธ์„œ์—์„œ ๋งํ–ˆ๋“ฏ์ด 3๋‹จ๊ณ„ ๋ Œ๋”๋ง์˜ ๋น„๋™๊ธฐ ์ž‘์—…์„ ์ „๋‹ฌํ•˜๊ณ  read() prop์ด ์žˆ๋Š” ๊ฐœ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
๊ทธ๋Ÿฌ๋‚˜ ์—ฌ๊ธฐ์—๋Š” ๋ถ€๋ชจ ์—…๋ฐ์ดํŠธ๊ฐ€ ํ‘œ์‹œ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ด ๋ฌธ์ œ๊ฐ€ ์žˆ๋Š” ๋‹ค๋ฅธ ์‚ฌ๋žŒ์˜ ๊ฒฝ์šฐ redux ์ž‘์—… ๋””์ŠคํŒจ์น˜๋ฅผ โ€‹โ€‹๋ฐ˜ํ™˜๋œ ๊ตฌ์„ฑ ์š”์†Œ๋กœ ์ด๋™ํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ ๋ชจ์Šต์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

const CabinetPage = ({
    alert,
    failedToLoad,
    tokensRefreshFailed,
    logout,
    loadCabinet,
    clearTokens,
    clearCabinet,
    clearAlert
}) => (
    <Suspense fallback={<MUIBackdropProgress />}>
        {alert.message && (failedToLoad || tokensRefreshFailed) ? (
            <MUIAlertDialog
                title={alert.message}
                text={errorText}
                onClose={() => {
                    clearAlert()
                    clearCabinet()
                    clearTokens()
                    logout()
                }}
            />
        ) : (
            <Cabinet resource={loadCabinet()} />
        )}
    </Suspense>
)

์ด ๊ฒฝ๊ณ ๊ฐ€ ๋งˆ์Œ์— ๋“œ๋Š” ์ด์œ ๋Š” ์˜ฌ๋ฐ”๋ฅธ ๋””์ž์ธ ํŒจํ„ด์„ ์„ ํƒํ•˜๋„๋ก ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ด์ œ ๋ชจ๋“  ๊ฒƒ์ด ์™„๋ฒฝํ•˜๊ฒŒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค!

16.13.1์—์„œ ๊ฒฝ๊ณ ๊ฐ€ ๊ณผ๋„ํ•˜๊ฒŒ ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋จธ์ง€ ์‚ฌ๋ก€๋Š” ํ•ฉ๋ฒ•์ ์ด๋ฉฐ ์ˆ˜์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

@gaearon์ด ๋ฐฉ๊ธˆ ์—…๊ทธ๋ ˆ์ด๋“œ๋˜๊ณ  ์˜ค๋ฅ˜๊ฐ€ ์‚ฌ๋ผ์กŒ์Šต๋‹ˆ๋‹ค! ๋‹น์‹ ์˜ ์ž‘์—…์— ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค!

@gaearon ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๋‹น์‹ ์€ ๋ฐฉ๊ธˆ ๋‚ด ํ•˜๋ฃจ๋ฅผ ๊ตฌํ–ˆ์Šต๋‹ˆ๋‹ค :-)

์—…๊ทธ๋ ˆ์ด๋“œํ•ด๋„ ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜์ง€ ์•Š์•˜์ง€๋งŒ ์ฝ˜์†”์—์„œ ๋ฌธ์ œ๋ฅผ ์ฐพ๋Š” ๋ฐ ๋„์›€์ด ๋˜๋Š” ์ถ”๊ฐ€ ์ •๋ณด๋ฅผ ์ œ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค. @gaearon ๊ฐ์‚ฌ

๋‹ค๋ฅธ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์ง€๋‚œ๋ฒˆ๊ณผ ๋™์ผํ•œ ์ƒํƒœ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ํ•˜๋Š” ์ž‘์—…์„ ์ „๋‹ฌํ•˜๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”? ๋‚˜์œ ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผ๋ฉ๋‹ˆ๊นŒ? ๊ทธ๋Ÿฐ ๊ฒฝ์šฐ์—๋Š” ๋‹จ๋ฝ๋  ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ์™„์ „ํžˆ์ด ๊ฒฝ๊ณ  ๋’ค์— ๋…ผ๋ฆฌ๋ฅผ ์ดํ•ดํ•˜๋ฉด์„œ ๋‚œ ๊ทธ๋ƒฅ์ด ์žˆ๋‹ค๊ณ  ๋งํ•  ์ˆ˜ ... ๊ฑฐ์˜ ํŒ€์ด ์ฝ”๋“œ์— ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์ด๋Ÿฌํ•œ ์ค‘์š”ํ•œ ์ง„๋ฆฌ๋ฅผ ๊ฐ€๋ฅด์ณ ์ค€ ๊ฒƒ์ฒ˜๋Ÿผ ๋Š๋‚Œ ๋•Œ๋ฌธ์—์ด ํŒ€์ด ์ง€์—ญ ์‚ฌํšŒ๋ฅผ ๋งํ•˜๊ณ ์žˆ๋‹ค ๋ฐ˜์ž‘์šฉ ๋ฌด์—‡์„ ๋ฐฐ์‹  ๊ฐ™์€ ๋Š๋‚Œ ๋ฐ˜์‘:

1) ๊ณ„์ธต ๊ตฌ์กฐ์—์„œ ํ•„์š”ํ•œ ๋งŒํผ ๋†’์€ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๊ณ (๋†’์ง€ ์•Š์Œ) ๋ฐ์ดํ„ฐ ์™€ ์„ค์ •์ž ๋ฅผ ํ•˜์œ„ ๊ตฌ์„ฑ ์š”์†Œ๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

2) ๊ธฐ๋Šฅ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๊ต‰์žฅํ•ฉ๋‹ˆ๋‹ค! ํด๋ž˜์Šค ๋…ธ์ด์ฆˆ๋Š” ์žŠ์–ด๋ฒ„๋ฆฌ๊ณ  ์ „์ฒด ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ํ•˜๋‚˜์˜ ๊ธฐ๋Šฅ์œผ๋กœ ์ˆ˜ํ–‰ํ•˜์‹ญ์‹œ์˜ค!

์ด์ œ ์‚ฌ๋žŒ๋“ค์ด ์ด ๋‘ ๊ฐ€์ง€ ๊ทœ์น™์„ ๋”ฐ๋ฅด๊ณ  ์ƒํƒœ ์„ค์ •์ž๋ฅผ ํ•จ์ˆ˜ ๊ตฌ์„ฑ ์š”์†Œ๋กœ ์ „๋‹ฌํ•˜๊ณ  ํ•ด๋‹น ๊ตฌ์„ฑ ์š”์†Œ์—์„œ ํ˜ธ์ถœํ•˜๋ฉด ... ๋‹น์‹ ์€ ๊น”๊ฐœ๋ฅผ ๊บผ๋‚ด๊ณ  "ํ•˜ํ•˜, ํ•˜์ง€๋งŒ ์šฐ๋ฆฌ๊ฐ€ ๋งํ•œ ๊ฒƒ์„ ์‹ค์ œ๋กœ ํ•  ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค. ํ•  ๊ฒƒ".

์ด ์ค‘ ์–ด๋Š ๊ฒƒ๋„ ์—ฌ๊ธฐ์„œ ๊ธฐ์ˆ ์ ์ธ ์š”๊ตฌ ์‚ฌํ•ญ์— ๋Œ€ํ•ด ์•„๋ฌด ๊ฒƒ๋„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์œผ๋ฉฐ ์ด ๋ณ€๊ฒฝ์— ๋Œ€ํ•ด ์•„๋ฌด ๋ง๋„ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์ž˜๋ชป๋˜์—ˆ๊ฑฐ๋‚˜ ๋‚˜์˜๋‹ค๊ณ  ๋งํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค ... ๋‚˜๋Š” ๋‹จ์ง€ ๋ฉ”์‹œ์ง• ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋ฐฉ๊ธˆ ์–ธ๊ธ‰ํ–ˆ์Šต๋‹ˆ๋‹ค) ์ด ์ƒˆ๋กœ์šด ์„ธ๊ณ„์—์„œ ์ฝ”๋”ฉํ•˜๊ธฐ ์œ„ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ์— ๋Œ€ํ•œ ๊ทœ์น™์„ ๋ณ€๊ฒฝํ•˜๋ ค๋Š” ๊ฒฝ์šฐ ๋จผ์ € ๋ชจ๋ฒ” ์‚ฌ๋ก€๋ฅผ ์„ค๋ช…ํ•˜์—ฌ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋  ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ์ œ๊ฐ€ ์ •๋ง๋กœ ๋ฌป๊ณ  ์‹ถ์€ ๊ฒƒ์€ ... ํŒ€์˜ ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ "์ด์ „์— ์šฐ๋ฆฌ๊ฐ€ ๋‘ ๊ฐ€์ง€ ๊ทœ์น™์„ ์ค€ ๊ฒƒ์„ ์••๋‹ˆ๋‹ค. ์ƒˆ ๊ทœ์น™"์„ ํด๋ฆญํ•œ ๋‹ค์Œ ์ด ์ƒˆ๋กœ์šด ๊ฒฝ๊ณ ๋ฅผ ์ฐธ์กฐํ•˜๋Š” ๋ฌธ์„œ/๋ฆด๋ฆฌ์Šค ๋…ธํŠธ์˜ ๋ชจ๋“  ์œ„์น˜์— ํ•ด๋‹น ๋ฌธ์„œ์— ๋Œ€ํ•œ ๋งํฌ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค(๋”ฐ๋ผ์„œ "WTF๊ฐ€ ์ด๊ฒƒ์ด?"๋ผ๊ณ  ์ธํ„ฐ๋„ท ๊ฒ€์ƒ‰์„ ํ•˜๋Š” ๋ชจ๋“  ์‚ฌ๋žŒ์€ "์ƒˆ๋กœ์šด ์„ธ๊ณ„").

@machineghost : ๋ฉ”์‹œ์ง€๊ฐ€ ๊ฒฝ๊ณ ํ•˜๋Š” ๋‚ด์šฉ์„ ์˜คํ•ดํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋ถ€๋ชจ์˜ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ์ฝœ๋ฐฑ์„ ์ž์‹์—๊ฒŒ ์ „๋‹ฌํ•˜๋Š” ๋ฐ ์•„๋ฌด๋Ÿฐ ๋ฌธ์ œ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ํ•ญ์ƒ ๊ดœ์ฐฎ์•˜์Šต๋‹ˆ๋‹ค.

๋ฌธ์ œ๋Š” _์ฒซ ๋ฒˆ์งธ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋ Œ๋”๋ง๋˜๋Š” ๋™์•ˆ_ ํ•œ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋‹ค๋ฅธ ๊ตฌ์„ฑ ์š”์†Œ์˜ ์—…๋ฐ์ดํŠธ๋ฅผ ๋Œ€๊ธฐ์—ด์— ๋„ฃ์„ ๋•Œ์ž…๋‹ˆ๋‹ค.

์ฆ‰, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค.

function SomeChildComponent(props) {
    props.updateSomething();
    return <div />
}

๊ทธ๋Ÿฌ๋‚˜ ์ด๊ฒƒ์€ ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค.

function SomeChildComponent(props) {
    // or make a callback click handler and call it in there
    return <button onClick={props.updateSomething}>Click Me</button>
}

๊ทธ๋ฆฌ๊ณ  Dan์ด ์—ฌ๋Ÿฌ ๋ฒˆ ์ง€์ ํ–ˆ๋“ฏ์ด ๋ Œ๋”๋งํ•˜๋Š” ๋™์•ˆ _same_ ๊ตฌ์„ฑ ์š”์†Œ์˜ ์—…๋ฐ์ดํŠธ๋ฅผ ๋Œ€๊ธฐ์—ด์— ๋„ฃ๋Š” ๊ฒƒ๋„ ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค.

function SomeChildComponent(props) {
  const [number, setNumber] = useState(0);

  if(props.someValue > 10 && number < 5) {
    // queue an update while rendering, equivalent to getDerivedStateFromProps
    setNumber(42);
  }

  return <div>{number}</div>
}

๋งž์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์ฝ”๋”ฉํ•  ๋•Œ ์ƒ์œ„ ์š”์†Œ์˜ ํƒ€์ด๋ฐ์„ ์ƒ๊ฐํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด React ๊ตฌ์„ฑ ์š”์†Œ์˜ ์•„๋ฆ„๋‹ค์›€์˜ ์ผ๋ถ€์ธ ์บก์Šํ™”์ž…๋‹ˆ๋‹ค.

๋‹ค์‹œ ๋งํ•˜์ง€๋งŒ, ์ƒˆ๋กœ์šด ๊ฒฝ๊ณ ๊ฐ€ ์ „ํ˜€ ๋‚˜์˜๋‹ค๊ณ  ๋งํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค. ์ข‹์€ React ๊ฐœ๋ฐœ์ž๊ฐ€ ๋”ฐ๋ฅผ ์ˆ˜ ์žˆ๋Š” ๋‘ ๊ฐ€์ง€ ๊ทœ์น™์ด ์žˆ๊ธฐ ์ „์— ๋งํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด์ œ X ์กฐ๊ฑด์—์„œ ์ด๋Ÿฌํ•œ ๊ทœ์น™์€ ์ฐฝ ๋ฐ–์œผ๋กœ ๋‚˜๊ฐ€์ง€๋งŒ X ์•„๋ž˜์—์„œ๋งŒ(X = "๋ถ€๋ชจ ๊ตฌ์„ฑ ์š”์†Œ๋„ ์—…๋ฐ์ดํŠธ๋˜๋Š” ๋™์•ˆ"์ฒ˜๋Ÿผ ๋“ค๋ฆผ).

๊ทธ๋ƒฅ "์ด๊ฒŒ ๋ฌธ์ œ์•ผ!"๋ผ๊ณ  ๋งํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๊ทธ๊ฒƒ์„ ์„ค๋ช…ํ•˜๊ณ  ๋ฌธ์ œ๋ฅผ ํ”ผํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋” ์ง‘์ค‘ํ•ด์•ผํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

@machineghost : ๋‹น์‹ ์€ _์ •๋ง_ ๋‚ด๊ฐ€ ์—ฌ๊ธฐ์„œ ๋งํ•˜๋Š” ๊ฒƒ์„ ์ดํ•ดํ•˜์ง€ ๋ชปํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๋ถ€๋ชจ/์ž์‹ ํƒ€์ด๋ฐ์ด ๋ฌธ์ œ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.

ํ•จ์ˆ˜ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋ Œ๋”๋งํ•˜๋Š” ๋™์•ˆ _๋‹ค๋ฅธ ๊ตฌ์„ฑ ์š”์†Œ์—์„œ ์ƒํƒœ ์—…๋ฐ์ดํŠธ๋ฅผ ๋Œ€๊ธฐ์—ด์— ๋„ฃ๋Š” ๊ฒƒ์ด ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค.

์ •์˜์— ๋”ฐ๋ฅด๋ฉด ๋ถ€๋ชจ/์ž์‹(๋˜๋Š” ์†์ž)์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์ƒํƒœ ์„ค์ •์ž๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

์ด๊ฒƒ์ด ๋‹ค๋ฅธ ๊ตฌ์„ฑ ์š”์†Œ ๊ด€๊ณ„์—์„œ๋„ ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์—†๋‹ค๊ณ  ๋งํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ React ๋ชจ๋ฒ” ์‚ฌ๋ก€๋ฅผ ๋”ฐ๋ฅด๊ณ  ์ƒํƒœ ์„ค์ •์ž๋ฅผ ์ „๋‹ฌํ•œ ๋‹ค์Œ ์ด ๊ฒฝ๊ณ ๋ฅผ ๋ฐ›๋Š” ์‚ฌ๋žŒ๋“ค์— ๋Œ€ํ•ด ๊ตฌ์ฒด์ ์œผ๋กœ ์ด์•ผ๊ธฐํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ทธ๊ฒƒ์ด ๋‚ด๊ฐ€ ๋งํ•˜๋Š” ์ „๋ถ€์ด๋ฉฐ, ๋‚ด๊ฐ€ ๋งํ•˜๋Š” ์ „๋ถ€๋Š” "์—ฌ๊ธฐ์— '์ƒˆ๋กœ์šด ์˜ค๋ฅ˜๊ฐ€ ์žˆ์œผ๋ฉฐ ์ด๊ฒƒ์ด ์˜๋ฏธํ•˜๋Š” ๋ฐ”'๊ฐ€ ์•„๋‹ˆ๋ผ ์ฝ”๋“œ๋ฅผ ์ž˜ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋” ์ค‘์ ์„ ๋‘์–ด ๋” ์ž˜ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."์ž…๋‹ˆ๋‹ค.

ํƒ€์ด๋ฐ. ์•„๋‹ˆ๋‹ค. ๋ฌธ์ œ.

ํ•จ์ˆ˜ ๊ตฌ์„ฑ ์š”์†Œ๋Š” ๋ Œ๋”๋งํ•˜๋Š” ๋™์•ˆ ์ž์ฒด์ ์œผ๋กœ๋งŒ ์ƒํƒœ ์—…๋ฐ์ดํŠธ๋ฅผ ๋Œ€๊ธฐ์—ด์— ๋„ฃ์„ getDerivedStateFromProps ์™€ ๋™์ผํ•œ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

ํ•จ์ˆ˜ ๊ตฌ์„ฑ ์š”์†Œ์˜ ์‹ค์ œ ๋ Œ๋”๋ง ๋ณธ๋ฌธ ๋‚ด์—์„œ _any_ ๋‹ค๋ฅธ ๊ตฌ์„ฑ ์š”์†Œ์— ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ๋ฅผ ๋Œ€๊ธฐ์—ด์— ๋„ฃ๋Š” ๊ฒƒ์€ ๋ถˆ๋ฒ•์ž…๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ์ด ๊ฒฝ๊ณ ๊ฐ€ ๋งํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์–ด๋–ป๊ฒŒ ๋” ๋ช…ํ™•ํ•˜๊ฒŒ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ์„์ง€ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค.

ํƒ€์ด๋ฐ์ด ๋ฌธ์ œ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ๋‹น์‹ ์˜ ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์ œ ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค. ๋‚ด ๋ฌธ์ œ๋Š” ๋ฌธ์„œ ๋˜๋Š” ๋ฌธ์„œ ๋ถ€์กฑ์ž…๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ๋‹น์‹ ์€ ๊ทธ๋“ค์ด ๋งํ•˜๋Š” ๊ฒƒ์„ ๋“ฃ๋Š” ๋Œ€์‹  ์ธํ„ฐ๋„ท ๋‚ฏ์„  ์‚ฌ๋žŒ๊ณผ์˜ ๋ฌธ์ œ ์Šค๋ ˆ๋“œ์—์„œ ์ „์Ÿ์„ ์‹œ์ž‘ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

์š”์ ์€ ๋ณ€๊ฒฝ๋œ ๊ทœ์น™์ด ์—†๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ํ•ญ์ƒ ์ž˜๋ชป๋œ ํŒจํ„ด์ด์—ˆ์Šต๋‹ˆ๋‹ค. ์ฝ”๋“œ์— ๋ฒ„๊ทธ๊ฐ€ ์žˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ๋„๋ก ๊ฐ•์กฐ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๋ง ๊ทธ๋Œ€๋กœ ๋‹น์‹ ์ด ๋ฐฉ๊ธˆ ๋งํ•œ ์–ด๋–ค ๊ฒƒ๋„ ๋‚ด๊ฐ€ ์“ด ๊ฒƒ๊ณผ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์‚ฌ์‹ค, ๊ทธ ๊ฐ™์€ ๊ทœ์น™์„ ๋” ์ž˜ ์„ค๋ช…, ๋‹น์‹ ์€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์€ ๋ง ์‚ฌ๋žŒ์ด์—ˆ๋‹ค (๋ฌผ๋ก  ๋‚ด๊ฐ€ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋˜‘๊ฐ™์€ ์ผ์„ ๋งํ•œ ... ๋ชจ๋‘ ๋‚ด๊ฐ€ ๋ชจ๋“  ์‹œ๊ฐ„์„ ์š”๊ตฌ ๊ฑฐ์˜ ๊ฐ™๋‹ค ๊ทธ๋“ค์€ ๋ณ€๊ฒฝ ํ•˜์ง€ ์•Š์•˜ ์œผ๋ฉฐ "์ƒˆ๋กœ์šด ์„ธ์ƒ์„ ๋งŒ๋“ค์—ˆ๋‹ค"๋Š” ๊ฒฝ๊ณ ์˜€์Šต๋‹ˆ๋‹ค).

์ถ”์‹ : ๋‹น์‹ ๋„ ์—ฌ๊ธฐ์„œ ์•„์ด๋Ÿฌ๋‹ˆ๋ฅผ ๊นจ๋‹ซ์ง€ ๋ชปํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ์ดํ•ดํ•˜์ง€ ๋ชปํ•˜๋Š” ๊ฒƒ์ด ์žˆ์œผ๋ฉด ๋ฌธ์„œ๋ฅผ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ์‚ฌ๋ฌผ์„ ์–ผ๋งˆ๋‚˜ ์ž˜ ์ดํ•ดํ•˜์ง€ ๋ชปํ•œ๋‹ค๊ณ  ๋‚˜์—๊ฒŒ ์†Œ๋ฆฌ์น˜๋Š” ๊ฒƒ์€ ๋‚˜์˜ ์ง€์œ„๋ฅผ ๊ฐ•ํ™”์‹œํ‚ฌ ๋ฟ์ž…๋‹ˆ๋‹ค. ๋ฌธ์„œ๋ฅผ ๋งˆ์ˆ ์ฒ˜๋Ÿผ ํ–ฅ์ƒ์‹œํ‚ค์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.

์•ˆ๋…•ํ•˜์„ธ์š” ์—ฌ๋Ÿฌ๋ถ„, ์กฐ๊ธˆ ์ง„์ •ํ•ฉ์‹œ๋‹ค. ๐Ÿ™‚

@markerikson ์ฐธ์—ฌํ•ด ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด ํ† ๋ก ์ด ๋„ˆ๋ฌด ๊ณผ์—ด๋˜๊ณ  ์žˆ๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

@machineghost ์šฐ๋ ค๋ฅผ ํ‘œ๋ช…

์ด ๊ฒฝ๊ณ ์—๋Š” ์‚ฌ์ „ ์ปจํ…์ŠคํŠธ๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋Š” ๋ฐ ๋™์˜ํ•ฉ๋‹ˆ๋‹ค. ๋ณธ์งˆ์ ์œผ๋กœ, ๋‹น์‹ ์€ ์ˆ˜์—… ์‹œ๋Œ€๋ถ€ํ„ฐ ๋‘ ๊ฐ€์ง€๋ฅผ ์•Œ์•„์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

  • ๋ Œ๋”๋งํ•˜๋Š” ๋™์•ˆ setState๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ์ˆ˜์—…์€ ํ•ญ์ƒ ์ด๊ฒƒ์— ๋Œ€ํ•ด ๊ฒฝ๊ณ ํ–ˆ์Šต๋‹ˆ๋‹ค.

  • ํ•ด๋‹น ํ•จ์ˆ˜ ๊ตฌ์„ฑ ์š”์†Œ ๋ณธ๋ฌธ์€ ๋ณธ์งˆ์ ์œผ๋กœ ํด๋ž˜์Šค ๊ตฌ์„ฑ ์š”์†Œ ๋ Œ๋”๋ง ๋ฉ”์„œ๋“œ์™€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.

ํ•จ์ˆ˜ ๊ตฌ์„ฑ ์š”์†Œ ๋ณธ๋ฌธ ์ค‘์— ๋‹ค๋ฅธ ๊ตฌ์„ฑ ์š”์†Œ์— ๋Œ€ํ•œ setState๊ฐ€ ์ด์ „์— ๊ฒฝ๊ณ ํ•˜์ง€ ์•Š์€ ๊ฒƒ์€ ์‹ค์ œ๋กœ ์šฐ๋ฆฌ์˜ ๋ˆ„๋ฝ์ž…๋‹ˆ๋‹ค. ๋‹น์‹ ์€ ์œ„์˜ ๋‘ ์ ์—์„œ ๋‚˜์œ ํŒจํ„ด์ž…๋‹ˆ๋‹ค ์ถ”๋ก  ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ํ•˜๋‚˜๋Š” ๊ทธ๊ฒƒ์„ ์‹คํ˜„ํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค ๋งํ•  ๊ณต์ •์ด๋‹ค. ๋ถˆํŽธ์„ ๋“œ๋ ค ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค.

๋ฌธ์„œ์—์„œ ์ด๋ฅผ ์–ธ๊ธ‰ํ•ด์•ผ ํ•˜๋Š” ํŠน์ • ์œ„์น˜๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐ๋˜๋ฉด ๋ฌธ์„œ ์ €์žฅ์†Œ์— ๋ฌธ์ œ๋ฅผ ์ œ๊ธฐํ•˜์„ธ์š”. Hooks๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ฌธ์„œ๋ฅผ ๋‹ค์‹œ ์ž‘์„ฑํ•  ๊ณ„ํš์ด๋ฏ€๋กœ ์—ผ๋‘์— ๋‘˜ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ์‚ฌ ํ•ด์š”!

๋‚˜๋Š” ๊ฒฐ์ฝ” ๋ˆ„๊ตฐ๊ฐ€๋ฅผ ๊ธฐ๋ถ„ ๋‚˜์˜๊ฒŒ ๋งŒ๋“ค ์ƒ๊ฐ์ด ์—†์œผ๋ฉฐ, ๋‚˜๋Š” ๋‹น์‹ ์˜ ์‚ฌ๊ณผ๋ฅผ ๋ฐ›์•„๋“ค์ด๊ธฐ๋ฅผ ๊ฑฐ๋ถ€ํ•ฉ๋‹ˆ๋‹ค ;) ํ›„ํฌ๋Š” ์ฒœ์žฌ์ด๋ฉฐ, ์—ฌ๋Ÿฌ๋ถ„์€ ํ›„ํฌ๋ฅผ ๋ฐœ๋ช…ํ•œ ์ฒœ์žฌ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋ชจ๋“  ๊ฒฐ๊ณผ๋ฅผ ์™„๋ฒฝํ•˜๊ฒŒ ์ƒ์ƒํ•˜์ง€ ๋ชปํ•œ๋‹ค๊ณ  ๋‹ค๋ฅธ ์—”์ง€๋‹ˆ์–ด๋ฅผ ํƒ“ํ•˜๋Š” ์—”์ง€๋‹ˆ์–ด๋Š” ... ๋ฐ”๋ณด์ž…๋‹ˆ๋‹ค.

๋‚ด๊ฐ€ ์˜์‚ฌ์†Œํ†ต์„ ํ•˜๋ ค๊ณ  ํ–ˆ๋˜ ๊ฒƒ์€ ํ˜„์žฌ ์ด ๊ฒฝ๊ณ ๋ฅผ ๋ฐ›์•˜์„ ๋•Œ ๋ชจ๋“  ์‚ฌ๋žŒ๋“ค์ด ํ•˜๋Š” ๋Œ€๋กœ ํ–ˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ตฌ๊ธ€๋ง์„ ํ•ด๋ดค์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ "์ด ์ƒˆ๋กœ์šด ๊ฒฝ๊ณ ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค"๋ผ๋Š” ํŽ˜์ด์ง€๋ฅผ ์ฐพ์•˜์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ๋‹จ์ง€ ๊ทธ ๋ฐœํ‘œ์— ๋งํฌ๊ฐ€ ์žˆ๊ฑฐ๋‚˜ ๊ตฌ๊ธ€๋ง์„ ํ†ตํ•ด ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ์ฐพ์„ ์ˆ˜ ์žˆ๋Š” ์œ ์‚ฌํ•œ ์žฅ์†Œ์— "์—ฌ๊ธฐ์— ์žˆ๋Š” ์šฐ๋ฆฌ๊ฐ€ ์ด ์˜ค๋ฅ˜๋ฅผ ๋„์ž…ํ•œ ์ด์œ ์™€ ๋ฉ‹์ง„ React ๊ฐœ๋ฐœ์ž๊ฐ€ ๋  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•๊ณผ ์ง์ ‘ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ๋ช‡ ๊ฐ€์ง€ ๊ธฐ๋ณธ ์ง€์นจ์„ ๋”ฐ๋ฅด๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค."

๊ทธ๋Ÿฌ๋‚˜ ๋‹ค์‹œ, hooks๋Š” ๊ต‰์žฅํ•˜๊ณ  React ํŒ€์€ ๊ต‰์žฅํ•˜๋ฉฐ ์ด ์ƒˆ๋กœ์šด ๊ฒฝ๊ณ ์กฐ์ฐจ๋„ ๊ต‰์žฅํ•ฉ๋‹ˆ๋‹ค(๋ณดํ˜ธํ•˜๋ ค๋Š” ์˜ค๋ฅ˜๋ฅผ ๋ฐœ๊ฒฌํ•˜๋Š” ๋ฐ ์ง€์˜ฅ์„ ๋Šฅ๊ฐ€ํ•  ๊ฒƒ์ด๋ผ๊ณ  ํ™•์‹ ํ•ฉ๋‹ˆ๋‹ค). ๋ˆ„๊ตฐ๊ฐ€์—๊ฒŒ ์‚ฌ๊ณผ๋ฅผ ๋นš์ง„ ์‚ฌ๋žŒ์ด ์žˆ๋‹ค๋ฉด ๋‚ด๊ฐ€ ์ฑ…์ž„์„ ์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋ฌผ๋ก  ํž˜๋“  ๊ฐ์ •์€ ์—†์Šต๋‹ˆ๋‹ค. "์™œ"์— ๋Œ€ํ•œ ๋Œ€๋‹ต์€ "๋ Œ๋”๋ง ์ค‘์— ํ•œ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋‹ค๋ฅธ ๊ตฌ์„ฑ ์š”์†Œ์— ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ๋ฅผ ํŠธ๋ฆฌ๊ฑฐํ•˜๋ฉด ๋” ์ด์ƒ ์œ„์—์„œ ์•„๋ž˜๋กœ ์ด๋™ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์•ฑ์˜ ๋ฐ์ดํ„ฐ ํ๋ฆ„์„ ์ถ”์ ํ•˜๊ธฐ๊ฐ€ ๋งค์šฐ ์–ด๋ ค์›Œ์ง‘๋‹ˆ๋‹ค"๋ณด๋‹ค ๋” ๋ณต์žกํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๊ทธ๋ ‡๊ฒŒ ํ•˜๋ฉด React์˜ ์ด์ ์„ ๋ฒ„๋ฆฌ๋Š” ๊ฒƒ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋‹ค์‹œ ํ•œ ๋ฒˆ ๋ช…ํ™•ํžˆ ํ•˜์ž๋ฉด, ์ด๊ฒƒ์€ ๊ทธ ์ž์ฒด๋กœ ์ƒˆ๋กœ์šด ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค. ํด๋ž˜์Šค์—๋Š” ํ•ญ์ƒ ๋™์ผํ•œ ๊ฒฝ๊ณ ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” Hooks๋กœ ๊ทธ๊ฒƒ์„ ๋†“์ณค๊ณ , ๊ทธ ์‹ค์ˆ˜๋ฅผ ์ˆ˜์ •ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์‚ฌ๋ก€์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ์ •ํ™•ํ•œ ์ง€์นจ์„ ๋“œ๋ฆด ์ˆ˜๋Š” ์—†์ง€๋งŒ ์–ด๋ ค์›€์„ ๊ฒช๊ณ  ์žˆ๋Š” ์˜ˆ๋ฅผ ๊ณต์œ ํ•ด ์ฃผ์‹œ๋ฉด ๊ธฐ๊บผ์ด ๋„์™€๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ํ•ญ์ƒ ์กด์žฌํ•˜๋Š” ํ•ด๋‹น ํด๋ž˜์Šค ๊ฒฝ๊ณ ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ๊ณผ ์œ ์‚ฌํ•œ ๋ฐฉ์‹์œผ๋กœ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.

์กฐ๊ธˆ์ด๋‚˜๋งˆ ๋„์›€์ด ๋˜๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค!

๋‚˜๋Š” ์ด ํ† ๋ก ์— 2์„ผํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ๊ธฐ๋Šฅ ๊ตฌ์„ฑ ์š”์†Œ์™€ ํ›„ํฌ๊ฐ€ ๋„์ž…๋œ ์ดํ›„๋กœ ๋งŽ์€ ํ˜ผ๋ž€์ด ์žˆ์—ˆ๋‹ค๋Š” @machineghost์˜ ์˜๊ฒฌ์— ๋™์˜ํ•ฉ๋‹ˆ๋‹ค. ์ปค๋ฎค๋‹ˆํ‹ฐ์—์„œ๋Š” ๋ฆฌ์•กํŠธํŒ€์„ ๋ฆฌ๋”์‹ญ์œผ๋กœ ์ฐพ๊ณ  ์žˆ๋Š”๋ฐ, ์˜ˆ์ „์—๋Š” ๋‹จ์ˆœํ–ˆ๋˜ ๊ฒƒ๋“ค์ด ์ ์  ๋ณต์žกํ•ด์ง€๊ณ  ์†Œํ†ต๊ณผ ๋ช…ํ™•ํ•œ ์‚ฌ๋ก€๊ฐ€ ๋ถ€์กฑํ•œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ผ€์ด์Šค์™€ ํฌ์ธํŠธ๋Š” ComponentDidMount ๋ฐ Unmount์ž…๋‹ˆ๋‹ค. ๋จผ์ € ํ•จ์ˆ˜ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜๋ผ๋Š” ์ง€์‹œ๋ฅผ ๋ฐ›์€ ๋‹ค์Œ ๋นˆ ๋ฐฐ์—ด๊ณผ ํ•จ๊ป˜ useEffect๋ฅผ ์‚ฌ์šฉํ•˜๋ผ๋Š” ์ง€์‹œ๋ฅผ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ์ด๊ฒƒ์ด ์ข‹์ง€ ์•Š๋‹ค๋Š” ์ง€์‹œ๋ฅผ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค. ์ด์ œ ์ด ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๊ฐ€ ์—‰๋ง์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ž˜ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋Œ€์‘ํ•˜๋Š” ๋ชจ๋“  ๋…ธ๋ ฅ์— ๊ฐ์‚ฌํ•˜์ง€๋งŒ ๋ฌธ์„œํ™” ๋ฐ ๋ชจ๋ฒ” ์‚ฌ๋ก€์— ๋” ๋งŽ์€ ๋…ธ๋ ฅ์„ ๊ธฐ์šธ์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ๋„ˆ๋ฌด ์˜ค๋žซ๋™์•ˆ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ(ํ›… ์ด์ „์—๋„ Recompose ๋“ฑ์œผ๋กœ ํด๋ž˜์Šค๋ฅผ ํ”ผํ•˜๋ ค๊ณ  ์‹œ๋„) ๊ทธ ํด๋ž˜์Šค ๊ฒฝ๊ณ ์กฐ์ฐจ ๊ธฐ์–ตํ•˜์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๊ท€ํ•˜์˜ ์‘๋‹ต์— ๊ฐ์‚ฌํ•˜์ง€๋งŒ ์ €๋Š” ์ฃผ๋กœ "๊ฒฝํ—˜ ๋ฒ•์น™", ์ง€์นจ, ๋ชจ๋ฒ” ์‚ฌ๋ก€ ๋“ฑ์„ ๊ธฐ๋Œ€ํ•˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. " ๋˜๋Š” "์ œ์–ด ํŒจํ„ด์˜ ๋ฐ˜์ „์„ ์‚ฌ์šฉํ•˜์—ฌ ์ƒํƒœ ์„ค์ •์ž๋ฅผ ํ•˜์œ„ ๊ตฌ์„ฑ ์š”์†Œ๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค."

์•„๋งˆ๋„ ์—ฌ๊ธฐ์—๋Š” ์—†์„ ์ˆ˜๋„ ์žˆ์ง€๋งŒ "๊ธฐ๋Šฅ ๊ตฌ์„ฑ ์š”์†Œ A๊ฐ€ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋ฉด ์ƒํƒœ ์„ค์ •์ž๋ฅผ ์ „๋‹ฌํ•˜๋Š” ํ•˜์œ„ ๊ตฌ์„ฑ ์š”์†Œ B๋ฅผ ๋ Œ๋”๋งํ•ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ์ž์‹ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋ Œ๋”๋ง๋˜๊ณ  ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค."

์•„๋‹ˆ๋ฉด ์ผ์š”์ผ์ด ๋Šฆ์–ด์„œ ํ•˜๋ฃจ ์ข…์ผ ๊ฐœ์ธ ํ”„๋กœ์ ํŠธ์— ๋ชฐ๋‘ํ–ˆ๊ณ , ๋‚ด ๋‘๋‡Œ๋Š” ๋„ˆ๋ฌด ํŠ€์–ด์„œ ๋‹จ์ˆœํ•œ ๊ฒƒ์„ ์–ด๋ ค์šด ๊ฒƒ์œผ๋กœ ๋งŒ๋“ค๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์–ด๋Š ์ชฝ์ด๋“ , ์ง€์นจ์— ๋Œ€ํ•œ ์ถ”๊ฐ€ ์ œ์•ˆ์ด ์žˆ์œผ๋ฉด ๋‹ค์‹œ ๊ฒŒ์‹œํ•  ๊ฒƒ์ด์ง€๋งŒ, ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ์ด์— ๋Œ€ํ•ด ์ผ์š”์ผ ๋ฐค์„ ๋ณด๋‚ด๋Š” ๊ฒƒ์„ ์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜๋„ ๋‹ค์‹œ ํ•œ ๋ฒˆ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

๋‚˜๋Š” ์šฐ๋ฆฌ๊ฐ€ ์ด ์“ฐ๋ ˆ๋“œ๊ฐ€ ๊ทธ ์œ ์šฉ์„ฑ์„ ๋Šฅ๊ฐ€ํ•˜๋Š” ์ง€์ ์— ๋„๋‹ฌํ–ˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์ผ€์ด์Šค์™€ ํฌ์ธํŠธ๋Š” ComponentDidMount ๋ฐ Unmount์ž…๋‹ˆ๋‹ค. ๋จผ์ € ํ•จ์ˆ˜ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜๋ผ๋Š” ์ง€์‹œ๋ฅผ ๋ฐ›์€ ๋‹ค์Œ ๋นˆ ๋ฐฐ์—ด๊ณผ ํ•จ๊ป˜ useEffect๋ฅผ ์‚ฌ์šฉํ•˜๋ผ๋Š” ์ง€์‹œ๋ฅผ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ์ด๊ฒƒ์ด ์ข‹์ง€ ์•Š๋‹ค๋Š” ์ง€์‹œ๋ฅผ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค. ์ด์ œ ์ด ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๊ฐ€ ์—‰๋ง์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์ €ํฌ ๋ฌธ์„œ๊ฐ€ ๋„์›€์ด ๋˜์ง€ ๋ชปํ•ด์„œ ์œ ๊ฐ์ด์ง€๋งŒ ์–ด๋–ค ํŠน์ • ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋Š”์ง€ ๋งํ•˜๊ธฐ๋Š” ๋งค์šฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋ถˆํ–‰ํžˆ๋„ ๋งค์šฐ ๋ชจํ˜ธํ•˜๋ฉฐ ์šฐ๋ฆฌ๊ฐ€ ๋งํ•˜๋ ค๊ณ  ํ•œ ๊ฒƒ์˜ ์™œ๊ณก์ž…๋‹ˆ๋‹ค. ๊ณ ์žฅ๋‚œ ์ „ํ™”๊ธฐ ๊ฒŒ์ž„์ฒ˜๋Ÿผ. ํŠน์ • ๋ฌธ์ œ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ์ƒˆ ๋ฌธ์ œ๋ฅผ ์ œ์ถœํ•˜๊ณ  ๋” ์ž์„ธํžˆ ์„ค๋ช…ํ•˜์‹ญ์‹œ์˜ค. ์ข€ ๋” ๊ตฌ์ฒด์ ์œผ๋กœ ์•Œ๋ ค์ฃผ์‹œ๋ฉด ๋„์™€๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ท€ํ•˜์˜ ์‘๋‹ต์— ๊ฐ์‚ฌํ•˜์ง€๋งŒ ์ €๋Š” ์ฃผ๋กœ "๊ฒฝํ—˜์ƒ ๊ทœ์น™", ์ง€์นจ, ๋ชจ๋ฒ” ์‚ฌ๋ก€ ๋“ฑ์„ ๊ธฐ๋Œ€ํ•˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๊ฒฝํ—˜์˜ ๋ฒ•์น™์€ ํ•ญ์ƒ "๋ Œ๋”๋งํ•˜๋Š” ๋™์•ˆ ๋ถ€์ž‘์šฉ์„ ์ˆ˜ํ–‰ํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค"์˜€์Šต๋‹ˆ๋‹ค. ๋ Œ๋”๋ง์„ ์ˆœ์ˆ˜ํ•œ ๊ณ„์‚ฐ์œผ๋กœ ์ƒ๊ฐํ•˜์‹ญ์‹œ์˜ค. ๋ถ€์ž‘์šฉ์€ ๋‹ค๋ฅธ ์œ„์น˜๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค(ํด๋ž˜์Šค์˜ ์ˆ˜๋ช… ์ฃผ๊ธฐ ๋ฉ”์„œ๋“œ ๋˜๋Š” ํ•จ์ˆ˜ ๊ตฌ์„ฑ ์š”์†Œ์˜ useEffect). ๊ทธ ์ด์ƒ์€ ์—†์Šต๋‹ˆ๋‹ค.

"๊ธฐ๋Šฅ ๊ตฌ์„ฑ ์š”์†Œ A๊ฐ€ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋ฉด ์ƒํƒœ ์„ค์ •์ž๋ฅผ ์ „๋‹ฌํ•˜๋Š” ํ•˜์œ„ ๊ตฌ์„ฑ ์š”์†Œ B๋ฅผ ๋ Œ๋”๋งํ•ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค(๊ทธ๋•Œ ๋ฐ”๋กœ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•จ). ์™œ๋ƒํ•˜๋ฉด ํ•˜์œ„ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์ƒํƒœ๋ฅผ ๋ Œ๋”๋งํ•˜๊ณ  ๋ณ€๊ฒฝํ•˜๋ฉด ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค."

์—ฌ๊ธฐ์— ์•„์ง ์˜คํ•ด๊ฐ€ ์žˆ๋Š” ๊ฒƒ ๊ฐ™์•„์š”. ์ƒํƒœ ์„ค์ •์ž๋ฅผ ์ž์‹ ๊ตฌ์„ฑ ์š”์†Œ์— ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์€ ์™„๋ฒฝํ•ฉ๋‹ˆ๋‹ค. ํ•ญ์ƒ ๊ดœ์ฐฎ์•˜๊ณ , ์•ž์œผ๋กœ๋„ ๊ทธ๋Ÿด ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋ฌธ์ œ๋Š” ๋ Œ๋”๋งํ•˜๋Š” ๋™์•ˆ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ ์ž…๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ์™„์ „ํžˆ ๋ถˆํ•„์š”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ตฌ์ฒด์ ์ธ ์˜ˆ๊ฐ€ ์—†์œผ๋ฉด ์™œ ๊ทธ๋Ÿฌ๋Š”์ง€ ์ง์ž‘ํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋„์›€์ด ์–ด๋ ต์Šต๋‹ˆ๋‹ค.

์ด ๋‘ ๋Œ€ํ™”์˜ ์ผ๋ฐ˜์ ์ธ ์ฃผ์ œ๋Š” ์šฐ๋ฆฌ๊ฐ€ ์›์„ ๊ทธ๋ฆฌ๋ฉฐ ๊ฐ€๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ† ๋ก ์€ ๋ฉ”ํƒ€๋กœ ์ „ํ™˜๋˜์—ˆ์œผ๋ฉฐ ํŠน์ • ์‚ฌ๋ก€์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•˜๋Š” ๋Œ€์‹  ๋ชจํ˜ธํ•œ ์ผ๋ฐ˜์„ฑ์— ๋Œ€ํ•ด ๋…ผ์˜ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์„œ๋กœ ์˜คํ•ดํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์ง€๋งŒ ๊ตฌ์ฒด์ ์ธ ์‚ฌ๋ก€๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์˜คํ•ด๋ฅผ ํ’€๊ธฐ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.

์ด๋Ÿฌํ•œ ์ด์œ ๋กœ ์ด ์Šค๋ ˆ๋“œ๋ฅผ ์ž ๊ธ€ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์—ฌ๊ธฐ ๊ณ„์‹  ๋ชจ๋“  ๋ถ„๋“ค์˜ ์˜๊ฒฌ์— ์ง„์‹ฌ์œผ๋กœ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค. ์ด ๊ฒฝ๊ณ ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๋ฐ ์–ด๋ ค์›€์„ ๊ฒช๊ณ  ๊ณ„์‹ ๋‹ค๋ฉด ๋” ๋งŽ์€ ์ •๋ณด๋ฅผ ๋“ฃ๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ๋„์›€์„ ๋ฐ›๋Š” ๋ฐฉ๋ฒ•์€ ์ตœ์†Œํ•œ์˜ ์žฌํ˜„ ์‚ฌ๋ก€๋กœ ๋ฌธ์ œ๋ฅผ ์ œ๊ธฐํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ๊ตฌ์ฒด์ ์ธ ๋ฌธ์ œ์— ๋Œ€ํ•ด ๋…ผ์˜ํ•˜๊ณ  ์†”๋ฃจ์…˜์„ ๋ธŒ๋ ˆ์ธ์Šคํ† ๋ฐํ•˜๋Š” ๋ฐ ๋„์›€์„ ๋“œ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๊ด€๋ จ๋œ ๋ชจ๋“  ์‚ฌ๋žŒ์—๊ฒŒ ๋” ์ƒ์‚ฐ์ ์ด๋ฉฐ, ์ด๋ฏธ ์ด ์Šค๋ ˆ๋“œ์— ๋Œ“๊ธ€์„ ๋‹ฌ๊ณ  ๊ตฌ๋…์„ ๋งˆ์นœ ์ˆ˜์‹ญ ๋ช…์˜ ์‚ฌ๋žŒ๋“ค์—๊ฒŒ ์ด๋ฉ”์ผ์„ ๋ณด๋‚ด๋Š” ๊ฒƒ์„ ํ”ผํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

์ž‘์€ ์—…๋ฐ์ดํŠธ๋กœ(๋ชจ๋‘์—๊ฒŒ ํ•‘์„ ๋ณด๋‚ด์„œ ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค) https://github.com/final-form/react-final-form/issues/751#issuecomment -606212893์„ ์‚ฌ์šฉํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์„ ์œ„ํ•ด ์ด๋Ÿฌํ•œ ์˜ค๋ฅ˜๋ฅผ ํ•ด๊ฒฐํ–ˆ๋‹ค๊ณ  ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ๋„์„œ๊ด€.

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰