React (useState와 이벤트 처리)

Jeonghun·2023년 5월 25일
4

React

목록 보기
3/21


React의 State와 이벤트 처리

- useState

React는 상태를 관리하기 위해 useState라는 Hook을 제공한다. useState는 가변적인 상태를 가질 수 있는 값과 그 값을 업데이트하는 함수를 제공한다. 여기서! React는 state(상태)가 변경되면 컴포넌트를 다시 렌더링 한다.

🤔 useState는 어떻게 사용하는 거지?
useState는 초기값을 인자로 받고, 배열을 반환한다. 이 배열의 첫 번째 원소는 상태 값이고, 두 번째 원소는 해당 상태를 변경하는 함수(setter)이다. 배열 구조 분해를 사용해 각각의 변수에 값을 할당할 수 있다.

// 배열 구조 분해를 사용한 state 선언
const [state, setState] = useState(initialState);

📌 useState 사용 예시

import React, { useState } from 'react'; // useState를 사용하기 위한 import

function Counter() {
  // useState의 인자는 초기값, 반환된 배열의 첫 번째 원소는 상태 값, 두 번째 원소는 상태 변경 함수
  // 아래 코드에서 count는 현재 상태의 값, setCount는 count를 변경하는 함수이며 '0'은 초기값을 0으로 설정한다는 뜻이 된다.
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}> // 클릭 시 count 상태 값을 변경
        Click me
      </button>
    </div>
  );
}

export default Counter;

위의 예제 코드에서, useState(0)은 count라는 상태를 0으로 초기화하고, 이 상태를 변경할 수 있는 setCount 함수를 제공한다. 클릭할 때마다 setCount 함수를 호출하여 count 상태 값을 변경하면, React는 새로운 count 값에 따라 컴포넌트를 다시 렌더링한다.

- 이전 state에 의존하는 상태 업데이트

🤔 배열이나 객체의 상태를 변경하려면?
setState 함수는 업데이트할 새로운 상태 값을 인수로 받지만, 때로는 이전 상태 값에 기반한 상태 업데이트가 필요한 경우도 있다. 예를 들어, 카운터의 값을 증가시키는 것과 같은 경우를 들 수 있는데, 이 경우, setState 함수에 함수를 전달할 수 있다. 이 함수는 이전 상태 값을 인수로 받아 새 상태 값을 반환한다.
또한, React에서는 상태를 불변적으로 관리하는 것이 중요하다. 즉, 상태 객체나 배열을 직접 변경하는 것이 아니라, 상태를 변경하기 위해 새로운 객체나 배열을 생성해야 한다. 이렇게 함으로써 React는 상태 변경을 효과적으로 감지하고 필요한 컴포넌트만 다시 렌더링할 수 있게된다.

📌 객체 혹은 배열의 상태 값 업데이트 예시

// 객체 상태 업데이트
const [user, setUser] = useState({ name: 'John', age: 30 });

const updateUser = () => {
  setUser(prevUser => ({ ...prevUser, age: prevUser.age + 1 }));
};

위에서 언급한 불변성을 유지하기 위해 JavaScript의 스프레드 연산자 ('...')를 사용할 수 있다. 위 예시 코드는 객체 상태를 업데이트 하는 적절한 예시이다. setUser 함수는 이전 사용자 객체를 복사한 새 객체를 만든다(...prevUser). 그리고, age 속성만 새 값으로 변경한다.

// 배열 상태 업데이트
const [items, setItems] = useState(['Apple', 'Banana', 'Cherry']);

const addItem = (item) => {
  setItems(prevItems => [...prevItems, item]);
};

배열 상태를 업데이트할 때에도 객체와 같은 방식을 사용할 수 있다. 위의 코드에서 setItems 함수는 이전 항목 배열을 복사한 새 배열을 만든다(...prevItems). 그리고, 새 항목을 배열 끝에 추가한다.

이러한 방식으로, React 상태의 불변성을 유지하면서도 객체나 배열의 상태를 효과적으로 업데이트할 수 있게된다.

- State(상태) 끌올

🤔 React에서도 끌올을?
여러 컴포넌트가 동일한 상태 데이터를 공유해야 하는 경우, 상태를 그들의 가장 가까운 공통 상위 컴포넌트로 '끌어올리는' 것이 좋다. 이를 '상태 끌어올리기'라고 한다. (줄여서 끌올 ㅋㅋ)
상태 끌어올리기를 통해 상태 변경 로직을 한 곳에서 관리할 수 있으며, 상태가 필요한 하위 컴포넌트에는 props를 통해 상태와 상태 변경 함수를 전달할 수 있습니다. 이렇게 함으로써 코드의 중복을 줄이고, 데이터의 일관성을 유지할 수 있다.

📌 상태 끌어올리기 예시

// Child 컴포넌트
function Child({ value, onValueChange }) {
  // 입력 필드가 변경될 때 이 함수가 호출된다.
  const handleChange = event => {
    // props로 전달받은 함수를 호출한다.
    onValueChange(event.target.value);
  };

  // 입력 필드의 값은 props에서 전달 받으며, 값이 변경될 때 handleChange 함수를 호출한다.
  return (
    <input type="text" value={value} onChange={handleChange} />
  );
}

