UseState

  • λΉ„κ³΅κ°œ κ°’μœΌλ‘œ μ»΄ν¬λ„ŒνŠΈμ— μ˜ν•΄ μ œμ–΄λœλ‹€.

  • state(current state)λ₯Ό 톡해 μ ‘κ·Όν•˜κ³ , setState(set function)λ₯Ό 톡해 λ³€κ²½ν•  수 μžˆλ‹€.

  • λ°°μ—΄ ꡬ쑰 λΆ„ν•΄λ₯Ό 톡해 stateκ°’κ³Ό stateλ₯Ό λ³€κ²½ν•˜λŠ” ν•¨μˆ˜λ₯Ό λ°˜ν™˜ λ°›λŠ”λ‹€.

const [state, setState] = useState(initialValue);

λΆˆλ³€μ„±μ„ 지킀기

  • λ¦¬μ•‘νŠΈμ—μ„œ μƒνƒœ μ—…λ°μ΄νŠΈλŠ” 항상 λΆˆλ³€ν•˜κ²Œ μˆ˜ν–‰λ˜μ–΄μ•Ό ν•œλ‹€

  • 이전 μƒνƒœμ™€ ν˜„μž¬ μƒνƒœκ°€ λ™μΌν•˜λ©΄ λ¦¬λ Œλ”λ§μ„ κ±΄λ„ˆλ›Έ 수 μžˆλ‹€.(μ„±λŠ₯ μ΅œμ ν™”)

  • λ”°λΌμ„œ λΆˆλ³€ν•˜μ§€ μ•ŠμœΌλ©΄(특히 μ°Έμ‘° 값을 μ‚¬μš©ν•  λ•Œ), μƒνƒœλ₯Ό μ—…λ°μ΄νŠΈ ν–ˆμ§€λ§Œ λ¦¬λ Œλ”λ§μ΄ λ˜μ§€ μ•Šμ€ 상황이 λ°œμƒν•  수 μžˆλ‹€

  • μƒνƒœμ˜ κ°€μž₯ λ°”κΉ₯ μ°Έμ‘°λ₯Ό λ³€κ²½ν•˜κΈ°λ§Œ 해도 λ¦¬λ Œλ”λ§μ΄ νŠΈλ¦¬κ±°λœλ‹€.

const [todos, setTodos] = useState([
    { name: 1, completed: false },
    { name: 2, completed: false },
    { name: 3, completed: false }
  ]);

// ❌ λ¦¬λ Œλ”λ§λ˜μ§€ μ•ŠμŒ
const onClick = () => {
  todos[2].completed = true;
  setTodos(todos);
};

// βœ…  얕은 볡사λ₯Ό 톡해 κ°€μž₯ λ°”κΉ₯ μ°Έμ‘°λ₯Ό λΆˆλ³€ν•˜κ²Œ μ—…λ°μ΄νŠΈν•˜μ—¬ λ¦¬λ Œλ”λ§
const onClick2 = () => {
  const newTodos = todos.slice();
  newTodos[2].completed = true;
  setTodos(newTodos);
};

// βœ…  얕은 볡사λ₯Ό 톡해 κ°€μž₯ λ°”κΉ₯ μ°Έμ‘°λ₯Ό λΆˆλ³€ν•˜κ²Œ μ—…λ°μ΄νŠΈν•˜μ—¬ λ¦¬λ Œλ”λ§
const onClick3 = () => {
  setTodos((prev) => {
    const newTodos = prev.slice();
    newTodos[2].completed = true;
    return newTodos;
  });
};

배치 처리

비동기적 νŠΉμ„±μ„ κ°€μ‘ŒκΈ° λ•Œλ¬Έμ— stateλŠ” μ¦‰μ‹œ λ°˜μ˜λ˜μ§€ μ•ŠλŠ”λ‹€.

λ§Œμ•½ μ¦‰μ‹œ 반영되게 λœλ‹€λ©΄ onClick ν•¨μˆ˜μ—μ„œ 3번의 λ¦¬λ Œλ”λ§μ΄ λ°œμƒν•΄μ•Ό ν•˜λŠ” λ‚­λΉ„κ°€ λ°œμƒν•˜κ²Œ λœλ‹€.

const [counter, setCounter] = useState(0);

const onClick = async () => {
  setCounter(counter + 1);
  setCounter(counter + 1);
  setCounter(counter + 1);
};
// 1
const [counter, setCounter] = useState(0);

