Frontend 세미나 5주차] React Hooks - 1
Hooks란?
먼저 side effect 개념 알기
side Effect : react 컴포넌트가 화면에 렌더링된 이후에 비동기로 처리되어야 하는 부수적인 효과
>> 데이타를 가져오려고 외부 api를 호출할 때 화면에 렌더링할 수 있는 건 뿌려주고 실제 가져 올 데이타는 비동기로 가져오는 것이 권장되고 있다. 사용자 경험 측면에 유리하도록
-> 즉, 요구되어지는 effect 이외에 다른 effect가 발생하는 현상으로 Hook은 이 side effect를 수행하는 역할은 한다.
- 함수현 컴포넌트에서 사용되어지는 몇 가지 기술을 hook이라고 한다.
- 함수형 컴포넌트에서 상태 관리를 할 수 있는 useState, 렌더링 지표에 작업을 설정하는 useEffect 등의 다양한 기능을 제공한다.
- side effect를 줄여 그냥 effect라고도 한다.
클래스형 컴포넌트의 단점
- 클래스 문법 어려움
- 축소 어려움
- reloading의 신뢰성이 떨어짐
- 최신 기술의 적용이 효과적이지 않음
But, 함수형 컴포넌트로는 life cycle 관리나 state 같은 기능을 사용할 수 없음
=> Hook을 사용해 함수형 컴포넌트도 클래스형 컴포넌트의 기능을 사용할 수 있도록 하자!
useState
: 소괄호 안에 있는 코드의 실행 결과를 콘솔에 출력하는 함수
const [state, setState] = useState (초기값) // 인자로 초기값을 받는다.
- state : 변수. But, 일반 변수와는 다르게 값이 변하면 렌더링이 일어난다. 즉, 값이 변하게 되면 연관있는 컴포넌트들이 다시 렌더링이 되어 화면이 바뀐다.
- setState : state 값을 변경시켜주는 함수
- useState : state, setState를 return 하면서 초기값을 설정해주는 hook
주의 : state 값을 3으로 바꾸고 싶어서 state = 3을 하면 X, 왜냐하면 렌더링이 일어나지 않기 때문!
setState(3)을 해야지 렌더링이 일어나고 state 값이 변경된다.
사용 방법
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p> You clicked {count} times </p>
<button onClick={() => setCount(count + 1)}> Click me </button>
</div>
);
}
useEffect
: React 에서 제공 내장 라이브러리 api 함수로 Side Effect를 함수형에서 사용할 수 있게 하는 리액트 hooks
- 리액트 컴포넌트가 렌더링 될 때마다 특정 작업을 실행할 수 있도록 하는 Hook
- component가 mount 됐을 때, unmount 됐을 때, update 됐을 때 특정 작업 처리 가능
useEffect(function, deps)
- function : 수행하고자 하는 작업 (ex, 실행시키고자 하는 함수)
- deps : 배열 형태이며, 배열 안에는 검사하고자 하는 특정 값, or 빈 배열
사용법
1. 컴포넌트가 mount 되었을 때 (처음 렌더링 됐을 때, 나타났을 때)
- deps가 없음 : 해당 컴포넌트가 렌더링 됄 때마다 useEffect가 실행된다.
useEffect(() => { console.log("렌더링 될 때마다 실행"); });
- deps에 빈 배열 있음 : 처음 렌더링 될 때 한 번만 실행된다.
useEffect(() => { console.log("맨 처음 렌더링 될 때 한 번만 실행"); }, []);
2. 컴포넌트가 update 될 때 (특정 props나 state값이 바뀔 때)
- 특정 값이 업데이트 될 때 실행하고 싶으면 deps 위치의 배열 안에 검사하고 싶은 값을 넣어준다.
useEffect(() => {
console.log(count);
console.log("업데이트 될 때마다 실행");
}, [count]);
3. 컴포넌트가 unmount 될 때(사라질 때), update 되기 직전에
- unmount 될 때만 cleanup함수를 실행하고 싶을 때 => 두번째 파라미터로 빈 배열 넣기
- 특정 값이 없데이트 되기 직전에 cleanup함수를 실행하고 싶을 때 => deps 배열 안에 검사하고 싶은 값 넣기
useEffect(() => {
console.log("컴포넌트 나타남");
console.log(name);
return () => {
console.log("cleanup 함수");
};
}); // deps 비어있음 -> 컴포넌트가 사라질 때 cleanup 함수 호출됨
* cleanup함수 : useEffect에 대한 뒤정리를 해주는 함수로 effect가 호출되기 전과 컴포넌트가 unmount 되면서 이전 effect를 정리하는 역할을 한다.
- cleanup함수가 먼저 출력되고 effect가 호출되는 것을 확인할 수 있다. (cleanup 함수 > 컴포넌트 나타남 > 닉네임 결혼)
Ex)
import React, {useState, useEffect} from 'react';
function UseEffect() {
const [name, setName] = useState("초기 닉네임");
useEffect(() => {
console.log("컴포넌트 나타남"); //2
console.log(name);
return () => {
console.log("cleanUp 함수"); //1
};
});
const onClick = () => {
setName("닉네임 변경"); //3 (새로운 effect 호출됨)
};
return (
<div>
{theme} <button onClick = {onClick}> 변경 </button>
</div>
};
}
Todo List 실습
- todo 관련 변수 선언
const [todos, setTodos] = useState([]);
const [todo, setTodo] = useState("");
- input에 onChange 함수를 통해서 이벤트를 인식하고 화살표 함수로 이벤트를 받아서 전달을 해줘야 한다.
<input type ="text"
onChange = {(e) => { setTodo(e.target.value); }}
value = {todo}
/>
- onSubmit을 통해서 값을 전송할 때 handle submit 함수를 실행하도록 해보자 (새롭게 저장된 값을 todos에 추가해주기 위해)
- 먼저 선언을 해야 한다.
const handleSubmit = (e) => {
e.preventDefault(); // 브라우저의 기본 액션을 방지하는 handle submit 함수, ex) add todo를 누를 때 원하지 않는 새로 고침이 생길 수 있다.
const newTodo = {
id : new Date().getTime(),
text : todo,
complete : false,
};
setTodos(newTodos); // But, 기존 값이 있는 경우에는 우리가 추가한 값이 들어온 채로 초기화 되어 버리니까 기존의 todos 값들이 있다면 앞의 객체에 뒤이어서 새로운 객체를 붙이는 방식으로 구현해야 한다.
// 여러가지 방식이 있지만 여기선 spread 연산자를 통해 기존 객체를 복제한 후 새로운 todo를 추가하겠다.
};
<form onSubmit={handleSubmit}>
<input type ="text"
onChange = {(e) => { setTodo(e.target.value); }}
value = {todo}
/>
<button type="submit"> Add Todo </button>
</form>
const handleSubmit = (e) => {
e.preventDefault(); // 브라우저의 기본 액션을 방지하는 handle submit 함수, ex) add todo를 누를 때 원하지 않는 새로 고침이 생길 수 있다.
const newTodo = {
id : new Date().getTime(),
text : todo,
complete : false,
};
setTodos([...todos].concat(newTodo));
console.log(newTodo);
};
- 하지만 위의 방식으로 하면 text 칸에 기존 값들이 지워지지 않고 계속 남아 있음 -> setTodos(); 다음 setTodo(""); 를 토해 비우자.
- todo list들을 받아와서 map에 뿌려주는 flow
{todos.map((todo) => (
<div key={todo.id} className="todo">
<div className="todo actions"> {todo.text} </div>
</div>
))}
- useState를 통해 추가하는 것은 완료! 이제 삭제 기능을 만들어보자.
function deleteTodo(id) {
const updateTodos = [...todos].filter((todo) => todo.id !== id);
setTodos(updateTodos);
}
{todos.map((todo) => (
<div key={todo.id} className="todo">
<div className="todo actions"> {todo.text} </div>
<button onClick={() => deleteTodo(todo.id)}> delete </button>
</div>
))}
- back과 연동해서 만드는 todo가 아니기 때문에 기존의 todo도 불러와서 랜더링 될 수 있게끔 useEffect를 사용해서 만들어보자.
- import loadTodo from "./data.json"; 으로 json 파일 불러오기 (여기에 data들이 담길 예정)
- 새로고침을 누를 때 한번만 실행되면 되므로 useEffect에 빈 배열을 넣어주면 된다.
useEffect(() => {
setTodos(loadTodo);
}, []);
- 아래는 data.json 파일 내용
[
{
"id" : 1,
"text" : "useState 배우기",
"completed" : false
},
{
"id" : 2,
"text" : "useEffect 배우기",
"completed" : false
}
]