본문 바로가기
WWWEB/react(&작심삼주)

(react) Events & state 처리해보기

by 미니토이 2024. 11. 26.

어제까지 To Do List앱의 컴포넌트화까지 완료가 되었는데... 

뭔가 어렵지 않은 내용인 것 같지만, 실제로 하나하나 해보니 역시 쉽게쉽게 되는 건 없는 듯...

 

이제 To Do List앱이 실제로 인터랙티브하게 구동되도록 이벤트 처리, 콜백 처리 등을 공부해보면 어느정도 리액트에 대해 가냘프게나마 알 수 있지 않을까... 하는... 작은 희망을 갖고 계속 공부를 해 보겠음~ ㅋ

 

이벤트 핸들링

<button type="button">Hello?</button>

<script>
  const btn = document.querySelector("button");

  btn.addEventListener("click", () => {
    alert("Hello!");
  });
</script>

 

 

투두앱에 이벤트를 적용하기 앞서 JSX에서의 이벤트 작성법을 살짝 알아보면,

예를 들어 지금까지는 위 코드와 같이 js를 사용을 했다면, 

jsx에서는 아래 코드와 같이 이벤트 핸들링을 직접 jsx요소에 작성해야 함.

 

<button type="button" onClick={() => alert("Hello!")}>
  Hello?
</button>

 

 

위 코드와 같이 JSX에서는 카멜케이스(camel-cased) 형태로 작성해야 하며,

모든 브라우저 이벤트는 on 뒤에 이벤트 이름을 붙이면 됨~

 

