Post

🌌 React μž…λ¬Έ XV - Context

🌌 React μž…λ¬Έ XV - Context

πŸ“˜ γ€Žμ†Œν”Œμ˜ 처음 λ§Œλ‚œ λ¦¬μ•‘νŠΈγ€λ₯Ό 읽고 μ •λ¦¬ν•œ κΈ€μž…λ‹ˆλ‹€.

Context

  • ContextλŠ” 기쑴의 propsλ₯Ό 톡해 React Componentλ“€ μ‚¬μ΄μ—μ„œ 데이터λ₯Ό μ „λ‹¬ν•˜λŠ” 방식 λŒ€μ‹  Component 트리λ₯Ό 톡해 κ³§λ°”λ‘œ Component에 μ „λ‹¬ν•˜λŠ” μƒˆλ‘œμš΄ 방식을 μ œκ³΅ν•œλ‹€.
  • κΈ°μ‘΄ λ°©μ‹μ˜ 경우 ν•˜μœ„ Component둜 데이터λ₯Ό μ „λ‹¬ν•˜λ €λ©΄ 트리λ₯Ό 타고 λͺ‡ λ²ˆμ„ λ‚΄λ €κ°€λ©° 전달을 ν•΄μ•Όν•˜μ§€λ§Œ, Contextλ₯Ό μ‚¬μš©ν•œλ‹€λ©΄ 데이터λ₯Ό ν•„μš”λ‘œ ν•˜λŠ” Component에 κ³§λ°”λ‘œ 데이터λ₯Ό 전달할 수 μžˆλ‹€.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function App(props) {
	return <Toolbar theme="dark"/>;
}

function Toolbar(props) {
    return (
    	<div>
        	<ThemeButton theme={props.theme}/>
        </div>
    );
}

function ThemeButton(props) {
	return <Button theme={props.theme}/>;
}
  • Contextλ₯Ό μ μš©ν•˜μ§€ μ•Šμ€ μ½”λ“œμ΄λ‹€.
  • μ΄λ ‡κ²Œ μ²˜λ¦¬ν•˜λ©΄ 반볡된 μ½”λ“œλ₯Ό 계속 μž‘μ„±ν•΄μ£Όμ–΄μ•Ό ν•˜κΈ° λ•Œλ¬Έμ— λΉ„νš¨μœ¨μ μ΄κ³  직관적이지도 μ•Šλ‹€.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const ThemeContext = React.createContext('light');

function App(props) {
    return (
    	<ThemeContext.Provider value="dark">
        	<Toolbar/>
        </ThemeContext.Provider>
    );
}

function Toolbar(props) {
    return (
    	<div>
        	<ThemeButton />
        </div>
    );
}

function ThemeButton(props) {
    return (
    	<ThemeContext.Consumer>
        	{value => <Button theme={value}/>}
        </ThemeContext.Consumer>
    );
}

μƒμœ„ Contextλ₯Ό Provider둜 감싸면 λͺ¨λ“  ν•˜μœ„ Contextκ°€ μ–Όλ§ˆλ‚˜ 깊이 μœ„μΉ˜ν•΄ μžˆλŠ”μ§€ 관계없이 Context의 데이터λ₯Ό 읽을 수 μžˆλ‹€.

Context μ‚¬μš© μ‹œ μ£Όμ˜ν•  점

  • 무쑰건 Contextλ₯Ό μ‚¬μš©ν•˜λŠ” 것은 μ˜³μ§€ μ•Šλ‹€.
  • Component와 Contextκ°€ μ—°λ™λ˜λ©΄ μž¬μ‚¬μš©μ„±μ΄ λ–¨μ–΄μ§€κΈ° λ•Œλ¬Έμ΄λ‹€.
  • λ‹€λ₯Έ 레벨의 λ§Žμ€ Componentκ°€ 데이터λ₯Ό ν•„μš”λ‘œ ν•˜λŠ” κ²½μš°κ°€ μ•„λ‹ˆλΌλ©΄ 기쑴에 μ‚¬μš©ν•˜λ˜ λ°©μ‹λŒ€λ‘œ propsλ₯Ό 톡해 데이터λ₯Ό μ „λ‹¬ν•˜λŠ” Component Composition방식이 더 μ ν•©ν•˜λ‹€.

Context λŒ€μ‹  Element λ³€μˆ˜ ν˜•νƒœ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Page(props) {
    const user = props.user;
    
    const userLink = (
    	<Link href={user.peralink}>
        	<Avatar user={user} size={props.avatarSize}/>
        </Link>
    );
    
    return <PageLayout userLink={userLink}/>
}