const onClick = async () => {
  setCounter(prevState => prevState + 1); // 큐에 μΆ”κ°€
  setCounter(prevState => prevState + 1); // 큐에 μΆ”κ°€
  setCounter(prevState => prevState + 1); // 큐에 μΆ”κ°€
};
// μƒνƒœ μ—…λ°μ΄νŠΈ
// 0 -> 1
// 1 -> 2
// 2 -> 3

직접 μˆ˜μ •ν•˜μ§€ μ•ŠκΈ°

  • Stateλ₯Ό λ³€κ²½ν•˜λ©΄ λ¦¬μ•‘νŠΈλŠ” λ³€κ²½ 뢀뢄을 λ¦¬λ Œλ”λ§ν•˜μ§€λ§Œ, 직접 λ³€κ²½(this.state.value = "foo")ν•˜λ©΄ λ¦¬λ Œλ”λ§ν•˜μ§€ μ•ŠλŠ”λ‹€.

this.state.value = "foo" // λ¦¬λ Œλ”λ§X
this.setState({value: "foo"}) //λ¦¬λ Œλ”λ§O

이전 μƒνƒœ 기반 μ—…λ°μ΄νŠΈ

  • μ—…λ°μ΄νŠΈλ˜λŠ” μƒνƒœκ°€ 이전 μƒνƒœλ₯Ό ν•„μš”λ‘œ ν•˜λŠ” 계산일 λ•Œ, 이전 μƒνƒœλ₯Ό κ°€μ Έμ™€μ„œ κ³„μ‚°ν•œλ‹€

addCount(count+1) // 
-> addCount(prevState => prevState + 1 )
updateUser(prevUser => {prevUser...,age: 20})

μƒνƒœ λ³€μˆ˜ λͺ…λͺ… κ·œμΉ™

  • setterν•¨μˆ˜μ˜ 이름 쀑 첫 κΈ€μžλ₯Ό λ”°μ„œ μ‚¬μš©ν•˜κΈ°

  • λ˜λŠ” 전체 λ³€μˆ˜ 이름을 κ°€μ Έμ˜€κΈ°

  • λ˜λŠ” prev μ ‘λ‘μ‚¬λŠ” λΆ™μ—¬ μ‚¬μš©

setCount(c => c+1)
setUserName(un => un.toLowerCase())
setIsOpen(open => !open)
setIsOpen(prevOpen=> !prevOpen)

μ—…λ°μ΄νŠΈ - 비동기

  • State와 propsλŠ” λΉ„λ™κΈ°μ μœΌλ‘œ μ—…λ°μ΄νŠΈλ  수 μžˆλ‹€ -> State λ³€κ²½ μ‹œ μ˜μ‘΄ν•˜λ©΄ μ•ˆλœλ‹€.

  • 객체 ν˜•νƒœ λ³΄λ‹€λŠ” ν•¨μˆ˜ ν˜•νƒœλ‘œ μƒνƒœ μ—…λ°μ΄νŠΈλ₯Ό μ „λ‹¬ν•œλ‹€.

//Classν˜•
<button onClick={() => this.setState({count: this.state.count + 1})}>
	button
</button>
//Functionν˜•
<button onClick={() => setCount(count + 1)}>
    Click me
</button>

μ—…λ°μ΄νŠΈ - 병합(ν΄λž˜μŠ€ν˜•)

  • React this.stateλŠ” State λ³€κ²½ ν•¨μˆ˜(setState)λ₯Ό 톡해 받은 κ°’κ³Ό κΈ°μ‘΄ Stateλ₯Ό λ³‘ν•©ν•œλ‹€.

  • Stateμ—μ„œ νŠΉμ • λ³€μˆ˜μ— λŒ€ν•œ μ—…λ°μ΄νŠΈλŠ” λ‹€λ₯Έ λ³€μˆ˜μ— 영ν–₯을 주지 μ•ŠλŠ”λ‹€.

  • λ”°λΌμ„œ λ³€κ²½λ˜μ§€ μ•ŠλŠ” state 값은 κ·ΈλŒ€λ‘œ 남아 μžˆλŠ”λ‹€.

//Classν˜•
this.state = { a: "a", b: "b"}
<button onClick={() => this.setState({ a: "b" })}></button>
// state: { a: "c", b: "b" } bμ—λŠ” 영ν–₯이 μ—†λ‹€.

μ—…λ°μ΄νŠΈ - 병합(ν•¨μˆ˜ν˜•)

  • ν•¨μˆ˜ν˜• useStateμ—μ„œλŠ” 병합이 μ•„λ‹Œ λŒ€μ²΄λ‘œ 이루어진닀.

  • state의 일뢀λ₯Ό λ³€κ²½ν•˜κΈ° μœ„ν•΄μ„œ ...μ—°μ‚°μžλ₯Ό 톡해 전체와 λ³€κ²½ 뢀뢄을 같이 μ „λ‹¬ν•œλ‹€.

