react.js

react ๊ณต์‹๋ฌธ์„œ ํ•™์Šต- Updating Objects in State

tues 2023. 11. 28. 22:46

๐ŸŽˆ ๊ฐ์ฒด state ์—…๋ฐ์ดํŠธ

 

state๋Š” ๊ฐ์ฒด๋ฅผ ํฌํ•จํ•ด์„œ ์–ด๋–ค ์ข…๋ฅ˜์˜ javascript ๊ฐ’์ด๋“  ์ €์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ react state์— ์žˆ๋Š” ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ๋ณ€์ดํ•ด์„œ๋Š” ์•ˆ๋ฉ๋‹ˆ๋‹ค. ⇒ ๊ฐ์ฒด์˜ ๋ถˆ๋ณ€์„ฑ

๋Œ€์‹  ๊ฐ์ฒด๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋ ค๋ฉด ์ƒˆ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  (ํ˜น์€ ๊ธฐ์กด ๊ฐ์ฒด์˜ ๋ณต์‚ฌ๋ณธ์„ ๋งŒ๋“ค๊ณ  = spread ์—ฐ์‚ฐ์ž) ํ•ด๋‹น ๋ณต์‚ฌ๋ณธ์„ ์‚ฌ์šฉํ•˜๋„๋ก state๋ฅผ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • react state์—์„œ ๊ฐ์ฒด๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฐฉ๋ฒ•
  • ์ค‘์ฒฉ๋œ ๊ฐ์ฒด๋ฅผ ๋ณ€์ดํ•˜์ง€ ์•Š๊ณ  ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฐฉ๋ฒ•
  • ๋ถˆ๋ณ€์„ฑ์ด๋ž€ ๋ฌด์—‡์ด๋ฉฐ, ๋ถˆ๋ณ€์„ฑ์„ ๊นจ๋œจ๋ฆฌ์ง€ ์•Š๋Š” ๋ฐฉ๋ฒ•
  • Immer๋กœ ๊ฐ์ฒด ๋ณต์‚ฌ๋ฅผ ๋œ ๋ฐ˜๋ณต์ ์œผ๋กœ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•

๐ŸŽˆ ๋ณ€์ด๋ž€ ๋ฌด์—‡์ธ๊ฐ€?

javascript ๊ฐ’์€ ๋ถˆ๋ณ€, ๋ณ€์ดํ•  ์ˆ˜ ์—†๊ฑฐ๋‚˜ ์ฝ๊ธฐ์ „์šฉ→ ๋‹ค์‹œ ๋ Œ๋”๋ง์„ ์ผ์œผ์ผœ์„œ ๊ฐ’์„ ๋ณ€ํ™”๊ฐ€๋Šฅ

const [x, setX] = useState(0);

setX(5);

X state๊ฐ€ 0์—์„œ 5๋กœ ๋ณ€๊ฒฝ๋˜์—ˆ์ง€๋งŒ ์ˆซ์ž 0 ์ž์ฒด๋Š” ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. javascript์—์„œ๋Š” ์ˆซ์ž, ๋ฌธ์ž์—ด, ๋ถˆ๋ฆฌ์–ธ๊ณผ ๊ฐ™์€ ๋นŒํŠธ์ธ ์›์‹œ ์ž๋ฃŒํ˜• ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์›์‹œ ํƒ€์ž… (Primitive Type)

์›์‹œ ํƒ€์ž…์€ ๋ชจ๋‘ ํ•˜๋‚˜์˜ ๊ฐ’์„ ๋‹ด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๋ฌธ์ž(string)
  • ์ˆซ์ž(number)
  • bigint
  • ๋ถˆ๋ฆฌ์–ธ(boolean)
  • null
  • undefined
  • ์‹ฌ๋ณผ(symbol) => ES6๋ถ€ํ„ฐ ์ถ”๊ฐ€๋œ ํƒ€์ž…

const [position, setPosition] = useState({ x: 0, y: 0 });

๊ฐ์ฒด ์ž์ฒด์˜ ๋‚ด์šฉ์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. → ๋ณ€์ด

์ˆซ์ž, ๋ถˆ๋ฆฌ์–ธ, ๋ฌธ์ž์—ด๊ณผ ๊ฐ™์ด ๋ถˆ๋ณ€ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์ทจ๊ธ‰ํ•ด์•ผ ํ•จ, ์ง์ ‘ ๋ณ€์ดํ•˜๋Š” ๋Œ€์‹  ๊ต์ฒดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

๐ŸŽˆ state๋ฅผ ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ์ทจ๊ธ‰ํ•˜์„ธ์š”

