모든 React Component는 생명 주기를 가진다. reacte v16.8이후부터 함수형 Component에서도 상태 관리를 할 수 있도록 Hook이 도입되었다.
마운트
- 페이지에 컴포넌트가 나타남
업데이트
- 컴포넌트 정보를 업데이트(리렌더링이 발생함)
- props가 바뀔 때
- state가 바뀔 때
- 부모 component가 리렌더링 될 때
- forceUpdate 함수가 실행될 때
언마운트
- 페이지에서 컴포넌트가 사라짐
// 배열의 첫번째 값은 상태값, 두번째 값은 상태 변경을 위한 함수.
// useState(안의 값은 초기 값을 의미한다.
const [name, setName] = useState('');
const onChangeName = (e) => {
setName(e.target.value);
};
useEffect(() => {
console.log('렌더링완료');
console.log(name, nickname);
});
//마운트될 때만 실행하고 싶다면 두 번째 parameter로 빈 배열을 넣으면 됨
useEffect(() => {
console.log(name, nickname);
}, []);
//특정 값만 업데이트될 때만 실행하고 싶을 때는 빈 배열 안에 검사하고 싶은 값을 넣어 주면 됨
useEffect(() => {
console.log(name);
}, [name]);
const getAverage = (number) => {
console.log('계산중');
if (number.length === 0) return 0;
const sum = number.reduce((a, b) => a + b);
return sum / number.length;
};
const Average = () => {
const [list, setList] = useState([]);
const [number, setNumber] = useState('');
const onChange = (e) => {
setNumber(e.target.value);
};
const onInsert = () => {
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber('');
};
const avg = useMemo(() => getAverage(list), [list]);
return (
<div>
<input value={number} onChange={onChange} />
<button onClick={onInsert}>등록</button>
<ul>
{list.map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
<div>
<b>평균값</b> {avg} {/* list값이 수정될 때만 계산한다. */}
{/* <b>평균값</b> {getAverage(list)} */}
</div>
</div>
);
};
const onChange = useCallback((e) => {
setNumber(e.target.value);
}, []); /* 컴포넌트가 처음 렌더링될때만 생성 */
const onInsert = useCallback(() => {
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber('');
}, [number, list]); /* number, list값이 업데이트될때만 생성 */
- 일반 값 재사용 시 : useMemo, 함수 재사용 시 useCallback
const Average = () => {
const [list, setList] = useState([]);
const [number, setNumber] = useState('');
const inputE1 = useRef(null);
const onChange = useCallback((e) => {
setNumber(e.target.value);
}, []);
const onInsert = useCallback(() => {
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber('');
inputE1.current.focus(); //재랜더링되지 않음.
}, [number, list]);
useRef() VS useMemo()
- useRef : 클래스로 치면 멤버변수 혹은 dom객체 처럼 특정한 '속성값'을 기억해야 할 때
- useMemo : 복잡한 함수의 'return값'을 기억해야 할 때
useMemo() VS useCallback()
- useMemo : '함수 return 값'을 기억
- useCallback : '함수 reference'를 기억.
function reducer (state, action) {
return {...}; //불변성을 지키면서 업데이트한 새로운 상태를 반환해야 함.
}
}
//액션은 아래와 같은 형태임.
{
type : 'TYPE'
}
import React, { useReducer } from 'react';
function reducer (state, action) {
switch(action.type) {
case 'INCRMENT':
return {value:state.value+1};
case 'DECREMENT':
return {value:state.value-1};
}
}
const Counter = () => {
const [state, dispatch] = useReducer(reducer, {value:0});
return (
<div>
<p>
현재 카운터 값은<b>{state.value}</b>입니다
</p>
<button onClick={()=>dispatch({type : 'INCRMENT'})}>+1</button>
<button onClick={()=>dispatch({type : 'DECRMENT'})}>-1</button>
</div>
);
};
export default Counter;
const Info = () => {
const [state, dispatch] = useReducer(reducer, { name: '', nickname: '' });
const { name, nickname } = state;
const onChange = (e) => {
dispatch(e.target);
};
return (
<div>
<input value={name} onChange={onChange} />
<input value={nickname} onChange={onChange} />
<div>
<div>
<b>이름:</b>
{name}
</div>
</div>
<div>
<div>
<b>닉네임:</b>
{nickname}
</div>
</div>
</div>
);
};
//userInput custom Hook
import React, { useReducer } from 'react';
function reducer(state, action) {
return {
...state,
[action.name]: action.value,
};
}
export default function useInputs(initalForm) {
const [state, dispatch] = useReducer(reducer, initalForm);
const onChange = (e) => {
dispatch.apply(e.target);
};
return [state, onChange];
}
//Info
import useInputs from './useInputs';
const Info = () => {
const [state, onChange] = useInputs({
name: '',
nickname: '',
});
const { name, nickname } = state;