// NavigationBar μ»΄ν¬λ„ŒνŠΈλ₯Ό λ Œλ”λ§
<PageLayout userLink={...}/>

// props둜 전달받은 userLink μ—˜λ¦¬λ¨ΌνŠΈλ₯Ό λ°˜ν™˜
<NavigationBar userLink={...}/>
  • μ΅œμƒμœ„ Componentμ—μ„œ Avatar Componentλ₯Ό λ³€μˆ˜μ— μ €μž₯ν•˜μ—¬ 직접 λ„˜κ²¨μ£Όλ©΄ 쀑간 Component듀은 user와 avatarSize에 λŒ€ν•΄ λͺ°λΌλ„ λœλ‹€.
  • μ΄λŠ” μ΅œμƒμœ„ Component에 μ’€ 더 λ§Žμ€ κΆŒν•œμ„ λΆ€μ—¬ν•΄μ€€λ‹€.
  • κ·ΈλŸ¬λ‚˜ 데이터가 λ§Žμ•„μ§ˆμˆ˜λ‘ μƒμœ„ Component에 μ½”λ“œκ°€ λͺ°λ¦¬κΈ° λ•Œλ¬Έμ— λ³΅μž‘ν•΄μ§€κ³ , ν•˜μœ„ ComponentλŠ” λ„ˆλ¬΄ μœ μ—°ν•΄μ§„λ‹€λŠ” 단점이 μžˆλ‹€.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Page(props) {
    const user = props.user;
	 
    const topBar = (
    	<NavigationBar>
        	<Link href={user.peralink}>
            	<Avatar user={user} size={props.avatarSize}/>
        	</Link>
    	</NavigationBar>
    );
    
    const content = <Feed user={user}>
    
    return (
    	<PageLayout
        	topBar={topBar}
            content={content}
        />
    );
}

이 방식은 ν•˜μœ„ Component의 μ˜μ‘΄μ„±μ„ μƒμœ„ Component와 뢄리할 ν•„μš”κ°€ μžˆλŠ” λŒ€λΆ€λΆ„μ˜ κ²½μš°μ— μ ν•©ν•œ 방법이닀.

Context API μ’…λ₯˜

βœ… React.createContext

1
const MyContext = React.createContext(κΈ°λ³Έκ°’);
  • Reactμ—μ„œ λ Œλ”λ§μ΄ 일어날 λ•Œ Context 객체λ₯Ό κ΅¬λ…ν•˜λŠ” ν•˜μœ„ Componentκ°€ λ‚˜μ˜€λ©΄ ν˜„μž¬ Context 값을 κ°€μž₯ κ°€κΉŒμ΄μ— μžˆλŠ” μƒμœ„ 레벨의 Providerλ‘œλΆ€ν„° λ°›μ•„μ˜€κ²Œ λœλ‹€.
  • μƒμœ„ λ ˆλ²¨μ— λ§€μΉ­λ˜λŠ” Providerκ°€ μ—†λ‹€λ©΄ 이 κ²½μš°μ—λ§Œ κΈ°λ³Έ 값이 μ μš©λœλ‹€.
  • κΈ°λ³Έ 값에 undefinedλ₯Ό λ„£μœΌλ©΄ κΈ°λ³Έ 값이 μ‚¬μš©λ˜μ§€ μ•ŠλŠ”λ‹€.

βœ… Context.Provider

1
<MyContext.Provider value={/* νŠΉμ •κ°’ */}/>
  • Contextλ₯Ό λ§Œλ“€μ—ˆλ‹€λ©΄ ν•˜μœ„ Component듀이 ν•΄λ‹Ή Context의 데이터λ₯Ό 받을 수 μžˆλ„λ‘ μ„€μ •ν•΄ μ€˜μ•Ό ν•œλ‹€.
  • λͺ¨λ“  ComponentλŠ” ProviderλΌλŠ” React Componentλ₯Ό κ°–κ³  μžˆλ‹€.
  • Context.Provider둜 μƒμœ„ Componentλ₯Ό 감싸면 λͺ¨λ“  ν•˜μœ„ Component듀이 ν•΄λ‹Ή Context 데이터에 μ ‘κ·Όν•  수 있게 λ˜λŠ”λ°, μ΄λŸ¬ν•œ ν•˜μœ„ Component듀을 Consumer Component라고 λΆ€λ₯Έλ‹€.
  • λͺ¨λ“  Consumer Component은 Provider의 value prop이 λ°”λ€” λ•Œλ§ˆλ‹€ λ Œλ”λ§λœλ‹€.
  • 값이 λ³€κ²½λ˜μ—ˆμ„ λ•Œ μƒμœ„ Componentκ°€ Update λŒ€μƒμ΄ μ•„λ‹ˆλ”λΌλ„ ν•˜μœ„ Componentκ°€ Contextλ₯Ό μ‚¬μš©ν•œλ‹€λ©΄ ν•˜μœ„ Componentμ—μ„œλŠ” Updateκ°€ μΌμ–΄λ‚œλ‹€.
  • κ°’μ˜ λ³€ν™”λ₯Ό νŒλ‹¨ν•˜λŠ” 기쀀은 Object.is()λΌλŠ” ν•¨μˆ˜μ™€ 같은 λ°©μ‹μœΌλ‘œ νŒλ‹¨ν•œλ‹€.