//Functionν˜•
const [state, setState] = usestate({ a: "a", b: "b" });
<button onClick={() => setState({ ...state, a: "C" })}></button>
// state: { a: "c", b: "b" } bμ—λŠ” 영ν–₯이 μ—†λ‹€.

ν•˜ν–₯식(단방ν–₯) 데이터 흐름

  • StateλŠ” μ–΄λŠ μ»΄ν¬λ„ŒνŠΈμ˜ μ†Œμœ μ΄λ©°, Stateλ‘œλΆ€ν„° λ‚˜μ˜¨ UIλ‚˜ λ°μ΄ν„°λŠ” 였직 ν•˜μœ„ μ»΄ν¬λ„ŒνŠΈμ— 영ν–₯

  • μ»΄ν¬λ„ŒνŠΈλŠ” μžμ‹ μ˜ Stateλ₯Ό μžμ‹ μ»΄ν¬λ„ŒνŠΈμ˜ props둜 전달 ν•  수 μžˆλ‹€.

μƒνƒœ λŒμ–΄μ˜¬λ¦¬κΈ°

  • StateλŠ” ν•˜μœ„ μ»΄ν¬λ„ŒνŠΈλ‘œλ§Œ 전달이 κ°€λŠ₯ν•˜κΈ° λ•Œλ¬Έμ— λ‹€λ₯Έ μ»΄ν¬λ„ŒνŠΈμ™€ Stateλ₯Ό κ³΅μœ ν•˜κΈ° μœ„ν•΄ 곡톡 μ‘°μƒμœΌλ‘œ Stateλ₯Ό λŒμ–΄ 올렀 μ‚¬μš©ν•œλ‹€.

class Calculator extends React.Component {
  render() {
    return (
      <div>
        <TemperatureInput scale="c" />
        <TemperatureInput scale="f" />
      </div>
    );
  }
}
  • state.temperature λŒμ–΄ 올리기

    • state와 setState ν•¨μˆ˜λ₯Ό μžμ‹ μ»΄ν¬λ„ŒνŠΈ props둜 μ „λ‹¬ν•˜μ—¬

    • 곡톡 μ‘°μƒμ—μ„œ Stateλ₯Ό 관리

class Calculator extends React.Component {
...
    this.state = {temperature: '', scale: 'c'};
...
  render() {
...
    return (
      <div>
        <TemperatureInput
          scale="c"
          temperature={celsius}
          onTemperatureChange={this.handleCelsiusChange} />
        <TemperatureInput
          scale="f"
          temperature={fahrenheit}
          onTemperatureChange={this.handleFahrenheitChange} />
        <BoilingVerdict
          celsius={parseFloat(celsius)} />
      </div>
    );
  }
}

μƒνƒœλ₯Ό μ°ΎλŠ” 3가지 질문

  1. λΆ€λͺ¨λ‘œλΆ€ν„° propsλ₯Ό 톡해 μ „λ‹¬λ©λ‹ˆκΉŒ? 그러면 ν™•μ‹€νžˆ stateκ°€ μ•„λ‹™λ‹ˆλ‹€.

  2. μ‹œκ°„μ΄ μ§€λ‚˜λ„ λ³€ν•˜μ§€ μ•Šλ‚˜μš”? 그러면 ν™•μ‹€νžˆ stateκ°€ μ•„λ‹™λ‹ˆλ‹€.

  3. μ»΄ν¬λ„ŒνŠΈ μ•ˆμ˜ λ‹€λ₯Έ stateλ‚˜ propsλ₯Ό 가지고 계산 κ°€λŠ₯ν•œκ°€μš”? κ·Έλ ‡λ‹€λ©΄ stateκ°€ μ•„λ‹™λ‹ˆλ‹€.

  • -> https://ko.reactjs.org/docs/thinking-in-react.html

λ‚΄λΆ€ λ™μž‘μ— λŒ€ν•œ 아이디어

  • Hooks에 λŒ€ν•œ μƒνƒœλ₯Ό Reactμ—μ„œ 관리

  • useStateκ°€ 호좜이 될 λ•Œλ§ˆλ‹€ Reactμ—μ„œ λ°°μ—΄μ˜ ν•œ 곡간을 ν• λ‹Ή ν•΄ μ€€λ‹€

  • useStateλŠ” state λ³€μˆ˜μ™€ setState ν•¨μˆ˜λ₯Ό λ°°μ—΄λ‘œ λ¬Άμ–΄ λ°˜ν™˜

