우리가 현재 만들고 있는 프로젝트를 보면, App 컴포넌트에서 onToggle, onRemove 가 구현이 되어있고 이 함수들은 UserList 컴포넌트를 거쳐서 각 User 컴포넌트들에게 전달이 되고 있다.
컴포넌트들이 상위 컴포넌트에서 하위컴포넌트로 전달되어지는 방법이다.
상위컴포넌트에서 함수들이 전달되어질 때, 거쳐야 할 컴포넌트가 여러개 ( 즉 하위 컴포넌트의 하위 컴포넌트의 ... )가
되면 매우 번거로운 작업이 될 것이다.
이 때 리액트의 Context API 와 이전 섹션에서 배웠던 dispatch 를 함께 사용하면 이러한 복잡한 구조를 해결 할 수 있다.
먼저 이렇게 React.createContext() 라는 함수를 사용한다.
const UserDispatch = React.createContext(null);
이러면 Context의 기본 값을 설정해줄 수 있다. 위에서 설정한 값은 따로 값을 사용하지 않았을 때의 기본 값이다.
Context를 만들면 Context안에 Provider 라는 컴포넌트가 들어있는데, 이 컴포넌트를 통해 Context의 값을 지정해줄 수
있다. value라는 값을 설정해주면 된다.
<UserDispatch.Provider value={dispatch}>...</UserDispatch.Provider>
아래 전체 코드를 보면서 이해해보자.
App.js
import React, { useRef, useReducer, useMemo, useCallback } from 'react';
import UserList from './UserList';
import CreateUser from './CreateUser';
import useInputs from './hooks/useInputs';
function countActiveUsers(users) {
console.log('활성 사용자 수를 세는중...');
return users.filter(user => user.active).length;
}
const initialState = {
users: [
{
id: 1,
username: 'velopert',
email: 'public.velopert@gmail.com',
active: true
},
{
id: 2,
username: 'tester',
email: 'tester@example.com',
active: false
},
{
id: 3,
username: 'liz',
email: 'liz@example.com',
active: false
}
]
};
function reducer(state, action) {
switch (action.type) {
case 'CREATE_USER':
return {
users: state.users.concat(action.user)
};
case 'TOGGLE_USER':
return {
...state,
users: state.users.map(user =>
user.id === action.id ? { ...user, active: !user.active } : user
)
};
case 'REMOVE_USER':
return {
...state,
users: state.users.filter(user => user.id !== action.id)
};
default:
return state;
}
}
// UserDispatch 라는 이름으로 내보내줍니다.
export const UserDispatch = React.createContext(null);
function App() {
const [{ username, email }, onChange, onReset] = useInputs({
username: '',
email: ''
});
const [state, dispatch] = useReducer(reducer, initialState);
const nextId = useRef(4);
const { users } = state;
const onCreate = useCallback(() => {
dispatch({
type: 'CREATE_USER',
user: {
id: nextId.current,
username,
email
}
});
onReset();
nextId.current += 1;
}, [username, email, onReset]);
const onToggle = useCallback(id => {
dispatch({
type: 'TOGGLE_USER',
id
});
}, []);
const onRemove = useCallback(id => {
dispatch({
type: 'REMOVE_USER',
id
});
}, []);
const count = useMemo(() => countActiveUsers(users), [users]);
return (
<UserDispatch.Provider value={dispatch}>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} onToggle={onToggle} onRemove={onRemove} />
<div>활성사용자 수 : {count}</div>
</UserDispatch.Provider>
);
}
export default App;
위에서 말했듯이 React.createContext 함수로 내보냈고,
<UserDispatch.Provider value={dispatch}>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} onToggle={onToggle} onRemove={onRemove} />
<div>활성사용자 수 : {count}</div>
</UserDispatch.Provider>
이런 식으로 컴포넌트들을 .Provider에 감싸고, value로 Context의 값을 지정해주었다.
그 다음 감싼 컴포넌트 내에서 먼저 import를 해주고,
import { UserDispatch } from './App';
아래와 같이 useContext를 사용하여 불러왔던 UserDispatch를 사용하게끔 설정해주면 끝!!
const User = React.memo(function User({ user }) {
const dispatch = useContext(UserDispatch);
이제 우리는 여러 컴포넌트를 거쳐서 전달할 필요없이 최상위 폴더에서 React.createContext 함수를 써서 내보내고,
Provider로 하위 컴포넌트를 감싼다음에 사용할 필요가 있을 때 useContext로 편하게 불러올 수 있다!
참고로 React.createContext를 안쓰고
import React, { useReducer, createContext } from 'react';
이런식으로 불러와도 된다.
참조
'Front-End > React.js' 카테고리의 다른 글
React.js | 입문 | Immer (0) | 2021.09.17 |
---|---|
React.js | 입문 | Context API 전역 값 관리(2) (0) | 2021.09.17 |
React.js | 입문 | React.memo (0) | 2021.09.17 |
React.js | 입문 | useCallback (0) | 2021.09.17 |
React.js | 입문 | 커스텀 Hook (0) | 2021.09.17 |