βœ… Class.contextType

1
MyClass.contextType = MyContext;
  • Provider ν•˜μœ„μ— μžˆλŠ” Class Componentμ—μ„œ Context의 데이터에 μ ‘κ·Όν•˜κΈ° μœ„ν•΄ μ‚¬μš©ν•˜λŠ” 것이닀.
  • Class ComponentλŠ” ν˜„μž¬ 거의 μ‚¬μš©ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— 참고만 ν•˜λ©΄ λœλ‹€.
  • 상기 μ½”λ“œμ™€ 같이 μž‘μ„±ν•˜λ©΄ MyClass ComponentλŠ” MyContext의 데이터에 μ ‘κ·Όν•  수 있게 λœλ‹€.
  • 이 APIλŠ” 단 ν•˜λ‚˜μ˜ Contextλ§Œμ„ ꡬ독할 수 μžˆλ‹€.

βœ… Context.Consumer

1
2
3
<MyContext.Consumer>
    {value => /* μ»¨ν…μŠ€νŠΈ 값에 λ”°λΌμ„œ μ»΄ν¬λ„ŒνŠΈ λ Œλ”λ§ */}
</MyContext.Consumer>
  • Context의 데이터λ₯Ό κ΅¬λ…ν•˜λŠ” Component이닀.
  • Component의 μžμ‹μœΌλ‘œ ν•¨μˆ˜κ°€ 올 수 μžˆλŠ”λ° 이것을 function as a child라고 ν•œλ‹€.
  • Context.Consumer둜 감싸면 μžμ‹μœΌλ‘œ λ“€μ–΄κ°„ ν•¨μˆ˜κ°€ ν˜„μž¬ Context의 valueλ₯Ό λ°›μ•„μ„œ React λ…Έλ“œλ‘œ λ°˜ν™˜ν•œλ‹€.

βœ… Context.displayName

1
2
3
4
5
6
7
const MyContext = React.createContext(/* some value*/);

// 개발자 도ꡬ에 MyDisplayName.Provider둜 ν‘œμ‹œλ¨
<MyContext.Provider/>

// 개발자 도ꡬ에 MyDisplayName.Consumer둜 ν‘œμ‹œλ¨
<MyContext.Consumer/>
  • Context κ°μ²΄λŠ” displayNameμ΄λΌλŠ” λ¬Έμžμ—΄ 속성을 κ°€μ§„λ‹€.
  • Chrome의 개발자 λ„κ΅¬μ—μ„œλŠ” 이 displayNameλ₯Ό 같이 ν‘œμ‹œν•΄μ€€λ‹€.

βœ… μ—¬λŸ¬ Context μ‚¬μš©

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
const ThemeContext = React.createContext('light');
const UserContext = React.createContext({
    name: 'Guest',
});

class App extends React.Component {
    render() {
        const { signedInUser, theme } = this.props;
			
        return (
            <ThemeContext.Provider value={theme}>
                <UserContext.Provider value={signedInUser}>
                    <Layout />
                </UserContext.Provider>
            </ThemeContext.Provider>
        );
    }
}

function Layout() {
    return (
        <div>
            <Sidebar />
            <Content />
        </div>
    )
}

function Content() {
    return (
        <ThemeContext.Consumer>
            {theme => (
                <UserContext.Consumer>
                    {user => (
                        <ProfilePage user={user} theme={theme} />
                    )}
                </UserContext.Consumer>
            )}
        </ThemeContext.Consumer>
    );
}
  • μ—¬λŸ¬ 개의 Contextλ₯Ό λ™μ‹œμ— μ‚¬μš©ν•˜λ €λ©΄ Context.Providerλ₯Ό 쀑첩 ν•΄μ„œ μ‚¬μš©ν•˜μ—¬ κ΅¬ν˜„ν•  수 μžˆλ‹€.
  • ν•˜μ§€λ§Œ 두 개 λ˜λŠ” κ·Έ μ΄μƒμ˜ Context 값이 ν•¨κ»˜ μ‚¬μš©λ  경우 λͺ¨λ“  값을 ν•œ λ²ˆμ— μ œκ³΅ν•΄μ£ΌλŠ” λ³„λ„μ˜ render prop Componentλ₯Ό 직접 λ§Œλ“œλŠ” 것을 κ³ λ €ν•˜λŠ” 것이 μ’‹λ‹€.

