이런저런 IT 이야기

ReactNative 화면 전환시 데이터 주고 받기 본문

React Native

ReactNative 화면 전환시 데이터 주고 받기

이런저런 IT 이야기 2020. 6. 20. 11:02
반응형

ReactNative 개발하면서 가장 신경이 쓰이는 것이 화면간 데이터를 주고 받는 부분입니다.

이 부분에 좀더 효과적으로 대응하기 위해서 Context 개념을 적용하여 모든 화면에서 데이터가 공유될수 있도록 구현해보았습니다.

샘플코드는 아래 링크를 참조하세요.

https://github.com/leeyonghe/ReactNativeApp

 

leeyonghe/ReactNativeApp

Contribute to leeyonghe/ReactNativeApp development by creating an account on GitHub.

github.com

 

Context는 React or React Native 컴포넌트 트리 안에서 전역적(global)이라고 볼 수 있는 데이터를 공유할 수 있도록 고안된 방법입니다.

Context를 사용하면 모든 컴포넌트를 일일이 통하지 않고도// 원하는 값을 컴포넌트 트리 깊숙한 곳까지 보낼 수 있습니다. 

정말 편하죠.

 

Github에 구현한 앱 동작영상입니다.

 

코드설명을 하겠습니다. 총 5개 파일만 익히시면 됩니다.

 

./Core/CoreManagement.js

import React, {Component, createContext} from 'react';

const CC = createContext({
  value: 0,
  tag: '',
  SetValue: () => {},
});

export const CoreContext = CC;

export const CoreConsumer = CC.Consumer;

export class CoreProvider extends Component {
  SetValue = (value) => {
    this.state.value = value;
  };

  state = {
    value: 0,
    tag: '>>>>>>>>>>>>>>>>>>>>>>>>>>>>',
    SetValue: this.SetValue
  };

  render() {
    return <CC.Provider value={this.state}>{this.props.children}</CC.Provider>;
  }
}

 

createContext 라는 함수로 CoreContext를 만들고, 이 함수를 호출하고 Consumer 라는 컴포넌트가 반환됩니다.

CoreProvider는 별도로 Class를 선언하여 부모 Layer에서 사용하고 CoreConsumer

그 하위 Layer 에서 사용합니다.

제가 여기서 시연하고 싶은 부분은 value값을 화면 2개가 왔다갔다 하면서 값을 공유하면서 +1을 하는 겁니다.

그렇게 하기 위해서 CoreProvider에서 변수, 함수를 공유하고 있다는 겁니다.

변수 : value
변수 : tag
함수 : SetValue: () => {}

 

App.js

import React from 'react';

import {CoreProvider} from './Core/CoreManagement';

import Route from './Route';

const App: () => React$Node = () => {
  return (
    <>
      <CoreProvider>
        <Route />
      </CoreProvider>
    </>
  );
};

export default App;

 

App.js는 기본이죠. 최초 앱이 시작되는 Entry Point입니다. 특히 CoreContext를 잘 보세요.

하위 Layer에 Route보이시죠. 즉 뷰(또는 뷰들)을 저 CoreProvider가 감싸야 합니다.

그래야 하위 Layer들이 값을 서로 공유가 가능해요.

 

Route.js

import React, {useContext} from 'react';


import {NavigationContainer} from '@react-navigation/native';

import {createStackNavigator} from '@react-navigation/stack';

import {CoreContext} from './Core/CoreManagement';

const Stack = createStackNavigator();

import View1 from './View1';
import View2 from './View2';

