티스토리 뷰
[꼬꼬공] useState, useReducer, Props, Context API
yunieyunie 2023. 12. 17. 21:42과거에는 대부분의 리액트 컴포넌트를 클래스형 컴포넌트로 작성했다.
하지만 코드의 가독성을 높이기 위해 함수형 컴포넌트가 등장했다.
클래스형 컴포넌트에서만 가능했던 상태 관리 등의 기능을 함수형 컴포넌트에서도 수행하기 위해 리액트에서 내장 함수 Hook을 제공하고 있다.
대표적으로 상태(state)를 관리하는 useState가 있다.
🔗 useState
useState : 함수형 컴포넌트 내부에서 상태를 관리하는 hook으로, 하나의 상태 값과 그 값을 업데이트하는 함수를 쌍으로 제공한다.
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<>
Count: {count}
<button onClick={() => setCount(count + 1)}>+</button>
<button onClick={() => setCount(count - 1)}>-</button>
</>
);
}
위의 코드에서 useState는 초기 상태 값 0을 인자로 받는다.
그리고 상태 값 count와 상태를 업데이트하는 함수 setCount를 반환한다.
setCount 함수는 onClick 이벤트 핸들러 내에서 호출되어 count 값을 업데이트한다.
그런데 useState를 사용할 때 주의해야 할 점은 setState 함수는 비동기적으로 작동해 업데이트된 상태를 바로 반영하지 않는다는 점이다.
따라서 상태 업데이트 후, 바로 업데이트된 상태 값을 참조하려고 하면 오류를 일으킬 수 있다.
이 문제를 해결하기 위해 setState 함수는 다음과 같이 업데이트 함수를 인자로 받을 수 있다.
setCount(prevCount => prevCount + 1);
그런데 useState는 간단한 상태를 관리하는 데에는 충분하지만, 여러 개의 상태 값을 관리하는 복잡한 로직에서는 useState 보다 useReducer를 사용하는 것이 더 효율적이다.
🔗 useReducer
useReducer : 복잡한 상태 로직을 처리하고 관리하는 데 사용하는 hook으로, dispatch와 reducer 함수를 사용해 상태를 업데이트한다.
import React, { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, 0);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'increment'})}>+</button>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
</>
);
}
위의 코드에서 useReducer는 reducer 함수와 초기 값을 인자로 받는다.
dispatch 함수는 reducer에 action을 전달하고, reducer는 전달된 action.type에 따라 새로운 상태를 반환한다.
즉, 컴포넌트는 상태의 현재 값을 관리하고 reducer는 상태를 업데이트하는 로직을 담당하는 것이다.
이처럼 useReducer를 사용하면 상태 업데이트 로직을 컴포넌트 자체에서 분리하고 reducer 함수에 모아둘 수 있다.
이로 인해 컴포넌트의 가독성이 높아지고 명확한 업데이트 로직을 구성하는 데 도움이 된다.
그런데 이렇게 각 컴포넌트에서 생성되고 관리되는 상태 값을 다른 컴포넌트로 전달하려면 어떻게 해야할까?
대표적으로 props를 사용하는 방법이 있다.
🔗 Props
props : properties의 약자로, 컴포넌트 간에 데이터를 전달할 때 사용한다.
import React from 'react';
function ChildComponent(props) {
return <p>{props.text}</p>;
}
function ParentComponent() {
return <ChildComponent text="Hello, World!" />;
}
위의 코드에서 ParentComponent는 ChildComponent에 text라는 props를 전달한다.
ChildComponent는 이 props를 받아 화면에 텍스트를 출력하게 된다.
주의할 점은, 부모 컴포넌트로부터 자식 컴포넌트로 데이터가 전달되는 방향으로만 작동하는 단방향 데이터 플로우를 따르기 때문에 컴포넌트 내부에서 props를 변경하면 안된다는 것이다.
props를 변경해야 할 경우, state를 사용하거나 부모 컴포넌트에게 상태 변경을 요청해야 한다.
부모에서 자식으로, 또 그 자식으로 계속해서 props를 내려주며 값을 전달하면 가장 밑에 있는 자식 컴포넌트가 부모 컴포넌트에서 정의된 상태 값을 사용할 수 있다.
이렇게 컴포넌트가 4~5개 이상 중첩되어 props를 전달하는 방식을 마치 드릴을 파고 내려가는 것 같다고 하여 이를 Props Drilling 패턴이라고 부른다.
props drilling이 되면 반복이 많아지고 값을 관리하는 데 다소 복잡해질 수 있는데 이를 해결하는 것이 Context API 이다.
🔗 Context API
context API : props로 내려받지 않고도 상위 컴포넌트가 가진 데이터에 접근할 수 있게 하는 hook으로, 상위 컴포넌트에서 하위 컴포넌트로 props를 통해 데이터를 전달하는 대신, Context를 통해 데이터를 직접 전달한다.
이를 통해 중간에 위치한 컴포넌트가 불필요한 렌더링을 하지 않도록 하는 장점이 있다.
// #1. React API에서 createContext() 함수 추출
const { useState, createContext, useContext } = React;
// #2. Context 생성
const MyContext = createContext();
const App = () => {
const [data, setData] = useState('Hello from Parent');
return (
// #3. Context 객체가 제공하는 컴포넌트인 Provider를 통해 context로 활용할 영역을 그룹화
// #4. value attribute에 Context로 제공할 데이터 지정
<div>
<MyContext.Provider value={[data, setData]}>
<h1>{data}</h1>
<A />
</MyContext.Provider>
<div>MyContext와 관련이 없는 영역</div>
</div>
);
};
const A = () => {
return (
<div>
<B />
</div>
);
};
const B = () => {
return (
<div>
<C />
</div>
);
};
const C = () => {
return (
<div>
<D />
</div>
);
};
const D = () => {
const clickHandler = () => {}
// #5. React API에서 제공하는 useContext() 훅을 통해 컨텍스트로부터 데이터를 전달받음
// #6. App으로부터 데이터를 전달받기 위해 A,B,C를 거쳐서(Props Drilling) 전달받지 않아도 접근 및 사용 가능
const [ data, setData ] = useContext(MyContext);
return (
<div>
<h5>{data}</h5>
<button onClick={() => setData('Data changed')}>Change Data</button>
</div>
);
};
위의 코드 처럼 먼저 Context를 생성하고 활용될 부분을 Context.Provider로 감싸준 다음, 하위 컴포넌트에 전달할 데이터를 value 속성으로 넣어주면 된다.
이후 데이터를 사용할 컴포넌트에서는 useContext로 데이터를 전달받으면 된다.
'꼬리에 꼬리를 무는 공부 > Front-end' 카테고리의 다른 글
[꼬꼬공] 콜백 함수, Promise, Fetch API, async/await (0) | 2023.12.05 |
---|---|
[꼬꼬공] 동기/비동기, Ajax (0) | 2023.11.27 |
[꼬꼬공] 자바스크립트(Javascirpt), 호이스팅(Hoisting) (0) | 2023.11.26 |