useContext는 prop을 통해 여러 뎁스를 거쳐 전달되는 prop을 한번에 바로 가져와 사용할 수 있다. 그래서 useContext와 useReducer를 합쳐서 Redux를 흉내낼 수 있다고 하는데 나는 Redux를 안배워봐서 잘 모르겠다.
그럼 일단 context부터 만들어보자.
button의 테마를 일단 context에 저장한다음 button 컴포넌트에서 바로 사용해보도록 하겠다.
// buttonContext.js
import { createContext } from 'react'
export const buttonContext = createContext('black')
export default function ButtonContextFunc(props){
return (
<buttonContext.Provider value='light'>
{props.children}
</buttonContext.Provider>
)
}
createContext로 초기값을 설정한다.
그리고 Provider로 감싸고 context를 통해 전달하고자 하는 값을 value에 지정한다.
children이라는 뜻은 ButtonContextFunc 컴포넌트로 감싸면 그안에 있는 모든 컴포넌트는 이제 value값에 접근가능하다.
그럼 App에서 선언한뒤에 Button 컴포넌트에서 바로 context에 저장된 값을 전달받고 써보자.
//App.js
import ButtonContextFunc from './ButtonContext'
import Button from './Button'
function App(props) {
return (
<ButtonContextFunc>
<Button/>
</ButtonContextFunc>
)
}
//Button.js
import { useContext } from 'react'
import { buttonContext } from './ButtonContext'
function Button(props) {
const buttonTheme = useContext(buttonContext)
return (
<button style={{ color:`${buttonTheme}` }}>
click here
</button>
)
}
ButtonContext에 있는 값을 받아 사용할때는 useContext안에 Provider로 사용된 my context(여기선 buttonContext)를 pass하면 된다. 그럼 useContext 가 Provider에서 지정했던 value값을 리턴한다.
그리고 value가 바뀔때 마다 context를 사용한 consumer 컴포넌트가 리랜더링 된다.
그럼 더이상 Button 컴포넌트의 조상들을 통해서 prop을 전달받지 않아도 된다. 진짜 간편하다.
주의해야할점은 value가 바뀔때 context를 사용하는 곳은 전부 리랜더링 되므로 context를 세분화 시켜서 딱 필요한 곳에만 적용시켜주면 쓸데없는 랜더링을 막을 수 있으니 참고하자!
좀 더 설명하자면, 아래의 코드와 같이 되어있을때
export const dispatchContext = createContext("");
export const issuesContext = createContext("");
const issuesList = [];
export default function IssuesContextWrapper(props) {
const [issues, dispatch] = useReducer(issueReducer, issuesList);
return (
<issuesContext.Provider value={issues}>
<dispatchContext.Provider value={dispatch}>{props.children}</dispatchContext.Provider>
</issuesContext.Provider>
);
issue context 만 사용되는 곳은 issue가 바뀌었을 경우 랜더링 됨.
반면 dispatch를 하용하는 경우는 dispatch가 바뀐경우만 랜더링됨.
여기서 dispatch는 바뀔일이 없으니 issue context를 소비하는 컴포넌트만 바뀜.
그리고 value에 {}
를 넣으면 매번 랜더링 될것임. 왜냐면 매번 다른 reference를 가진 {}
가 넘어갈테니. 그러니 value안에 값만 넣기!
만약 context를 여러개 만들필요없이 관련된 value를 {}에 담아서 넘겨주고 싶을떄는 useMemo를 사용하기.
아래처럼
export const ListContext = createContext();
export const ListContextProvider = ({ children }) => {
const [issues, setIssues] = useState({});
const [page, setPage] = useState(1);
const setNextPage = () => setPage(page + 1);
const value = useMemo(
() => ({ issues, page, setNextPage, setIssues }),
[issues, page]
);
return <ListContext.Provider value={value}>{children}</ListContext.Provider>;
};
-- 참고로 value가 바뀔때, provider가 선언된 컴포넌트, 즉 여기선 App컴포넌트는 리랜더링 되지 않으니 참고하자.
끝!
출쳐: https://reactjs.org/docs/context.html#gatsby-focus-wrapper