이 글은 <처음 배우는 리액트 네이티브 (김범준, 한빛미디어)>를 읽고 이해한 바를 바탕으로 작성되었습니다.
+ 참고) react docs - context
✔️ 예시 코드
const Context = createContext(defaultValue);
💡Context API가 필요한 이유
일반적인 리액트 네이티브 애플리케이션에서 데이터는 부모 컴포넌트에서 자식 컴포넌트로 전달된다. (위에서 아래로 top-down 방식)
예를 들어, F컴포넌트가 데이터를 필요로 하여 Root 컴포넌트에서 F컴포넌트까지 데이터를 전달해야한다면,
Root → A → B → F
이런 경로로 전달되어야 할 것이다.
이런 방법으로 상태를 관리하면, 관리하는 상태가 추가되거나 변경될 경우 과정에 속한 모든 컴포넌트를 찾아서 수정해야 한다.
하지만 Context API를 이용하면 Root 컴포넌트(App 컴포넌트)에서 시작해서 중간 과정에 있는 컴포넌트들을 거치지 않고, 한번에 원하는 데이터를 받아와서 사용할 수 있다.
즉, Context는 컴포넌트 트리의 모든 레벨을 prop으로 거치지 않아도 원하는 데이터를 컴포넌트들에 공유할 수 있게 해준다.
context라는 상자안에 다른 컴포넌트한테도 전달하고 싶은 데이터를 넣어놓으면 위에서 아래로 데이터를 보내지 않아도 바로 context로 접근하여 데이터를 사용할 수 있다.
context에 연결된 컴포넌트의 모든 하위 컴포넌트는 context에 직접적으로 접근 가능하고, 만약 최상위 컴포넌트를 context에 연결해놓으면 모든 컴포넌트들이 다이렉트로 context에서 데이터에 접근하는것이 가능하다!
++ 이 블로그를 참고하였더니 너무 어려웠던 개념이 조금은 윤곽이 잡혔다! 참고해서 설명을 해보자면,
원래는 이렇게 App에서부터 Button 컴포넌트로 색깔데이터가 차례대로 props로 전달되었는데, contextAPI를 사용하면 다음과 같이 작성될 수 있다.
import React from 'react';
export const BtnColorContext = React.createContext('red');
((Button component의 상위 컴포넌트인 App component에서 Context의 Provider 컴포넌트에서 'yellow'라는 값을 전달해주고 있다.)
App component 안에서도 Header component에 props를 전달해주지 않아도 된다.
여기가 포인트! 이렇게 중간에 buttoncolor에 대한 데이터가 필요없는 중간 컴포넌트인 Header component에서
context를 사용하지 않으면 필요없어도 props를 전달해야하지만,
context를 사용하면 props를 전달할 필요가 없다!
((상위 컴포넌트인 App 컴포넌트에서 Provider 컴포넌트가 전달한 'yellow'라는 값을 받는다.))
💡Context API 사용하기
const Context = createContext(defaultValue);
✔️예시) UserContext
import { createContext } from 'react';
const UserContext = createContext({ name: 'Eunbin Kwon' });
export default UserContext;
/* src/contexts/User.js */
Context 오브젝트는 입력된 기본값 외에도 Consumer 컴포넌트와 Provider 컴포넌트를 갖고 있다.
💡Consumer
Consumer 컴포넌트는 Context의 내용을 읽고 사용하는 컴포넌트이다.
Consumer 컴포넌트는 상위 컴포넌트 중 가장 가까운 곳에 있는 Provider 컴포넌트가 전달하는 데이터를 이용한다. 만약 상위 컴포넌트 중 Provider 컴포넌트가 없다면 createContext 함수의 파라미터로 전달된 초기값을 사용한다.
import { createContext } from 'react';
const UserContext = createContext({ name: 'Eunbin Kwon' });
export default UserContext;
/* src/contexts/User.js */
UserContex를 만들었고, default값으로 name을 'Eunbin Kwon'으로 작성하였다.
import React from 'react';
import styled from 'styled-components/native';
import UserContext from '../contexts/User';
const StyledText = styled.Text`
font-size:24px;
margin:10px;
`;
const User = () => {
return (
<UserContext.Consumer>
{value => <StyledText>Name: {value.name}</StyledText>}
</UserContext.Consumer>
)
}
export default User;
/* src/components/User.js */
Context component는 Consumer component는 상위 component의 Provider에서 전달하는 값을 가져오지만, 지금 여기서는 상위 컴포넌트에 Provider component가 없기 때문에 Context 컴포넌트를 처음 만들 때, 파라미터로 전달해줬었던 default값('Eunbin Kwon')을 가져온다.
import React from 'react';
import styled from 'styled-components/native';
import User from './components/User'
const Container = styled.View`
flex: 1;
background-color: #ffffff;
justify-content: center;
align-items: center;
`;
const App = () => {
return (
<Container>
<User />
</Container>
);
};
export default App
/* src/App.js */
App 컴포넌트에 User 컴포넌트를 넣어주었다.
💡Provider
Context의 Provider 컴포넌트는 하위 컴포넌트에 Context의 변화를 알리는 역할을 한다.
Provider 컴포넌트는 value를 받아서 모든 하위 컴포넌트에 전달하고, 하위 컴포넌트는 Provider 컴포넌트의 vlaue가 변경될 때마다 다시 렌더링된다.
import React from 'react';
import styled from 'styled-components/native';
import User from './components/User'
import UserContext from './contexts/User';
const Container = styled.View`
flex: 1;
background-color: #ffffff;
justify-content: center;
align-items: center;
`;
const App = () => {
return (
<UserContext.Provider value={{name: 'Eunbin'}}>
<Container>
<User />
</Container>
</UserContext.Provider>
);
};
export default App
/* src/App.js */
앞에서는 User 컴포넌트의 상위 컴포넌트(App 컴포넌트)에서 Context의 Provider에서 값('Eunbin')을 전달
Provider 컴포넌트로부터 value를 전달받는 하위 컴포넌트의 수에는 제한이 없다.
하지만 Consumer 컴포넌트는 가장 가까운 상위 컴포넌트에서 Provider 컴포넌트의 value를 전달받는다는 것을 유의하자!
예를 들어, 바로 User 컴포넌트에서 Provider를 제공하며, 한 depth 건너뛴, app 컴포넌트에서 Provider를 제공하더라도 더 가까운 User 컴포넌트의 Provider의 값을 가져오게 된다.
import React from 'react';
import styled from 'styled-components/native';
import UserContext from '../contexts/User';
const StyledText = styled.Text`
font-size:24px;
margin:10px;
`;
const User = () => {
return (
<UserContext.Provider value={{name: 'React Navtive'}}>
<UserContext.Consumer>
{value => <StyledText>Name: {value.name}</StyledText>}
</UserContext.Consumer>
</UserContext.Provider>
)
}
export default User;
/* src/components/User.js */
💡Context 수정
Context의 값을 수정해서 Context를 사용하는 컴포넌트에 변경된 내용을 반영하는 방법!
import React, { createContext, useState } from 'react';
const UserContext = createContext({
user: {name: ''},
dispatch: () => {},
});
const UserProvider = ({ children }) => {
const [name, setName] = useState('Eunbin Kwon');
const value = {user: {name}, dispatch: setName};
return <UserContext.Provider value={value}>{children}</UserContext.Provider>
};
const UserConsumer = UserContext.Consumer;
export {UserProvider, UserConsumer};
export default UserContext;
/* src/contexts/User.js */
UserProvider 컴포넌트와 UserConsumer 컴포넌트를 새롭게 생성하였다.
- UserProvider에서는 value에 전역적으로 관리할 상태변수와 세터함수를 함께 전달할 수 있게 하였다. (이전까지는 value로 name만 전달했다면, 여기서는 name을 변경할 수 있는 setName도 함께 전달한다.)
- UserConsumer는 지금까지 써오던 UserContext.Consumer와 동일하다.
import React from 'react';
import styled from 'styled-components/native';
import User from './components/User'
import UserContext from './contexts/User';
import { UserProvider } from './contexts/User';
import Input from './components/Input';
const Container = styled.View`
flex: 1;
background-color: #ffffff;
justify-content: center;
align-items: center;
`;
const App = () => {
return (
<UserProvider>
<Container>
<User />
<Input />
</Container>
</UserProvider>
);
};
export default App
/* src/App.js */
UserProvider 컴포넌트는 내부에서 Provider 컴포넌트를 이용해서 value를 전달하므로 따로 value를 전달하지 않아도 된다. (UserProvider 컴포넌트 자체가
<UserContext.Provider value = {value}>{childern}</UserContext.Provider>
를 return 한다.)
import React from 'react';
import styled from 'styled-components/native';
import UserContext from '../contexts/User';
import { UserConsumer } from '../contexts/User';
const StyledText = styled.Text`
font-size:24px;
margin:10px;
`;
const User = () => {
return(
<UserConsumer>
{({user}) => <StyledText>Name: {user.name}</StyledText>}
</UserConsumer>
);
}
export default User;
/* src/components/User.js */
원래 <UserContext.Consumer> 컴포넌트를 사용하던 것을 <UserConsumer>로 대신 작성하였다. 동일하지만, Provider 컴포넌트에서 전달되는 value의 형태가 바뀌었기 때문에 함수 형태도 전달되는 값에 맞게 수정하였다.
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;
/* src/components/Input.js */
만들어놓은 세터함수를 이용해서 textInput 컴포넌트에 입력되는 값으로 context의 값을 변경해보자.
StyledInput의 value 속성에 name을 넣어서 input되는 값을 name에 넣었다.
그리고 onChangeText에서는 바뀐 text를 setName에 넘겨서 name의 상태값을 바꿀 수 있게 하였다.
onSubmitEditing속성으로는 키보드에서 확인버튼을 누르면 dispatch함수를 이용해 TextInput 컴포넌트에 입력된 값으로 Context의 값을 변경하도록 작성하였다.
💡useContext
Hooks 중에 useContext라는 hook을 사용하여 Consumer 컴포넌트를 사용하지 않고 Context의 내용을 사용할 수 있다.
useContext 함수는 Consumer 컴포넌트의 자식 함수로 전달되던 값과 동일한 데이터를 반환하여 주기 때문이다.
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/User.js */
import React, { createContext, useState } from 'react';
const UserContext = createContext({
user: {name: ''},
dispatch: () => {},
});
const UserProvider = ({ children }) => {
const [name, setName] = useState('Eunbin Kwon');
const value = {user: {name}, dispatch: setName};
return <UserContext.Provider value={value}>{children}</UserContext.Provider>
};
const UserConsumer = UserContext.Consumer;
export {UserProvider, UserConsumer};
export default UserContext;
/* src/contexts/User.js */
이렇게 useContext 함수를 이용하면 코드가 훠어얼씬 깔끔해진다! (결과는 동일)
'JavaScript > React-Native' 카테고리의 다른 글
[react-native] Tab Navigation (0) | 2021.08.26 |
---|---|
[react-native] Stack Navigation (3) | 2021.08.25 |
[react-native] Hooks(4) useMemo (0) | 2021.08.22 |
[react-native] Hooks(3) useRef (0) | 2021.08.22 |
[react-native] Hooks(2) useEffect (0) | 2021.08.21 |
댓글