๋‹ค์‹œ ๋งํ•ด state์— ๋„ฃ๋Š” ๋ชจ๋“  javascript ๊ฐ์ฒด๋ฅผ ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ์ทจ๊ธ‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

onPointerMove={e => {

position.x = e.clientX;

position.y = e.clientY;

}}

 

์ด์ „ ๋ Œ๋”๋ง์—์„œ position์— ํ• ๋‹น๋œ ๊ฐ์ฒด๋ฅผ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ state ์„ค์ •์ž ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด react๋Š” ๊ฐ์ฒด๊ฐ€ ๋ณ€์ด๋˜์—ˆ๋‹ค๋Š” ์‚ฌ์‹ค์„ ์•Œ์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ react๋Š” ์•„๋ฌด ๋ฐ˜์‘๋„ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

state ๋ณ€์ด๋Š” ๊ฒฝ์šฐ์— ๋”ฐ๋ผ ์ž‘๋™ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๊ถŒ์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ Œ๋”๋ง์—์„œ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” state๊ฐ’์€ ์ฝ๊ธฐ์ „์šฉ์œผ๋กœ ์ทจ๊ธ‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

onPointerMove={e => {

setPosition({

x: e.clientX,

y: e.clientY

});

}}

 

์ƒˆ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ state ์„ค์ •์ž ํ•จ์ˆ˜์— ์ „๋‹ฌ → ๋ฆฌ๋ Œ๋”๋ง !

setPosition์œผ๋กœ react์— 1. position์„ ์ƒˆ ๊ฐ์ฒด๋กœ ๋ฐ”๊ฟˆ, 2. ์ปดํฌ๋„ŒํŠธ ๋‹ค์‹œ ๋ Œ๋”๋ง ์‹œํ‚ด

 

๐ŸŽˆ ์ „๊ฐœ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ์ฒด ๋ณต์‚ฌํ•˜๊ธฐ

position ๊ฐ์ฒด๋Š” ํ•ญ์ƒ ํ˜„์žฌ ์ปค์„œ ์œ„์น˜์—์„œ ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์กŒ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ข…์ข… ๊ธฐ์กด ๋ฐ์ดํ„ฐ๋ฅผ ์ƒˆ๋กœ ๋งŒ๋“œ๋Š” ๊ฐ์ฒด์˜ ์ผ๋ถ€๋กœ ํฌํ•จ์‹œํ‚ค๊ณ  ์‹ถ์„ ๋•Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, form์— ์žˆ๋Š” ํ•˜๋‚˜์˜ ํ•„๋“œ๋งŒ ์—…๋ฐ์ดํŠธํ•˜๊ณ  ๋‹ค๋ฅธ ๋ชจ๋“  ํ•„๋“œ๋Š” ์ด์ „ ๊ฐ’์„ ์œ ์ง€ํ•˜๊ณ  ์‹ถ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import { useState } from 'react';

export default function Form() { const [person, setPerson] = useState({ firstName: 'Barbara', lastName: 'Hepworth', email: 'bhepworth@sculpture.com' });

function handleFirstNameChange(e) { person.firstName = e.target.value; }

function handleLastNameChange(e) { person.lastName = e.target.value; }

function handleEmailChange(e) { person.email = e.target.value; }

return ( <> <label> First name: <input value={person.firstName} onChange={handleFirstNameChange} /> </label> <label> Last name: <input value={person.lastName} onChange={handleLastNameChange} /> </label> <label> Email: <input value={person.email} onChange={handleEmailChange} /> </label> <p> {person.firstName}{' '} {person.lastName}{' '} ({person.email}) </p> </> ); }

 

์ด ์ค„์€ ์ด์ „ ๋ Œ๋”๋ง ์‹œ์˜ state๋ฅผ ๋ณ€์ดํ•ฉ๋‹ˆ๋‹ค

person.firstName = e.target.value;

 

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

setPerson({

firstName: e.target.value, // New first name from the input

// input์—์„œ ๋ฐ›์•„์˜จ ์ƒˆ๋กœ์šด first name

lastName: person.lastName,

email: person.email

});

 

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

setPerson({

...person, // Copy the old fields

// ์ด์ „ ํ•„๋“œ๋ฅผ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค.

firstName: e.target.value // But override this one

// ๋‹จ, first name๋งŒ ๋ฎ์–ด์”Œ์›๋‹ˆ๋‹ค.

});

 

๊ฐ ์ž…๋ ฅ ํ•„๋“œ์— ๋Œ€ํ•ด ๋ณ„๋„์˜ state ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•˜์ง€ ์•Š์€ ๊ฒƒ์„ ์ฃผ๋ชฉํ•˜์„ธ์š”. ํฐ ์–‘์‹์˜ ๊ฒฝ์šฐ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ์ฒด์— ๊ทธ๋ฃนํ™”ํ•˜์—ฌ ๋ณด๊ด€ํ•˜๋Š” ๊ฒƒ์ด ๋งค์šฐ ํŽธ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

 

