React Native

[React Native] Context API

joy_lee 2022. 1. 10. 15:04

React를 사용하다보면 여러 컴포넌트를 거쳐서 자료를 전달해야 하거나, 동시에 같은 자료를 사용해야 하는 경우가 생긴다. 최상위 컴포넌트에서 여러 컴포넌트를 거쳐서 자료를 전달하는 방법도 있지만 context API를 사용하면 더 쉽게 관리할 수 있다.

전역으로 상태관리를 더 쉽게 할 수 있다.

 

Context관련 컴포넌트는 크게 두 개로 나눌 수 있다.

Provider Component Context의 변화를 하위 컴포넌트에 알린다
Consumer Component 부모 Component 중 가장 가까운 Provider가 전달하는 데이터를 받아서 이용한다.

 

사용방법

import { createContext } from 'react';

context 사용을 위해 react에서 import한다.

 

const Context = createContext({
	name: "Coffee",
	setName: () => {} // 함수도 전달 가능
});

createContext를 이용해 Context를 만들어준다. createContext를 통해서는 초기설정값을 정할 수 있다.

어떤 값 뿐만 아니라 함수도 전달할 수 있다.

 

1
2
3
4
5
<Context.Provider value={{ name"Latte" }}> // 하위 컴포넌트에 새로운 값 전달가능
    <Context.Consumer>
        {value => console.log(value.name) } // 자식이 함수여야 한다
    </Context.Consumer>
</Context.Provider>
cs

Provider의 value={} 으로 하위 컴포넌트에 값을 전달할 수 있다. 위의 코드를 실행하면 console에 'Latte'라고 출력된다.

대신 초기 설정값과 형태가 같아야한다. (다른 형태를 사용한다면 오류가 뜬다)

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
export default function App() {
    const UserContext = createContext({name"Coffee"})
 
  return (
    <UserContext.Provider value={{name"Latte"}}>
        <UserContext.Consumer>
            {value => {
                return <StyledText>{value?.name}</StyledText> // Latte
            }}
        </UserContext.Consumer>
 
        <UserContext.Provider value={{name"Coffee 2"}}>
            <UserContext.Consumer>
                {value => {
                    return <StyledText>{value?.name}</StyledText> // Coffee 2
                }}
            </UserContext.Consumer>
        </UserContext.Provider>
    </UserContext.Provider>
  );
};
 
cs

첫 번째 Provider에서는 {name: "Latte"}를 전달해 하위 Consumer에 전달한다.

첫 번쨰 Provider안의 두 번째 Provider에서는 {name: "Coffee 2"} 를 전달하는데, 두 번째 Provider내부의 Consumer는 가장 가까운 상위 Provider의 value를 가져오기 때문에 Coffee 2를 화면에 출력한다.

 

1
2
3
4
// Provider없는 Consumer는 Context 생성시 처음 설정한 값을 전달받는다
<Context.Consumer>
    {value => console.log(value.name)} // 
</Context.Consumer>
cs

이 경우에는 console에 기본값인 'Coffee'가 출력된다.

 

ThemeProvider를 생각하면 Context를 쉽게 이해할 수 있다.

 

ThemeProvider처럼 쉽게 Context를 사용하려면 어떻게 하면 가능할까?

직접 만드는 Context Component

기존 Context사용시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import React, { createContext } from "react";
 
export default function App() {
    const UserContext = createContext({name"Coffee"})
 
  return (
    <UserContext.Provider value={{name"Latte"}}>
        <UserContext.Consumer>
            {value => {
                return <StyledText>{value?.name}</StyledText>
            }}
        </UserContext.Consumer>
    </UserContext.Provider>
  );
};
 
cs

createContext를 import해서 사용해야 한다.

 

직접 Context를 만들어보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// ./contexts/User.js
 
import React, { createContext, useState } from "react";
 
const UserContext = createContext({
  name"",
  setName: () => {},
});
 
const UserProvider = ({ children }) => {
  const [name, setName] = useState("");
  const value = { name, setName };
  return <UserContext.Provider value={value}>{children}</UserContext.Provider>;
};
 
const UserConsumer = UserContext.Consumer;
 
export { UserProvider, UserConsumer };
export default UserContext;
 
cs

위 파일은 createContext를 통해 userContext를 만든다.

UserProvider에서는 name과 setName을 Provider에 value로 제공하고 UserProvider 내부의 children을 그대로 출력하도록 한다.

UserConsumer은 따로 수정해 줄 부분이 없으므로 그대로 사용한다.

UserContext를 export하면서 UserProvider와 UserConsumer도 사용하기 쉽도록 따로 export한다.

 

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
// ./components/User.js
 
import React, { useState } from "react";
import styled from "styled-components/native";
import { UserConsumer } from "../contexts/User";
 
const StyledText = styled.Text`
// 생략
`;
const StyledInput = styled.TextInput`
// 생략
`;
 
const User = () => {
  const [text, setText] = useState("");
  return (
    <>
      <UserConsumer>
        {({ name }) => <StyledText>Name: {name}</StyledText>}
        {/* Context 통해 자동으로 name 받음 */}
      </UserConsumer>
      <UserConsumer>
        {({ setName }) => (
          <StyledInput
            value={text}
            onChangeText={setText}
            onSubmitEditing={() => {
              setName(text);
              setText("");
            }}
          />
        )}
      </UserConsumer>
    </>
  );
};
 
export default User;
 
cs

User.js에서는 UserConsumer 를 가져와서 사용한다. 위 파일에는 Provider를 사용하지 않는다.

App.js에서 UserProvider와 User를 따로 import해서 사용하면 되기 때문이다.

Context의 name을 화면에 출력하고, name을 변경하는 Input을 만든다.

 

name이나 setName을 User에서 전달받는다고 작성하지 않아도 Context를 통해 자동으로 받아오기 때문에 정의할 필요가 없다.(ThemeProvider에서 Theme을 props형태로 받아오지 않아도 되는 것과 같다)

 

이렇게 Context와 Consumer를 사용한 Component를 따로 만들어서 사용하면 훨씬 깔끔하게 작성할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import React, { createContext } from "react";
import styled from "styled-components/native";
import { UserProvider } from "./contexts/User";
import User from "./components/User";
 
const Container = styled.View`
 // 생략
`;
 
export default function App() {
  return (
    <UserProvider>
      <Container>
        <User />
      </Container>
    </UserProvider>
  );
}
 
cs

./contexts/User에서는 UserProvider만 가져오고, ./components/User에서 User를 가져온다.

App에서는 UserProvider안에 User 가 자리하게 되므로 Context를 사용할 수 있다.