Hook은 함수형 컴포넌트에서 React state와 생명주기 기능을 “연동(hook into)“할 수 있게 해주는 함수이다.
함수형 컴포넌트들은 기본적으로 리렌더링이 될때, 함수 안에 작성된 모든 코드가 다시 실행됨. 이는 함수형 컴포넌트들이 기존에 가지고 있던 상태(state)를 전혀 관리(기억)할 수 없게 만듦. 하지만 Hook의 등장으로, 브라우저에 메모리를 할당 함으로써, 함수형 컴포넌트가 상태(state)를 가질 수 있게 한 것.
인자로 초기 state 값을 하나 받고
현재의 state 값과 이 값을 업데이트 하는 함수를 같이 제공

useEffect를 통해 Mounting/Updating/Unmounting 처리가 가능
Component가 re-render 될때마다 호출
useEffect 의 첫번째 parameter : rendering이 될때 실행시킬 함수
useEffect 의 두번째 parameter : 배열 (dependency array - 의존성 배열)
**//** 의존성 배열**[]**에 아무것도 넣지 않으면 Mount시에만 렌더해주고 끝남.(1번만 실행)
useEffect(()=>{
console.log("마운트 됨!!")
},**[]**)
// 의존성 배열이 없기 때문에 뭐 하나라도 바뀌면 무조건 다시 실행
useEffect(()=>{
console.log("수정하고 다시 그려짐!!")
})
// someState가 수정될때만 리렌더
useEffect(()=>{
console.log("수정하고 다시 그려짐!!")
},[someState])
useEffect(()=>{
console.log("수정하고 다시 그려짐!!")
//이부분이 끝나고 진행할 것들
return(()=>{
console.log("여기서 나갈래요!!")
})
})
componentDidUpdate와 비슷하지만 다른점 하나는, Mount 되자마자 실행되는 점.
왜 리턴문이 필요할까??
⇒ useeffect가 실행되기 전에 먼저 실행되는 return 문을 clean up함수라고 하는데, 만약 settimeout함수를 실행시키려는 useeffect일때 의존성 배열도 없고 return도 없다면 렌더링 될때마다 settimeout함수가 실행될 것이다. 나중가면 타이머가 여러개 생겨서 버그를 일으킬 수도 있기 때문에 이럴때 cleanup함수를 사용해서 타이머 함수를사용하기 전에 기존 타이머들을 다 제거해줄수가 있다.
말그대로 cleanup해준다고 생각하면 편할듯!
의존성 배열이 함수형 컴포넌트의 생명주기를 결정하는 키포인트!!
💡 ***useEffect 안에서 setState의 사용*** useEffecrt 내에서 setState를 사용할때는 **정말 필요한 경우가 아니라면 지양**컴포넌트가 마운트된 이후에 setState를 적용하게 되면,
1. state가 변경되고,
2. 변경된 state로 컴포넌트가 다시그려짐(=리렌더)
즉, useEffecrt 내에서 setState를 사용하게 되면 불필요한 리렌더나 무한루프를 일으키게 되고 성능면에서 비효율적이게 됨
React에서 DOM을 직접 컨트롤할때 사용함
규모가 큰 프로젝트에서 불필요한 리렌더링이 자주 일어나면 성능 저하 문제를 발생시키기 때문에 최적화라는 작업이 필요함
⇒ 이미 수행된 연산에 대한 결과를 기억해두었다가 동일한 연산이 다시 수행될 경우 그 값을 반환하는 방법 (callback은 함수 반환, memo는 값 반환)
계산이 복잡해질때 적절히 사용해야함
React.memo는 고차 컴포넌트(High Order Component)
고차 컴포넌트는 컴포넌트를 가져와 새 컴포넌트를 반환하는 함수
React.memo는 props가 변화하지 않으면 반환되지 않는 강화된 컴포넌트를 반환함