@참고: jsx 작성 시 유의사항 (https://minitoy.tistory.com/561385)

 

(react) jsx 작성 시 유의사항

하나의 부모 요소React에서 사용하는 Virtual DOM은 효율적인 컴포넌트 변화를 감지를 위해 컴포넌트 내부는 하나의 DOM 트리 구조로 이루어져야 한다는 규칙이 있기 때문 // 올바른 예 제목 내용//

minitoy.tistory.com

 

 

Form.jsx 에서 폼 처리

  1. Form() 컴포넌트 함수의 맨 위에 handleSubmit() 함수 만들고,
  2. alert() 트리거 작성
  3. <form>요소에 onSubmit 속성 추가 및 handleSubmit 값 설정.
function handleSubmit(e) {      // #스텝1
  e.preventDefault();
  alert("Hello!");       // #스텝2
}


<form onSubmit={handleSubmit}>  // #스텝3

 

 

Callback props

  1. App() 컴포넌트 함수의 맨 위에 addTask() 함수 생성 및 name 매개변수 작성
  2. addTask()를 <Form />에 prop으로 전달, prop은 시맨틱한 이름으로 작성
  3. Form.jsx의 Form() 함수에서 props를 매개변수로 작성
  4. <Form /> 컴포넌트의 handleSubmit() 함수 내에 props 추가
// #스텝1
function addTask(name) {
  alert(name);
}

// #스텝2
<Form addTask={addTask} />

// #스텝3
function Form(props) {
  // ...
}

// #스텝4
function handleSubmit(e) {
  e.preventDefault();
  props.addTask("Hello!");
}

 

 

props는 불변이기 때문에 인터랙티브하게 사용하기에는 부족하기 때문에 hook이라는 것을 사용해서 컴포넌트 자체가 소유한 데이터인 상태를 부여해줄 수 있음. 말이 좀 어려우니... 일단 용어 정리부터 좀 하고 넘어가보겠음~

 

 

React에서 상태(State)란?

컴포넌트 내부에서 변경될 수 있는 데이터를 의미함.

 

쉽게 말해, 컴포넌트가 가지고 있는 값들이라고 생각하면 되는데, 이 값들은 시간이 지나거나 사용자의 상호작용에 따라 변화하며 이러한 변화에 따라 컴포넌트의 UI가 동적으로 업데이트가 됨~

 

React 어플리케이션의 크기가 커질 수록 상태 관리가 복잡해지는데 이를 쉽게 관리하기 위해 Redux, Context API 등의 라이브러리들을 사용한다고 함.

 

상태는 useState Hook(Functional Component에서 상태 관리), Class Component(this.state 객체를 통한 상태 관리) 등의 방법으로 관리를 해주게 됨.

import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

 

위 코드는 useState Hook의 예시로 count는 상태 변수이며, setCount 함수를 통해 값을 변경할 수 있음. 

버튼을 클릭하면 setCount 함수가 호출되고, count 값이 1 증가하며 화면이 다시 렌더링 됨~

 

 

React에서 Hook이란?


React 16.8 버전부터 도입된 Hook은 함수형 컴포넌트에서 state와 생명주기 기능을 사용할 수 있도록 해주는 함수. 쉽게 말해, 클래스형 컴포넌트에서만 사용할 수 있었던 기능들을 함수형 컴포넌트에서도 사용할 수 있게 해주는 도구라고 함~

Hook을 사용하면 로직을 작은 함수로 분리하여 재사용할 수 있고, 클래스형 컴포넌트의 this 바인딩, 생명주기 메서드 등 복잡한 부분을 줄여 코드를 간결하게 해주며, 코드 가독성 증가 및 함수형 프로그래밍 적용 가능 등 여러 장점을 갖음.

주요  Hook의 종류를 정리해보면,

  • useState: 상태를 관리
  • useEffect: 컴포넌트가 마운트, 업데이트, 언마운트될 때 특정 작업을 수행
  • useContext: Context API를 사용하여 상위 컴포넌트에서 제공하는 값을 하위 컴포넌트에서 사용하도록~
  • useReducer: 복잡한 상태 로직을 관리
  • useRef: DOM 요소에 대한 참조를 유지하거나, 값을 변경하지 않고 저장 시
  • useMemo: 값이 변하지 않을 때 계산 결과를 캐싱하여 성능 향상
  • useCallback: 함수를 메모이제이션하여 불필요한 렌더링을 방지

 

 

메모이제이션(Memoization)이란?

function factorial(n) {
  if (n === 0 || n === 1) {
    return 1;
  }

  if (cache[n]) {
    return cache[n];
  }

  const result = n * factorial(n - 1);
  cache[n] = result;
  return result;
}

const cache = {};
console.log(factorial(5)); // 120
console.log(factorial(5)); // 120 (이미 계산된 값을 재사용)

 

메모이제이션은 이전에 계산된 결과를 저장해 두었다가, 동일한 입력값으로 함수가 다시 호출될 때 이전 결과를 재사용하여 불필요한 계산을 줄이는 최적화 기법으로 예를 들어 위 코드를 보면, factorial 함수는 한 번 계산된 결과를 cache 객체에 저장하고, 다음에 같은 입력값으로 호출될 때는 cache에서 값을 가져옴~

 

React에서는 useMemo, useCallback, React.memo 등의 Hook과 함수를 통해 메모이제이션을 활용한다고 함.

다시 Todo 작업으로 돌아가서~~

 

 

useState hook 사용

  1. Form.jsx파일에서 첫 번째 줄에 "import...(중략)" 추가
  2. Form()의 handleSubmit()함수에 name 등 정의
  3. 폼 입력에 값 속성 추가 및 name 설정
  4. 문자열 변경
  5. 실시간 입력을 받기 위해 onChange를 이용해 handleChange() 함수 작성
  6. <input />태그에서 입력 테스트 및 콘솔 확인
  7. hadleChange()함수 내 console을 setName()
  8. props.addTask를 호출하도록 handleSubmit()함수 변경.
// #스텝1: React hook을 사용하기 위해 모듈에서 가져오기
import { useState } from "react";

// #스텝2: 초기 name값 설정 및 setName() 정의. useState()는 이 2가지를 반환.
const [name, setName] = useState("Learn React");

// #스텝3: value props 추가하고 해당 값은 name으로 설정
<input
  type="text"
  id="new-todo-input"
  className="input input__lg"
  name="text"
  autoComplete="off"
  value={name}
/>

// #스텝4: "Use hooks!" 문자열을 빈 문자열로 변경
const [name, setName] = useState("");

// #스텝5: Form 컴포넌트 상단쪽에 handleChange() 함수 작성
function handleChange(e) {
  console.log("Typing!");
}

// #스텝6: return문 아래로 사용자의 실시간 입력을 받기 위해 onChange 이벤트 작성
<input
  type="text"
  id="new-todo-input"
  className="input input__lg"
  name="text"
  autoComplete="off"
  value={name}
  onChange={handleChange}
/>;

// #스텝7: 작동이 되는지 console로 확인되면, 실제로 저장하기 위해 console을 setName()으로 변경
function handleChange(e) {
  setName(e.target.value);
}

// #스텝8: 이름을 인수로 사용하여 props.addTask를 호출하도록 
// #      handleSubmit()함수에서 콜백 props를 이용
// #      form이 제출한 후 입력을 지우기 위해 빈 문자열을 사용하여 setName() 다시 호출
function handleSubmit(e) {
  e.preventDefault();
  props.addTask(name);
  setName("");
}

 

 

 

완료된 Form.jsx

import { useState } from "react";

function Form(props) {
  const [name, setName] = useState("");

  function handleChange(event) {
    setName(event.target.value);
  }

  function handleSubmit(event) {
    event.preventDefault();
    props.addTask(name);
    setName("");
  }

  return (
    <form onSubmit={handleSubmit}>
      <h2 className="label-wrapper">
        <label htmlFor="new-todo-input" className="label__lg">
          What needs to be done?
        </label>
      </h2>
      <input
        type="text"
        id="new-todo-input"
        className="input input__lg"
        name="text"
        autoComplete="off"
        value={name}
        onChange={handleChange}
      />
      <button type="submit" className="btn btn__primary btn__lg">
        Add
      </button>
    </form>
  );
}

export default Form;

 

 

오늘은 여기까지... ㅎㅎㅎㅎ

 

 

 

 

 

728x90
반응형

댓글