βœ… useContext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function MyComponent(props) {
    const value = useContext(MyContext);
	    
    return (
    	// ...
    );
}

// μ˜¬λ°”λ₯Έ μ‚¬μš©λ²•
useContext(MyContext);

// 잘λͺ»λœ μ‚¬μš©λ²•
useContext(MyContext.Consumer);
useContext(MyContext.Provider);
  • Functional Componentμ—μ„œλŠ” Contextλ₯Ό μ‚¬μš©ν•˜κΈ° μœ„ν•΄ Componentλ₯Ό 맀번 Consumer Component둜 κ°μ‹Έμ£ΌλŠ” 것보닀 더 쒋은 방법이 μžˆλ‹€.
  • λ°”λ‘œ useContext() Hook이닀.
  • ν•΄λ‹Ή Hook은 React.createContext() ν•¨μˆ˜ 호좜둜 μƒμ„±λœ Context 객체λ₯Ό 인자둜 λ°›μ•„μ„œ ν˜„μž¬ Context의 값을 λ°˜ν™˜ν•œλ‹€.
  • useContext() Hook은 λ‹€λ₯Έ 방식과 λ™μΌν•˜κ²Œ Component 트리 μƒμ—μ„œ κ°€μž₯ κ°€κΉŒμš΄ μƒμœ„ Providerλ‘œλΆ€ν„° Context의 값을 λ°›μ•„μ˜€κ²Œ λœλ‹€.
  • λ§Œμ•½ Context의 값이 λ³€κ²½λ˜λ©΄ λ³€κ²½λœ κ°’κ³Ό ν•¨κ»˜ useContext() Hook을 μ‚¬μš©ν•˜λŠ” Componentκ°€ λ Œλ”λ§ λœλ‹€.
  • κ·ΈλŸ¬λ―€λ‘œ ν•΄λ‹Ή Hook을 μ‚¬μš©ν•˜λŠ” Component의 λ Œλ”λ§μ΄ 무거운 μž‘μ—…μΌ κ²½μš°μ—λŠ” λ³„λ„λ‘œ μ΅œμ ν™” μž‘μ—…μ„ ν•΄μ£Όμ–΄μ•Ό ν•œλ‹€.
  • useContext() Hook을 μ‚¬μš©ν•  λ•Œμ—λŠ” νŒŒλΌλ―Έν„°λ‘œ Context 객체λ₯Ό λ„£μ–΄μ€˜μ•Ό ν•œλ‹€.

μ‹€μŠ΅

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// ThemeContext.jsx
const ThemeContext = React.createContext();

ThemeContext.displayName = "ThemeContext";

export default ThemeContext;

// MainContent.jsx
function MainContent(props) {
	const { theme, toggleTheme } = useContext(ThemeContext);
		
	return (
		<div
			style=>	
			<p>μ•ˆλ…•ν•˜μ„Έμš”, ν…Œλ§ˆ 변경이 κ°€λŠ₯ν•œ μ›Ήμ‚¬μ΄νŠΈ μž…λ‹ˆλ‹€.</p>
			<button onClick={toggleTheme}>ν…Œλ§ˆ λ³€κ²½</button>
		</div>
	);
}

export default MainContent;

// DarkOrLight.jsx
function DarkOrLight(props){
	const [theme, setTheme] = useState("light")
	
	const toggleTheme = useCallback(() => {
		if (theme == "light") {
			setTheme("dark");
		} else if (theme == "dark") {
			setTheme("light");
		}
	},[theme]);
			
	return (
		<ThemeContext.Provider value={/*{theme, toggleTheme}*/}>
			<MainContent />
		</ThemeContext.Provider>
	);
}

export default DarkOrLight;
  • Context APIλ₯Ό μ΄μš©ν•œ μ „μ—­ μƒνƒœ 관리λ₯Ό μˆ˜ν–‰ν•œλ‹€.

회고

  • μ—¬λŸ¬ Componentμ—μ„œ κ³΅ν†΅μœΌλ‘œ μ‚¬μš©ν•˜λŠ” λ°μ΄ν„°λŠ” Context둜 κ΄€λ¦¬ν•˜λ©΄ props drilling 없이 κΉ”λ”ν•˜κ²Œ ν•΄κ²° κ°€λŠ₯ν•˜λ‹€λŠ” 점을 μ•Œκ²Œ λ˜μ—ˆλ‹€.
This post is licensed under CC BY 4.0 by the author.