// Parent 컴포넌트
function Parent() {
  // Parent 컴포넌트는 상태를 관리한다.
  const [value, setValue] = useState("");

  // Child 컴포넌트가 값을 변경하려고 할 때, 이 함수를 호출한다.
  const handleValueChange = newValue => {
    // 여기서 상태 업데이트
    setValue(newValue);
  };

  // 현재의 값을 저장하고, 그 값을 변경하는 함수를 Child 컴포넌트에 전달한다.
  return (
    <div>
      <Child value={value} onValueChange={handleValueChange} />
      <p>Child 컴포넌트의 입력 값은: {value}</p>
    </div>
  );
}

위의 예제 코드에서, Child 컴포넌트는 입력 필드를 렌더링하고 사용자 입력을 처리한다. 그러나 Child 컴포넌트 자체는 상태를 관리하지 않는다. 대신, Parent 컴포넌트에서 상태를 관리하고 이 상태를 Child 컴포넌트에 props를 통해 전달하고있다.

Child 컴포넌트는 value와 onValueChange 두 가지 prop을 받는다. value는 현재 상태를 나타내고, onValueChange는 상태를 변경하는 함수가 된다. 사용자가 입력 필드를 변경하면, handleChange 함수가 호출되어 onValueChange를 통해 상태 변경을 Parent 컴포넌트에 알린다.

Parent 컴포넌트는 useState로 상태를 관리하고, handleValueChange 함수를 통해 Child 컴포넌트에서 알린 변경사항을 상태에 반영하고, 변경된 상태는 다시 Child 컴포넌트에 전달되어 화면에 렌더링 된다. 이런 식으로 Parent 컴포넌트는 상태를 Child 컴포넌트와 공유하며, 상태 변경은 모두 Parent 컴포넌트에서 처리되는데, 이것을 상태 끌어올리기 라고 하는 것이다.

이런 식으로, 상태 끌어올리기를 통해 여러 컴포넌트가 동일한 상태를 공유하고 동기화하는 것이 가능하다. 이는 상태 로직을 중앙화하고 코드의 중복을 줄이는 데 유용하며, 일관된 사용자 경험을 제공하는 데도 도움이 된다.

- React에서의 이벤트 처리

React의 이벤트 처리는 DOM 요소의 이벤트 처리와 유사하다. 하지만 몇 가지 차이점이 있다. React에서는 이벤트 핸들러 이름이 camelCase로 작성되어야 하며, 이벤트 핸들러에는 문자열이 아니라 함수를 전달해야 한다. 이 함수는 해당 이벤트가 발생할 때마다 실행된다.

🤔 React에서의 이벤트 처리는 어떻게 이루어지는걸까?
React에서 이벤트를 처리하는 방식은 원래의 DOM 이벤트 처리 방식과 조금 다르다. React에서는 이벤트 핸들러를 등록할 때 실제 DOM 요소에 직접 등록하는 것이 아니라, 문서의 최상위에서 리스너를 등록하고 이벤트를 직접 관리한다. 이를 '이벤트 위임'이라고 한다. 이런 방식을 사용함으로써, 컴포넌트가 마운트/언마운트될 때 매번 이벤트 리스너를 추가하거나 제거하는 비용을 줄일 수 있다.

📌 이벤트 처리 예시

function Button() {
  function handleClick(e) {
    e.preventDefault(); // 기본 이벤트를 방지
    console.log('Button was clicked.');
  }

  return (
    <button onClick={handleClick}> // 클릭 이벤트에 대한 핸들러를 설정
      Click me
    </button>
  );
}

export default Button;

위 예제 코드에서, 클릭 이벤트에 대한 핸들러로 handleClick 함수를 지정한다. 사용자가 버튼을 클릭하면, handleClick 함수가 호출되어 콘솔에 메시지를 출력한다.

- 양방향 바인딩

🤔 양방향 바인딩 그게 뭔데?
React에서 양방향 바인딩은 useState Hook과 onChange 이벤트 핸들러를 조합하여 구현할 수 있다. 양방향 바인딩은 사용자 입력에 따라 상태 값을 업데이트하고, 상태 값의 변경에 따라 입력 필드의 값을 업데이트함으로써 입력 필드와 상태 사이에 실시간 연결을 만드는 방식을 말한다.

📌 양방향 바인딩 예시

const [text, setText] = useState('');

const handleChange = (event) => {
  setText(event.target.value);
};

<input type="text" value={text} onChange={handleChange} />

위 코드에서, onChange 이벤트 핸들러는 사용자 입력에 따라 setText를 호출하여 상태 값을 업데이트 한다. value prop은 상태 값에 바인딩되어 있으므로, 상태 값이 변경될 때마다 입력 필드의 값도 업데이트 된다.


이렇게 useState를 이용하면 React 컴포넌트 내에서 상태 관리를 효과적으로 할 수 있고, 이벤트 처리를 통해 사용자와 상호작용하는 동적인 UI를 구현할 수 있다. state에 관한 설명이 길어졌는데, 그만큼 이 두 기능은 React에서 가장 중요한 기능 중 하나이므로, 잘 이해하고 사용하는 것이 중요하다.


profile
안녕하세요, 프론트엔드 개발자 임정훈입니다.

0개의 댓글