리액트의 훅(Hook)이란?
- 리액트의 함수형 컴포넌트는 클래스형 컴포넌트와 같은 문법으로 state를 생성, 작성, 수정 등이 어렵습니다.
- 그래서 함수형 컴포넌트에서도 클래스형 컴포넌트처럼 state나 여러 값들 그리고 각종 가변적인 상황에 대해 대응할 수 있도록 Hook이라는 함수를 통해 값을 다룰 수 있도록 지원합니다.
useState
- state를 관리하기 위한 함수입니다.
- 자바스크립트의 destructuring 문법으로 변수, set 함수를 할당하여 사용합니다.
import { useState } from "react";
const MyUseStateComp = () => {
// 카운터
const [value, setValue] = useState(0);
// 입력창
const [name, setName] = useState("");
const [nickName, setNickName] = useState("");
// 함수 선언
const handleChangeName = (e) => {
setName(e.target.value);
};
const handleChangeNickName = (e) => {
setNickName(e.target.value);
};
const handleNameKeyDown = (e) => {
if (e.key === "Enter") {
alert(`name : ${name}`);
setName("");
}
};
const handleNickNameKeyDown = (e) => {
if (e.key === "Enter") {
alert(`nick-name : ${nickName}`);
setNickName("");
}
};
return (
<>
<div className="comp func-comp hook-comp">
<h1> useState </h1>
<p>
{" "}
<b>value</b> : {value}{" "}
</p>
<button
onClick={() => {
setValue(value + 1);
}}
>
+1
</button>
<button
onClick={() => {
setValue(value - 1);
}}
>
-1
</button>
<br />
<input value={name} onChange={handleChangeName} onKeyDown={handleNameKeyDown} />
<input value={nickName} onChange={handleChangeNickName} onKeyDown={handleNickNameKeyDown} />
<p>name : {name}</p>
<p>nickName : {nickName}</p>
</div>
</>
);
};
export default MyUseStateComp;
- 렌더링된 페이지
usetEffect
- 컴포넌트 내부의 값이 변경될 때, 새롭게 화면을 렌더링 하는 데에 사용됩니다.
- 이벤트 리스너와 같은 용도로 사용가능합니다.
- 컴포넌트 생성 시 단 한번 초기화 하고 동작하지 않는 초기화 함수 용으로 사용 가능합니다.
import {useState, useEffect} from "react";
const MyUseEffectComp = () => {
const [name, setName] = useState('');
useEffect(()=>{
console.log('렌더링 완료!');
});
// 마운트 시에만 사용하고 싶을 때
useEffect(()=>{
console.log('렌더링 완료!');
},[]);
// 일반적인 용례
useEffect(()=>{
console.log('렌더링 완료!');
},[name]); // 배열안에 들어가는 값이 변할때!
const handleKeyDown = e => {
if(e.key==='Enter'){
alert(`name : ${name}`);
setName('');
}
}
return(
<>
<div className="comp func-comp hook-comp">
<h1>useEffect</h1>
<input value={name}
onChange={(e)=>{setName(e.target.value)}}
onKeyDown={handleKeyDown}
/>
<p> name : {name} </p>
</div>
</>
)
}
export default MyUseEffectComp;
- 렌더링 된 페이지
- 입력 값이 바뀌어 name 값이 바뀔대마다 콘솔에 로그가 찍히는 모습
useReducer
- 첫번째 매개변수로 state와 action을 매개변수로 받는 함수를 줍니다.
- 이 함수에서 action은 type 프로퍼티를 가지므로 이를 참조하여 조건을 분기 할 수도 있습니다.
- 클래스형 컴포넌트에서 이벤트 핸들러 하나로 특정 이벤트에 대해 대응하는 식으로 소프트코딩을 하고 싶을 때 함수형 컴포넌트에서는 useReducer를 통해 구현 가능합니다.
import {useState, useReducer} from "react";
function reducerFunc(state, action){
// action의 type에 따라 분기
switch(action.type){
case 'INC':
return {value : state.value +1};
case 'DEC':
return {value : state.value -1};
default:
return state;
}
}
// 이벤트 핸들러 하나로 처리듯이 하는 문법
function reducerFunc2(state, action){
return{
...state,
[action.name]:action.value
};
}
const MyUseReducer = () => {
// const [state, dispatch] = useReducer(reducerFunc, {value:0});
const [state, dispatch] = useReducer(reducerFunc2, {
name:'',
nickname:'',
});
const {name, nickname} = state;
const handleOnChange = e => {
dispatch(e.target);
}
return(
<>
<div className="comp func-comp hook-comp">
<h1>Reducer</h1>
<p>count : <b>{state.value}</b></p>
<button onClick={()=>{dispatch({type:'INC'})}}>+1</button>
<button onClick={()=>{dispatch({type:'DEC'})}}>-1</button>
<hr></hr>
<input name="name" value={name} onChange={handleOnChange} />
<input name="nickname" value={nickname} onChange={handleOnChange} />
<p>name : {name}</p>
<p>nickname : {nickname}</p>
</div>
</>
)
}
export default MyUseReducer;
- 렌더링 된 페이지
useMemo
- state 값이나 어떤 변수, 배열의 값이 변하지 않았을 경우 이미 계산된 데이터를 참조하여 렌더링 성능을 올려주는 Hook 함수입니다.
- 가령 전체 리스트의 평균이 변화되거나, 숫자를 입력하고 평균이나 배열 내부 값의 변화가 있었을 때에만 데이터를 갱신하게 됩니다.
import {useState, useMemo} from "react";
const getAvg = numbers => {
console.log('평균 계산중....');
if(numbers.length === 0) return 0;
const sum = numbers.reduce((a,b)=> a+b);
return sum / numbers.length;
}
const MyUseMemo = () =>{
const [list, setList] = useState([]);
const [number, setNumber] = useState('');
const handleOnChange = e => {
setNumber(e.target.value);
};
const onInsert = () => {
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber('');
}
// useMemo
const avg = useMemo(()=>getAvg(list), [list]);
// 이렇게 해두면 렌더링 될때마다 함수가 호출되지 않는다.
return(
<>
<div className="comp func-comp hook-comp">
<h1>useMemo</h1>
<input
value={number}
onChange={handleOnChange}
/>
<button onClick={onInsert}>등록</button>
<p>평균 : <b>{avg}</b></p>
<ul>
{list.map((value, index)=>(
<li key={index}>{value}</li>
))}
</ul>
</div>
</>
)
}
export default MyUseMemo;
- 렌더링 된 페이지
useCallback
- useMemo + useEffect를 합쳐놓은 듯한 Enhanced Hook라고 볼 수 있습니다.
- useEffect 처럼 컴포넌트 생성 시 생성자처럼 state를 초기화하는 데 사용하거나
- useMemo처럼 state 값에 변화가 있을 때에만 작동하는 함수를 작성하는 데 응용 가능합니다.
import {useState, useMemo, useCallback} from "react";
const getAvg = numbers => {
console.log('평균 계산중....');
if(numbers.length === 0) return 0;
const sum = numbers.reduce((a,b)=> a+b);
return sum / numbers.length;
}
const MyUseCallback = () => {
const [list, setList] = useState([]);
const [number, setNumber] = useState('');
// useCallback
const handleOnChange = useCallback(e=>{
setNumber(e.target.value);
}, []); // 컴포넌트가 처음 렌더링 될 때만 함수 생성
const onInsert = useCallback(()=>{
if(isNaN(number) || number=='' || number == null){
alert('잘못 값을 입력하셨습니다.');
setNumber('');
return;
}
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber('');
}, [number, list]); // number 또는 list가 바뀌었을 때만 작동!
const avg = useMemo(()=>getAvg(list), [list]);
return(
<>
<div className="comp func-comp hook-comp">
<h1>useCallback</h1>
<input
value={number}
onChange={handleOnChange}
/>
<button onClick={onInsert}>등록</button>
<p>평균 : <b>{avg}</b></p>
<ul>
{list.map((value, index)=>(
<li key={index}>{value}</li>
))}
</ul>
</div>
</>
)
}
export default MyUseCallback;
- 렌더링 된 페이지
useRef
- ref를 관리하기 위한 함수입니다.
- 내부 변수를 선언하는 데에도 응용될 수 있습니다.
import {useState, useMemo, useCallback, useRef, useEffect} from "react";
const getAvg = numbers => {
console.log('평균 계산중....');
if(numbers.length === 0) return 0;
const sum = numbers.reduce((a,b)=> a+b);
return sum / numbers.length;
}
const MyUseRef = () => {
const [list, setList] = useState([]);
const [number, setNumber] = useState('');
const inputEl = useRef(null);
// useCallback
const handleOnChange = useCallback(e=>{
setNumber(e.target.value);
}, []); // 컴포넌트가 처음 렌더링 될 때만 함수 생성
const onInsert = useCallback(()=>{
if(isNaN(number) || number=='' || number == null){
alert('잘못 값을 입력하셨습니다.');
setNumber('');
return;
}
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber('');
inputEl.current.focus(); // useRef로 추가된 부분!;
}, [number, list]); // number 또는 list가 바뀌었을 때만 작동!
const avg = useMemo(()=>getAvg(list), [list]);
// useRef를 사용하면 로컬변수를 사용할 수 있습니다.
// 단, 로컬 변수는 바뀌더라도 리렌더링 되지 않으므로
// 렌더링이 필요 없는 변수에 한해서 사용해야 합니다.
// 변수에 useRef를 통해 할당한 후 current를 통해 접근하는 식으로 사용합니다!
const id = useRef(1);
const setId = (n) => {
this.id = n;
}
const printId = () => {
console.log(`id : ${id.current}`);
}
useEffect(()=>{
printId();
}, [list]);
return(
<>
<div className="comp func-comp hook-comp">
<h1>MyUseRef</h1>
<input
ref={inputEl}
value={number}
onChange={handleOnChange}
/>
<button onClick={onInsert}>등록</button>
<p>평균 : <b>{avg}</b></p>
<ul>
{list.map((value, index)=>(
<li key={index}>{value}</li>
))}
</ul>
</div>
</>
)
}
export default MyUseRef;
- 렌더링 된 페이지
'프론트엔드(Front-End) > React' 카테고리의 다른 글
[REACT] Context API (2) | 2023.01.10 |
---|---|
[REACT] 리액트와 라우터(Router) (2) | 2023.01.09 |
[REACT] 리액트의 라이프사이클(LifeCycle) (0) | 2023.01.07 |
[REACT] 컴포넌트 요소의 반복(map, filter) (1) | 2023.01.06 |
[REACT] 리액트의 ref (0) | 2023.01.05 |