솜이의 데브로그

[React Native] Context API 본문

dev/React native

[React Native] Context API

somsoming 2021. 11. 14. 17:06

Reference : 처음배우는 리액트 네이티브 (김범준)

 

 

7. Context API

 

  • 데이터를 전역적으로 관리하고 사용할 수 있도록 하는 기능.
  • 스타일드 컴포넌트 역시 Context API를 이용해 theme을 애플리케이션 전체에서 사용할 수 있도록 했다.

 

 

(1) 전역 상태 관리

  • 데이터를 사용하는 컴포넌트가 많다면 최상위 컴포넌트인 App 컴포넌트에서 상태를 관리하여 하위 컴포넌트 어디서 필요로 하든 전달 할 수 있도록 해야한다.
  • props를 이용하여 데이터를 전달하는 경우, 관리하는 상태가 추가되거나 변경될 경우 과정에 속한 모든 컴포넌트를 찾아서 수정해야한다는 단점이 있다. 따라서 개발 뿐만 아니라 유지보수에서도 매우 불편하다.
  • Context API를 이용하면 Context를 생성해 필요한 컴포넌트에서 데이터를 바로 받아올 수 있다.

 

 

(2) Context API

  • createContext 함수를 통해 파라미터에 생성되는 Context의 기본값을 지정할 수 있다.

 

Consumer
  • 상위컴포넌트 중 가장 가까운 곳에 있는 Provider 컴포넌트가 전달하는 데이터를 이용한다.
  • 만약 상위 컴포넌트 중 Provider 컴포넌트가 없다면 createContext 함수의 파라미터로 전달된 기본값을 사용한다.
const StyledText = styled.Text`
    font-size: 24px;
    margin: 10px;
`;

const User = () => {
    return(
        <UserContext.Consumer>
            {value => <StyledText>Name: {value.name}</StyledText>}
        </UserContext.Consumer>
    );
};

 

 

Provider
  • Provider 컴포넌트는 하위 컴포넌트에 Context의 변화를 알린다.
  • value를 받아서 모든 하위 컴포넌트에 전달하고, 하위 컴포넌트는 Provider 컴포넌트의 value가 변경될 때마다 다시 렌더링된다.
  • 컴포넌트를 Provider로 감싼 경우 Cosumer 컴포넌트는 더이상 기본값을 사용하지 않고 상위 컴포넌트인 Provider 컴포넌트가 전달하는 데이터를 사용한다. 따라서 Provide 컴포넌트의 value에 값을 전달한다.
  • value를 전달받는 하위 컴포넌트의 수에는 제한이 없지만, Consumer 컴포넌트는 가장 가까운 Provider 컴포넌트에서 값을 받으므로, 자식 컴포넌트 중 Provider 컴포넌트가 있다면 그 이후의 자식 컴포넌트는 중간에 있는 Provider 컴포넌트가 전달하는 값을 사용한다.

★Provider 컴포넌트를 사용할 때 반드시 value를 지정해야하며, Consumer 컴포넌트는 가장 가까운 Provider 컴포넌트가 전달하는 값을 이용한다.

 

 

 

Context 수정하기
  • Provider 컴포넌트의 value에 전역적으로 관리할 상태 변수와 상태를 변경하는 함수를 함께 전달하는 UserProvider 컴포넌트를 생성한다.
  • Consumer 컴포넌트의 자식함수의 파라미터로 데이터를 변경할 수 있는 함수도 함께 전달한다.
  • 상위 컴포넌트에 Provider 컴포넌트가 없더라도 동작에 문제가 생기지 않도록 형태를 동일하게 맞춘다.

 

src/components/Input.js

  • useState 함수를 이용해 name 상태 변수를 생성하고, TextInput 컴포넌트에 값이 변경될 때마다 name에 반영되도록한다.
  • 자식함수에 전달되는 value에는 Context의 값을 변경할 수 있는 dispatch 함수가 함께 전달된다.
import React, {useState} from 'react';
import styled from 'styled-components/native';
import { UserConsumer  } from '../contexts/User'

const StyledInput = styled.TextInput`
    border: 1px solid #606060;
    width: 250px;
    padding: 10px 15px;
    margin: 10px;
    font-size: 24px;
`;

const Input = () => {
    const [name, setName] = useState('');
    return(
        <UserConsumer>
            {({ dispatch })=> {
                return(
                    <StyledInput
                        value={name}
                        onChangeText={text => setName(text)}
                        onSubmitEditing={()=> {
                            dispatch(name);
                            setName('');
                        }}
                        placeholder="Enter a name..."
                        autoCapitalize="none"
                        autoCorrect={false}
                        returnKeyType="done"
                    />
                );
            }}
        </UserConsumer>
    );
};

export default Input;

 

 

(3) useContext

  • 컴포넌트의 자식함수로 전달되던 값과 동일한 데이터를 반환하므로 Consumer 컴포넌트를 사용하지 않고 Context의 내용을 사용할 수 있게 한다.
  • Consumer 컴포넌트를 사용할 때는 자식으로 반드시 리액트 컴포넌트를 반환하는 함수를 넣어야하지만, userContext를 이용하면 사용법이 간편해진다.

src/components/User.js

import React, { useContext } from "react";;
import styled from "styled-components/native";
import UserContext from "../contexts/User";

const StyledText = styled.Text`
    font-size: 24px;
    margin: 10px;
`;

const User = () => {
    const { user } = useContext(UserContext);
    return <StyledText>Name: {user.name}</StyledText>;
};

export default User;

 

src/components/Input.js

import React, {useState, useContext } from 'react';
import styled from 'styled-components/native';
import UserContext from '../contexts/User'

const StyledInput = styled.TextInput`
    border: 1px solid #606060;
    width: 250px;
    padding: 10px 15px;
    margin: 10px;
    font-size: 24px;
`;

const Input = () => {
    const [name, setName] = useState('');
    const {dispatch} = useContext(UserContext);

    return(
        <StyledInput
            value={name}
            onChangeText={text => setName(text)}
            onSubmitEditing={()=> {
                dispatch(name);
                setName('');
            }}
            placeholder="Enter a name..."
            autoCapitalize="none"
            autoCorrect={false}
            returnKeyType="done"
        />
    );
};

export default Input;

코드가 더 깔끔해졌음을 확인할 수 있다.

 

 

 

결론

  • props를 이용해 상태를 전달하는 것 보다 편하고 간결하게 상태를 공유하여 사용할 수 있다.
  • 하지만 상태를 전달해야하는 관계의 구조가 간단하다면 굳이 Context API를 사용할 필요가 없다.
  • 프로젝트의 구조가 복잡하고 많은 컴포넌트가 이용하는 데이터를 전역적으로 관리할 때 사용하는 것이 좋다.