리액트 네이티브(React Native), Expo로 카메라, 갤러리에서 이미지를 가져오는 방법을 알았으니 이제는 해당 해당 데이터를 저장할 차례이다.

일단, 인스타그램의 비슷한 구조로 만들 것이기에 몇가지 필드가 필요하다.

User 테이블

id : INTEGER // AUTOINCREMENT
name : TEXT
picture : TEXT // image path

Content 테이블

id : INTEGER // AUTOINCREMENT
content : TEXT
picture : TEXT // image path
user_id : INTEGER // User 테이블의 id
user_name : TEXT // User 테이블의 name
user_pic : TEXT // User 테이블의 picture

일단, 리액트 네이티브(React Native), Expo의 SQLite 데이터베이스 사용을 전혀 모르기 때문에 Document의 샘플 코드를 참고했다.

import { SQLite } from 'expo';

const db = SQLite.openDatabase('db.db');

export default class App extends React.Component {
  // ...

  componentDidMount() {
      db.transaction(tx => {
        tx.executeSql(
          'CREATE TABLE IF NOT EXISTS contents (id INTEGER PRIMARY KEY AUTOINCREMENT, content TEXT, picture TEXT, user_id INTEGER, user_name TEXT, user_pic TEXT);'
        );
        tx.executeSql(
          'CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, picture TEXT);'
        );
      });
  }

  // ...
}

componentDidMount에서 먼저 table을 생성한다. 그리고 난 후, 이제 실제 데이터를 입력하는 화면에서는 아래와 같이 구현하면 된다.

Navigation을 사용했기 때문에 아래와 같이 표현할 수 있다.

import { SQLite } from 'expo';

const db = SQLite.openDatabase('db.db');
let _this;

export default class CreateScreen extends React.Component {
  // ...
  static navigationOptions = ({navigation}) => {
    const {params = {}} = navigation.state;
    return {
      title: 'Write',
      headerRight: (
        <Button
          onPress={() => {
            params.done();
            navigation.navigate('Home', {added: 1})
          }}
          title="Done"
        />
      ),
    }
  };

  constructor(props) {
    super(props);
    this.state = {text: '', height: 0, users: null, user: null, loading: true};
    _this = this;
    this.props.navigation.setParams({
      done: this.add,
    });
  }

  add() {
    db.transaction(
      tx => {
        tx.executeSql('INSERT INTO contents (content, picture, user_id, user_name, user_pic) values (?, ?, ?, ?, ?)', [_this.state.text, _this.image.state.image, _this.state.user.id, _this.state.user.name, _this.state.user.picture]);
      },
    );
  }

  // ...
}

Image관련 처리를 ImageProcess라는 Component로 분리했기 때문에 해당 컴포넌트를 ref로 직접 접근하는 방법을 사용했다.

<ImageProcess
  ref={image => {
    this.image = image;
  }}
  // ...
/>

navigationOptions에서 this.add처럼 사용할 수 없다. 그렇기에 아래와 같이 해당 함수를 param으로 등록하는 편법을 이용하였다.

this.props.navigation.setParams({
  done: this.add,
});

이렇게 하면, add함수에서 역시 this를 사용할 수 없다. 그렇기에 _this라는 변수를 전역에 선언한 후에 constructor에서 _this = this와 같이 사용하였다. 이 부부은 여기를 참고하면 알 수 있다.

navigationOptions에서 버튼이 눌렸을 때, navigation.navigate('Home', {added: 1}) 부분에서 param을 등록해준 이유는 Home이라는 화면으로 돌아갔을 때, componentWillReceiveProps를 호출하기 위해서다.

componentWillReceiveProps이 호출되어야 ListView에 표현할 데이터를 다시 부를 수 있다.

다음은 리액트 네이티브(React Native), Expo의 Picker, ListView 그리고 SQLite에서의 삭제 및 수정에 관한 글을 순서대로 올릴 것이다.