let componentHooks = []; // hooks에 λŒ€ν•œ μƒνƒœλ₯Ό μ €μž₯ν•˜κ³  μžˆλŠ” 곡간
let currentHookIndex = 0;

// How useState works inside React (simplified).
function useState(initialState) {
  let pair = componentHooks[currentHookIndex];
  if (pair) {
    // This is not the first render,
    // so the state pair already exists.
    // Return it and prepare for next Hook call.
    currentHookIndex++;
    return pair;
  }

  // This is the first time we're rendering,
  // so create a state pair and store it.
  pair = [initialState, setState];

  function setState(nextState) {
    // When the user requests a state change,
    // put the new value into the pair.
    pair[0] = nextState;
    updateDOM();
  }

  // Store the pair for future renders
  // and prepare for the next Hook call.
  componentHooks[currentHookIndex] = pair;
  currentHookIndex++;
  return pair;
}

https://react.dev/learn/state-a-components-memory#how-does-react-know-which-state-to-return

λ°°μ—΄ μ—…λ°μ΄νŠΈ

  • μƒˆλ‘œμš΄ 배열을 λ°˜ν™˜ν•˜λŠ” 순수 ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜μ—¬ μƒνƒœλ₯Ό μ—…λ°μ΄νŠΈ

  • κΈ°μ‘΄ 배열을 λ³€κ²½ν•˜λŠ” ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œλŠ” 이미 κΈ°μ‘΄ 배열을 λ³΅μ‚¬ν•œ ν›„ μ‚¬μš©

배열에 μ›μ†Œ μΆ”κ°€

  • 맨 뒀에 μ‚½μž…

const addTodo = ({newId, title}) => {
  setTodos(t=> [
      ...t,
      {id: newId, title: title}
  ])
}
  • νŠΉμ • 쀑간에 μ‚½μž…

const addTodo = ({newId, title}) => {
	setTodos(todos => {
		const insertAt = todos.findIndex(todo => todo.id > newId);
		const newTodos = [
			...todos.slice(0, insertAt),
			{id: newId, title},
			...todos.slice(insertAt)
		]
	})
}

λ°°μ—΄μ—μ„œ μ›μ†Œ 제거

const deleteTodo = (todoId) => {
  setTodos(todos=> todos.filter(t=>t.id! == todoId))
}

λ°°μ—΄μ˜ μ›μ†Œ λ³€κ²½

const updateTodo = ({todoId, newTitle}) => {
	setTodo(todos => todos.map((t)=> t.id === todoId ? {...t, title:newTitle} : t))
}

immer 라이브러리λ₯Ό μ‚¬μš©ν•˜μ—¬ Reactμ—μ„œ λΆˆλ³€μ„±μ„ μ§€ν‚€λ©΄μ„œ useStateλ₯Ό μ‚¬μš©ν•˜κ²Œ ν•΄μ£ΌλŠ” 라이브러리

https://www.npmjs.com/package/use-immer

μƒνƒœ ꡬ쑰화

1. μƒνƒœ κ·Έλ£Ήν™”

항상 두 개 μ΄μƒμ˜ μƒνƒœλ₯Ό λ™μ‹œμ— μ—…λ°μ΄νŠΈ ν•΄μ•Ό ν•˜λŠ” 경우, ν•˜λ‚˜λ‘œ 병합할 수 μžˆλŠ”μ§€ 생각해보아야 ν•œλ‹€.

ex. form의 inputs -> form의 값듀을 ν•˜λ‚˜μ˜ μƒνƒœλ‘œ 관리

const [name, setName] = useState('')
const [age, setAge] = useState(0)

const handleSubmit = () => {
    // 처리 둜직
    setName('')
    setAge(0)
}
const [formData, setFormData] = useState({
	name: '',
	age: 0,
})

const handleSubmit = () => {
    // 처리 둜직
    setFormData({name: '', age: 0})
}

2. λͺ¨μˆœλ˜λŠ” μƒνƒœ ν”Όν•˜κΈ°

μ„œλ‘œ λͺ¨μˆœλ˜κ³  λ™μ˜ν•  수 μ—†λŠ” μƒνƒœλ₯Ό ν”Όν•΄μ•Ό ν•œλ‹€.

ex. isTyping, isSubmitting -> 타이핑 쀑일 λ•ŒλŠ” 제좜 쀑일 수 μ—†κ³ , λ°˜λŒ€μ˜ κ²½μš°λ„ λ§ˆμ°¬κ°€μ§€

isTypingisSubmittingμƒνƒœμ˜ κ°€λŠ₯ μ—¬λΆ€

false

false

λΆˆκ°€λŠ₯

false

