context는 다양한 레벨에 네스팅된 많은 컴포넌트에게 데이터를 전달하는 것이다.
context를 이용하면 단계마다 일일이 props를 넘겨주지 않고도 컴포넌트 트리 전체에 데이터를 제공할 수 있다.
일반적인 React 데이터는 위에서 아래로 (즉, 부모로부터 자식에게) props를 통해 전달되지만, 여러 컴포넌트들에 전해줘야 하는 props의 경우 이 과정이 번거로울 수 있다. context를 이용하면, 단계마다 props를 넘겨주지 않아도 많은 컴포넌트가 이러한 값을 공유할 수 있다.
하지만, context를 사용하면 컴포넌트를 재사용하기가 어려워지므로 꼭 필요할 때만 사용해야 한다.
Context 객체를 만든다.
const MyContext = React.createContext(defaultValue);
Context 오브젝트에 포함된 React 컴포넌트인 Provider는 context를 구독하는 컴포넌트들에게 context의 변화를 알리는 역할을 한다.
Provider 컴포넌트는 value prop을 받아서 이 값을 하위에 있는 컴포넌트에게 전달한다. 값을 전달받을 수 있는 컴포넌트의 수에 제한은 없다. Provider 하위에 또 다른 Provider를 배치하는 것도 가능하며, 이 경우 하위 Provider의 값이 우선시된다.
Provider 하위에서 context를 구독하는 모든 컴포넌트는 Provider의 value prop가 바뀔 때마다 다시 렌더링 된다
<MyContext.Provider value={{list, todolist, loading}}>
context 변화를 구독하는 React 컴포넌트
이 컴포넌트를 사용하면 함수 컴포넌트안에서 context를 구독할 수 있다.
이 함수는 context의 현재값을 받고 React 노드를 반환한다. 이 함수가 받는 value 매개변수 값은 해당 context의 Provider 중 상위 트리에서 가장 가까운 Provider의 value prop과 동일하다. 상위에 Provider가 없다면 value 매개변수 값은 createContext()에 보냈던 defaultValue와 동일할 것이다.
function TodoList(props) {
return <TodoContext.Consumer>
{(value) => value.list}
</TodoContext.Consumer>;
}
context 객체(React.createContext에서 반환된 값)을 받아 그 context의 현재 값을 반환한다. context의 현재 값은 트리 안에서 이 Hook을 호출하는 컴포넌트에 가장 가까이에 있는 <MyContext.Provider>의 value prop에 의해 결정된다.
import {TodoContext} from './App.js'
function TodoList(props) {
const { list } = useContext(TodoContext);
return <div>{list}</div>;
}
다시 렌더링할지 여부를 정할 때 참조(reference)를 확인하기 때문에, Provider의 부모가 렌더링 될 때마다 불필요하게 하위 컴포넌트가 다시 렌더링 되는 문제가 생길 수도 있다. 예를 들어 아래 코드는 value가 바뀔 때마다 매번 새로운 객체가 생성되므로 Provider가 렌더링 될 때마다 그 하위에서 구독하고 있는 컴포넌트 모두가 다시 렌더링 될 것입니다.
class App extends React.Component {
render() {
return (
<MyContext.Provider value={{something: 'something'}}>
<Toolbar />
</MyContext.Provider>
);
}
}
이를 피하기 위해서는 값을 부모의 state로 끌어올리는 방법이 있다.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: {something: 'something'},
};
}
render() {
return (
<MyContext.Provider value={this.state.value}>
<Toolbar />
</MyContext.Provider>
);
}
}
상태를 업데이트 할 때에는 useState 를 사용해서 새로운 상태를 설정었는데, 상태를 관리하게 될 때 useReducer를 사용할 수 있다.
이 Hook 함수를 사용하면 컴포넌트의 상태 업데이트 로직을 컴포넌트에서 분리시킬 수 있다. 상태 업데이트 로직을 컴포넌트 바깥에 작성 할 수도 있고, 혹은 다른 파일에 작성 후 불러와서 사용 할 수도 있다.
const initial = ['run', 'study'];
function reducer(state, action) {
switch (action.type) {
case 'ADD_LIST':
return [...state, action.payload];
default:
return state;
}
}
function App(props) {
const [state, dispatch] = useReducer(reducer, initial);
return (
<TodoContext.Provider value={{ state, dispatch }}>
<TodoList />
</TodoContext.Provider>
);
}
TodoLi 파일 중 dispatch에 atction 전달
const addClick = () => {
dispatch({ type: 'ADD_LIST', payload: inputRef.current.value });
};