export default function Route(props) {
  const result = useContext(CoreContext);

  console.log(result.tag + ' Route ');
  console.log(result.tag + ' Route result : ' + JSON.stringify(result));

  return (
    <NavigationContainer>
      <Stack.Navigator headerMode={'none'} initialRouteName="View1">
        <Stack.Screen name="View1" component={View1} />
        <Stack.Screen name="View2" component={View2} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

 

저는 Stack Navigation를 이용하여 하위 View들이 CoreProvider가 선언한 값, 함수를 공유합니다.

여기는 특별한건 없습니다.

 

View1.js

/* eslint-disable react-native/no-inline-styles */
import React, {useEffect, useContext, useState} from 'react';
import {
  SafeAreaView,
  StyleSheet,
  ScrollView,
  View,
  Text,
  StatusBar,
  Image,
  ActivityIndicator,
  Button
} from 'react-native';

import {CoreContext, CoreConsumer} from './Core/CoreManagement';

import {useNavigation} from '@react-navigation/native';

export default function View1(props) {
  const navigation = useNavigation();

  const {route} = props;

  const {params} = route;

  const result = useContext(CoreContext);

  console.log(result.tag + ' View1 ');
  console.log(result.tag + ' View1 ' + result.value);

  const [backValue , SetbackValue] = useState(result.value);

  useEffect(() => {
    const focus = props.navigation.addListener('focus', async () => {
      console.log('>>>>>>>>>>>>>>>>>>>>>>>>>>>> View1 Refresh');
      console.log(
        '>>>>>>>>>>>>>>>>>>>>>>>>>>>> props ' + JSON.stringify(props),
      );
      SetbackValue(result.value);
    });
    return focus;
  }, [props, props.navigation]);


  return (
    <CoreConsumer>
      {({value, SetValue}) => (
        <View
          style={{
            alignItems: 'center',
            justifyContent: 'center',
            flex: 1,
            backgroundColor: '#828192',
          }}>
          <Text>View1 : {backValue} </Text>
          <Button
            title="Next"
            onPress={() => {
              SetValue(backValue + 1);
              navigation.navigate('View2');
            }}
          />
        </View>
      )}
    </CoreConsumer>
  );
}

View2.js

import React, {useEffect, useContext} from 'react';
import {
  SafeAreaView,
  StyleSheet,
  ScrollView,
  View,
  Text,
  StatusBar,
  Image,
  ActivityIndicator,
  Button
} from 'react-native';

import {CoreContext, CoreConsumer} from './Core/CoreManagement';

import {useNavigation} from '@react-navigation/native';

export default function View2(props) {

  const navigation = useNavigation();

  const {route} = props;

  const {params} = route;

  const result = useContext(CoreContext);

  console.log(result.tag + ' View2 ');

  useEffect(() => {
    const focus = props.navigation.addListener('focus', async () => {
      // console.log('>>>>>>>>>>>>>>>>>>>>>>>>>>>> View2 Refresh');
      console.log(
        '>>>>>>>>>>>>>>>>>>>>>>>>>>>> props ' + JSON.stringify(props),
      );
    });
    return focus;
  }, [props, props.navigation]);

  return (
    <CoreConsumer>
      {({value, SetValue}) => (
        // eslint-disable-next-line react-native/no-inline-styles
        <View
          // eslint-disable-next-line react-native/no-inline-styles
          style={{
            alignItems: 'center',
            justifyContent: 'center',
            flex: 1,
            backgroundColor: '#273623',
          }}>
          <Text>View2 : {value} </Text>
          <Button
            title="Next"
            onPress={() => {
              SetValue(result.value + 1);
              navigation.goBack();
            }}
          />
        </View>
      )}
    </CoreConsumer>
  );
}

 

여기에 CoreConsumer가 나옵니다. 상위 뷰에서 선언한 CoreProvider와 공유된 변수 및 함수를 하위 뷰들과 공유하는 겁니다.

특히 주의 깊게 보셔야 할 부분은 다름아닌 랜더링 진입 전 및 후 입니다.

랜더링 진입전 :

개발자들은 뷰 함수에 진입시 미리 값을 불러와 최초 화면을 그리기전에 하고 싶은 작업이 있습니다. 대표적인게 서버통신일 겁니다.

const result = useContext(CoreContext);

const [backValue , SetbackValue] = useState(result.value);

위와 같이 useContext를 사용하여 CoreProvider에서 선언한 CoreContext를 불러오면 데이터를 공유 할 수 있습니다.

랜더링 진입후 :

만약 CoreProvider에서 가지고 있는 데이터를 바로 화면에 그리고 싶을때가 있을 겁니다.

{({value, SetValue}) => (
        <View
          style={{
            alignItems: 'center',
            justifyContent: 'center',
            flex: 1,
            backgroundColor: '#828192',
          }}>
          <Text>View1 : {backValue} </Text>
          <Button
            title="Next"
            onPress={() => {
              SetValue(backValue + 1);
              navigation.navigate('View2');
            }}
          />
        </View>
)}

위와 같이 CoreProvider에서 선언한 value, SetValue를 화면에 바로 매핑하여 사용 할 수 있습니다.

Next버튼을 계속 누르면 +1 증가 하게 되고 가장 중요한 포인트는 navigation.goBack(); 입니다.

처음 화면(View1.js)과 두번째 화면(View2.js)은 서로 데이터를 공유하며 앞으로 뒤로 왔다갔다는 액션에서 데이터를

공유하기 때문입니다.

 

데이터 가져오는 부분으로 고생하시는 분들께 도움이 되었으면 합니다.

 

궁금하신 부분은 댓글남겨주세요.

 

반응형