정의
-
React 컴포넌트 트리 안에서 데이터를 쉽게 공유하는 방법중 하나이다.
-
컴포넌트가 깊어지면 props를 내리는데 리소스 증가에 따라 사용되는 방법 중 하나
// context 사용전 class App extends React.Component { render() { return <Toolbar theme="dark" /> } } function Toolbar(props) { return ( <div> <ThemedButton theme={props.theme} /> </div> ) } class ThemedButton extends React.Component { render() { return <Button theme={this.props.theme} /> } } // context 사용 후 const ThemeContext = React.createContext('light') class App extends React.Component { render() { return ( <ThemeContext.Provider value="dark"> <Toolbar /> </ThemeContext.Provider> ) } } // props 과정이 사라짐 function Toolbar() { return ( <div> <ThemedButton /> </div> ) } // 컨텍스트를 활용하여 value 접근 class ThemedButton extends React.Component { static contextType = ThemeContext render() { return <Button theme={this.context} /> } }
Context를 사용하기 전에 고려할 것
- Context에 의존성이 높아지므로 재사용이 어려워짐.
- context보다는 컴포넌트 합성으로 문제점 해결(props.children)
- IOC(제어의 역전)를 이용하여 내리는 props 줄일 수 있으므로, props를 여러개 내리지 않아도 된다.
- 장점 : props는 수는 줄고 최상위 컴포넌트의 제어력이 커지기 떄문에 클린코드 작성 가능
- 단점 : 복잡한 로직이 상위에 있기 때문에, 상위컴포넌트는 복잡성이 증대되고, 하위 컴포넌트는 필요이상으로 유연해져야 한다.
function Page(props) { const user = props.user; const userLink = ( <Link href={user.permalink}> <Avatar user={user} size={props.avatarSize} /> </Link> ); return <PageLayout userLink={userLink} />; } // 이제 이렇게 쓸 수 있습니다. <Page user={user} avatarSize={avatarSize} /> // ... 그 아래에 ... <PageLayout userLink={...} /> // ... 그 아래에 ... <NavigationBar userLink={...} /> // ... 그 아래에 ... {props.userLink}
- Context가 변경이되면, 모든 하위 컴포넌트가 리렌더링이 된다.
- 한번 변경이 되면 향후 변경되지 않은 데이터를 Context로 활용한다(테마, 데이터캐쉬)
API
React.createContext
const MyContext = React.createContext(dafaultValue) export const ThemeContext = React.createContext({ theme: themes.dark, toggleTheme: () => {}, })
- Context 객체 생성하며, dafaultValue는 값이 없을때 사용되며, 객체( = 값)에 값과 메소드를 넣어 사용할수도 있다.
Context.Privider
<MyContext.Provider value={}>
- Context를 통해 전달될 값을 넣어주며 Provider 하위 컴포넌트에게 value가 변경될때마다 전파한다.
- value가 변하면 props가 변하기 때문에 불필요한 렌더링이 발생한다(shouldComponentUpdate 미적용)
- Object.is를 통해서 Context의 값 변경 여부를 체크한다(true/false)
Class.contextType
class MyClass extends React.Component { componentDidMount() { let value = this.context } componentDidUpdate() { let value = this.context } componentWillUnmount() { let value = this.context } render() { let value = this.context } } MyClass.contextType = MyContext
-
contextType을 통해 class내에서 사용하는 Context를 지정할 수 있다
-
모든 컴포넌트의 생명주기 메서드에서 사용가능하다.
class MyClass extends React.Component { static contextType = MyContext render() { let value = this.context /* context 값을 이용한 렌더링 */ } }
-
public class field 문법으로 contextType을 지정할 수 도 있다.
Context.Consumer
<MyContext.Consumer> {value => /* context 값을 이용한 렌더링 */} </MyContext.Consumer>
- Provider의 하위 트리에서 사용할 수 있는 Cosumer 컴포넌트입니다.
- Consumer 컴포넌트의 children은 함수형으로 작성되면 props는 해당 Context의 value값이다.
- value값이 없다면, defaultValue가 적용이 된다.
Context.displayName
const MyContext = React.createContext(/* some value */);
MyContext.displayName = 'MyDisplayName';
<MyContext.Provider> // "MyDisplayName.Provider" in DevTools
<MyContext.Consumer> // "MyDisplayName.Consumer" in DevTools
- 개발자 도구에서 손 쉽게 확인하려고 라벨링을 해 줄 수 있다.
Context의 value값이 reference일때!!
- value 값이 객체이면 매번 새로운 객체가 생성되므로 Provider가 렌더링될때마다 리렌더링 된다
- state를 객체로 생성하고 객체를 가리키고 있는 포인터를 넘기자
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
value: { something: 'something' },
}
}
render() {
return (
<Provider value={this.state.value}>
<Toolbar />
</Provider>
)
}
}
Hook - useContext
- 함수형 컴포넌트에서 Context를 사용할때 사용
- 가장 가까이 있는 Context.Provider의 value를 리턴한다.
- React.memo, shouldComponentUpdate를 사용해도 최적화가 안되며 리렌더링이 된다
const themes = {
light: {
foreground: '#000000',
background: '#eeeeee',
},
dark: {
foreground: '#ffffff',
background: '#222222',
},
}
const ThemeContext = React.createContext(themes.light)
function App() {
return (
<ThemeContext.Provider value={themes.dark}>
<Toolbar />
</ThemeContext.Provider>
)
}
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
)
}
function ThemedButton() {
const theme = useContext(ThemeContext)
return (
<button
style={{ background: theme.background, color: theme.foreground }}
>
I am styled by theme context!
</button>
)
}