부모 컴포넌트인 App은 count와 text 두 가지 state를 가지고 이를 각각 CountView와 TextView의 prop으로 전달하고 있다. 만약 setCount()이 실행되면 count state의 값이 변해 App 컴포넌트가 리렌더링 되기때문에 자식 컴포넌트인 CountView와 TextView 또한 리렌더링된다. 하지만 TextView의 경우 count state와 아무 관련이 없는 컴포넌트이기 때문에 리렌더링 될 필요가 없다. 반대로 setText()가 실행되는 경우에도 CountView는 리렌더링 될 필요가 없다. 이런 경우 React.memo를 통해 리렌더되지 않도록 해주어야한다.
useMemo와의 차이점
- useMemo는 React Hooks인 반면, React.memo는 HOC(고차 컴포넌트)
- React Hooks인 useMemo는 컴포넌트 내부에서만 사용이 가능함.
데이터는 props를 통해서 부모에서 자식에게 전달 되지만, 어플리케이션 안의 여러 컴포넌트들에게 props를 전달해줘야 하는 경우 context를 이용하면 명시적으로 props를 넘겨주지 않아도 값을 공유할 수 있게 해주는 것
즉, 데이터가 필요할 때마다 props를 통해 전달할 필요가 없이 context 를 이용해 공유 가능
context API를 사용하기 위해서는 Provider , Consumer , createContext 이렇게 세가지 개념을 알고 있으면 된다.
createContext : context 객체를 생성한다.Provider : 생성한 context를 하위 컴포넌트에게 전달하는 역할을 한다.Consumer : context의 변화를 감시하는 컴포넌트이다.import React, { createContext } from "react";
import Children from "./Children";
// AppContext 객체를 생성한다.
export const AppContext = createContext();
const App = () => {
const user = {
name: "김예린",
job: "대학생"
};
return (
<>
<AppContext.Provider value={user}>
<div>
<Children />
</div>
</AppContext.Provider>
</>
);
};
export default App;
import React from "react";
import { AppContext } from "./App";
const Children = () => {
return (
<AppContext.Consumer>
{(user) => (
<>
<h3>AppContext에 존재하는 값의 name은 {user.name}입니다.</h3>
<h3>AppContext에 존재하는 값의 job은 {user.job}입니다.</h3>
</>
)}
</AppContext.Consumer>
);
};
export default Children;
import React, { useContext } from "react";
import { AppContext } from "./App";
const Children = () => {
// useContext를 이용해서 따로 불러온다.
const user = useContext(AppContext);
return (
<>
<h3>AppContext에 존재하는 값의 name은 {user.name}입니다.</h3>
<h3>AppContext에 존재하는 값의 job은 {user.job}입니다.</h3>
</>
);
};
export default Children;
App.js 에서 Context를 생성하고 Provider를 통해 전달하는 코드는 그대로지만 Children.js에서 AppContext 를 사용하는 과정에서 <AppContext.Consumer> 같은 코드를 사용해서 복잡하게 작성하지 않고 const user = useContext(AppContext)를 통해 Context를 불러 온 후 바로 사용이 가능하게 바뀌었다.
💡 **Context는 상태 관리 도구가 아니다따라서 useContext 를 사용하면 기존의 Context 사용 방식보다 더 쉽고 간단하게 Context를 사용이 가능하고, 앞서 다뤘던 useState, useEffect와 조합해서 사용하기 쉽다는 장점이 있다
리액트에서 상태관리를 하는 이유 중 대표적인 게 prop-drilling방지라고 생각했다. 그래서 context도 prop-drilling을 피하기 위해 사용하니까 상태관리 도구와 비슷하지 않을까? 라고 생각했는데, 결론적으로는 아니다.
context는 어떤 것도 저장하거나 관리하지 않는다.
애초에 상태관리란 다음을 의미한다.
Context는 이 조건에 부합하지 않으므로 상태 관리 툴이 아니라고 한다.
컨텍스트는 이미 어딘가에 존재하는 상태를 다른 컴포넌트와 공유하는 방법일 뿐이다.**
⇒ 첫번째 인자인 reducer 함수를 실행시킨다.
⇒ 컴포넌트 내에서 state의 업데이트를 일으키키 위해 사용하는 함수
⇒ action 객체를 인자로 받으며 action 객체는 어떤 행동인지를 나타내는 type 속성과 해당 행동과 관련된 데이터(payload)를 담고 있다.
⇒ action을 이용하여 컴포넌트 내에서 state의 업데이트를 일으킨다.
<button onClick={() => dispatch({ type: "INCREMENT", payload: 1 })}>증가</button>
⇒ dispatch 함수에 의해 실행되며, 컴포넌트 외부에서 state를 업데이트 하는 로직을 담당
⇒ state, action 객체를 인자로 받아, 기존의 state를 대체하여 새로운 state를 반환하는 함수
counter 예시
import React, { useReducer }from "react";
functionreducer(state, action) {
switch (action.type) {
case "INCREMENT":
return { count: state.count + action.payload };
case "DECREMENT":
return { count: state.count - action.payload };
default:
thrownewError("unsupported action type: ", action.type);
}
}
const Counter = () => {
const initialState = { count: 0 }; //초기 state
const [state, dispatch] =useReducer(reducer, initialState);
return (
<><h2>{state.count}</h2><button onClick={() => dispatch({ type: "INCREMENT", payload: 1 })}>
증가
💡 Context 와 useReducer
컴포넌트 복잡도가 보통 수준 or 외부 라이브러리를 사용하고 싶지 않을때 상태관리 목적으로 사용 가능하다고 한다.
상태관리 라이브러리와 몇가지 차이점이 있다면,
Redux ⇒ 모든 UI에 독립적이므로 별개로 사용 가능
Context & useReducer ⇒ 리액트에서만 사용가능
useReducer에는 미들웨어가 없음
얼핏 보기에는 Context + useReducer가 Redux와 거의 같아보일지 몰라도, 절대 동일하지 않고 기능적으로 리덕스를 대체할 수는 없다고 한다.