function handleFirstNameChange(e) { setPerson({ ...person, firstName: e.target.value }); }

return ( <> <label> First name: <input value={person.firstName} onChange={handleFirstNameChange} /> </label>

 

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

 

๐ŸŽˆ ์ค‘์ฒฉ๋œ ๊ฐ์ฒด ์—…๋ฐ์ดํŠธํ•˜๊ธฐ

const [person, setPerson] = useState({

name: 'Niki de Saint Phalle',

artwork: {

title: 'Blue Nana',

city: 'Hamburg',

image: 'https://i.imgur.com/Sd1AgUOm.jpg',

}

});

 

person.artwork.city๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋ ค๋ฉด ๋ณ€์ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ๋ช…ํ™•ํ•ฉ๋‹ˆ๋‹ค.

person.artwork.city = ‘new delhi’;

 

ํ•˜์ง€๋งŒ react์—์„œ๋Š” state๋ฅผ ๋ถˆ๋ณ€์œผ๋กœ ์ทจ๊ธ‰ํ•ฉ๋‹ˆ๋‹ค. city๋ฅผ ๋ณ€๊ฒฝํ•˜๋ ค๋ฉด ๋จผ์ € ์ƒˆ artwork ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ ๋‹ค์Œ ์ƒˆ artwork๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ์ƒˆ๋กœ์šด ์‚ฌ๋žŒ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

const nextArtwork = { ...person.artwork, city: 'New Delhi' };

const nextPerson = { ...person, artwork: nextArtwork };

setPerson(nextPerson);

 

๋˜๋Š” ๋‹จ์ผ ํ•จ์ˆ˜ ํ˜ธ์ถœ๋กœ ์ž‘์„ฑ๊ฐ€๋Šฅ

setPerson({

...person, // Copy other fields

artwork: { // but replace the artwork

// ๋Œ€์ฒดํ•  artwork๋ฅผ ์ œ์™ธํ•œ ๋‹ค๋ฅธ ํ•„๋“œ๋ฅผ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค.

...person.artwork, // with the same one

city: 'New Delhi' // but in New Delhi!

// New Delhi'๋Š” ๋ฎ์–ด ์”Œ์šด ์ฑ„๋กœ ๋‚˜๋จธ์ง€ artwork ํ•„๋“œ๋ฅผ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค!

}

});

 

๐ŸŽˆ Immer๋กœ ๊ฐ„๊ฒฐํ•œ ์—…๋ฐ์ดํŠธ ๋กœ์ง ์ž‘์„ฑ

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

updatePerson(draft => {

draft.artwork.city = 'Lagos';

});

 

ํ•˜์ง€๋งŒ ์ผ๋ฐ˜ ๋ณ€์ด์™€ ๋‹ฌ๋ฆฌ ์ด์ „ state๋ฅผ ๋ฎ์–ด์“ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค

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

 

๐ŸŽˆ react์—์„œ state ๋ณ€์ด๋ฅผ ๊ถŒ์žฅํ•˜์ง€ ์•Š๋Š” ์ด์œ ๋Š”?

  1. ๋””๋ฒ„๊น…: ๋ Œ๋”๋ง ์‚ฌ์ด์— state๊ฐ€ ์–ด๋–ป๊ฒŒ ๋ณ€๊ฒฝ๋˜์—ˆ๋Š”์ง€ ๋ช…ํ™•ํ•˜๊ฒŒ ํ™•์ธ์„ ์œ„ํ•ด์„œ
  2. ์ตœ์ ํ™”: ๋ณ€๊ฒฝ์ด ์žˆ์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ด ๋งค์šฐ ๋น ๋ฆ„
  3. ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ: ๊ณผ๊ฑฐ ๋ฒ„์ „ state๋ฅผ ๋ณ€์ดํ•˜๋Š” ๊ฒฝ์šฐ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์ง€ ๋ชปํ•  ์ˆ˜ ์žˆ์Œ
  4. ์š”๊ตฌ์‚ฌํ•ญ ๋ณ€๊ฒฝ : ์žฌ์‚ฌ์šฉ๊ฐ€๋Šฅ
  5. ๋” ๊ฐ„๋‹จํ•œ ๊ตฌํ˜„: ๋ณ€์ด์— ์˜์กดํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ํŠน๋ณ„ํ•œ ์ž‘์—…์ด ํ•„์š”๊ฐ€ ์—†์Œ