true

κ°€λŠ₯

true

false

κ°€λŠ₯

true

true

λΆˆκ°€λŠ₯

const [isTyping, setTyping] = useState(true)
const [isSubmitting, setSubmitting] = useState(false)
const [isStatus, setStatus] = useState('typing') // 'typing' or 'submitting'

3. 계산할 수 μžˆλŠ” μƒνƒœ ν”Όν•˜κΈ°

기쑴의 propsλ‚˜ μƒνƒœλ₯Ό 톡해 계산할 수 μžˆλŠ” μƒνƒœλŠ” μƒνƒœλ‘œ μ§€μ •ν•˜μ§€ μ•Šμ•„μ•Ό ν•œλ‹€

ex. isError -> error !== null을 톡해 확인

const [isError, setIsError] = useState(false)
const [error, setError] = useState(null)
const [error, setError] = useState(false)

const isError = useMemo(()=> error !== null,[error])

4. 쀑볡 μƒνƒœ ν”Όν•˜κΈ°

λ˜‘κ°™μ€ μƒνƒœκ°€ μ—¬λŸ¬ 개 있으면 μƒνƒœλ₯Ό λ™κΈ°ν™”ν•˜κ³  μœ μ§€ν•˜κΈ° μ–΄λ ΅λ‹€

// selectedTodoλ₯Ό μ—…λ°μ΄νŠΈ ν•˜λ €λ©΄ todos와 selectedTodoλ₯Ό λͺ¨λ‘ μ—…λ°μ΄νŠΈ ν•΄μ•Ό ν•œλ‹€.
const [todos, setTodos] = useState([...]);
const [selectedTodo, setSelectedTodo] = useState({...});
const [todos, setTodos] = useState([...]);
const [selectedTodoId, setSelectedTodoId] = useState(...);

const selectedTodo = useMemo(()=> todos.find(todo =>
   	todo.id === selectedTodoId
),[todos,selectedTodoId])

5. μƒνƒœμ˜ 깊이 μ–•κ²Œ μœ μ§€ν•˜κΈ°

μƒνƒœλ₯Ό κΉŠμ΄κ°€ 깊으면 μ—…λ°μ΄νŠΈν•˜κΈ° μ–΄λ ΅λ‹€

μƒνƒœ μœ μ§€

λ™μΌν•œ μœ„μΉ˜μ—μ„œμ˜ μƒνƒœ μœ μ§€

UI 트리의 μœ„μΉ˜κ°€ λ™μΌν•˜λ©΄ React μž…μž₯μ—μ„œλŠ” 같은 μ»΄ν¬λ„ŒνŠΈλ‘œ κ°„μ£Όλœλ‹€

{isFancy ? (
	<Counter isFancy={true} /> 
) : (
	<Counter isFancy={false} /> 
)}

μ–Έλ§ˆμš΄νŠΈ μƒνƒœ 파괴

μ»΄ν¬λ„ŒνŠΈκ°€ μ–Έλ§ˆμš΄νŠΈ 되면 μƒνƒœλŠ” νŒŒκ΄΄λ˜μ–΄ 사라진닀.

μƒνƒœκ°€ μœ μ§€ 되기 μœ„ν•΄μ„œλŠ” λ¦¬λ Œλ”λ§ 사이에 트리 ꡬ쑰가 μΌμΉ˜ν•΄μ•Ό ν•œλ‹€

{isPaused ? (
	<p>See you later!</p> 
) : (
	<Counter /> 
)}

Key 속성을 μ‚¬μš©ν•œ μƒνƒœ μž¬μ„€μ •

λ°°μ—΄μ—μ„œμ˜ key 속성을 μ‚¬μš©ν•˜λŠ” κ²ƒμ²˜λŸΌ key 속성을 μ§€μ •ν•˜λ©΄ Reactμ—μ„œ λͺ…μ‹œμ μœΌλ‘œ μ»΄ν¬λ„ŒνŠΈλ₯Ό ꡬ뢄할 수 μžˆλ‹€.

μ•„λž˜μ˜ μ˜ˆλŠ” 같은 UI 트리 μœ„μΉ˜μ˜ Counter μ»΄ν¬λ„ŒνŠΈμ§€λ§Œ, key 속성을 톡해 κ΅¬λΆ„ν•˜μ—¬ isPlayerA 값이 λ°”λ€” λ•Œλ§ˆλ‹€ μƒνƒœκ°€ μž¬μ„€μ •λœλ‹€.

{isPlayerA ? (
	<Counter key="Taylor" person="Taylor" />
) : (
	<Counter key="Sarah" person="Sarah" />
)}

Last updated