리액트 네이티브(React Native), Expo로 이전 글까지 완료했으면 이제는 저장된 Content들을 수정, 삭제할 차례이다.

이 때, 고민해야할 것들이 몇가지 있다.

  • 수정버튼을 누르면 컨텐츠 생성화면으로 가고, 내가 선택한 데이터들이 표현되어 있어야 한다.
  • 수정 후, ListView에 반영되어야 한다.
  • 삭제 시, ListView에 반영되어야 한다.

View에 버튼을 추가하는 것은 쉽기 때문에 생략하기로 하고 바로 수정하는 부분으로 넘어가도록 한다.

참고로 **Button**은 글씨 크기조절이 안되니, **TouchableOpacity**을 사용하기를 추천한다.

수정 부분

일단, 수정을 눌렀을 때 가장 처음으로 필요한 값은 내가 누른 Row의 Index값이다.

그리고 난 후에는 내가 표현한 데이터들 중에서 해당 Index에 해당하는 녀석의 SQLiteid값이다.

그러면 아래와 같은 순서를 생각해 볼 수 있다.

  1. SQLite에서 읽은 배열 값들을 state에 저장해 둔다.
  2. Row가 선택되면 state에 저장해둔 배열에서 선택된 Index에 해당하는 값을 뽑아낸다.
  3. 뽑아낸 값을 Navigation을 통해 넘긴다.

그럼 이전 글에서 read함수와 ListView부분을 아래와 같이 고친다.

read() {
  db.transaction(
    tx => {
      tx.executeSql('SELECT * FROM contents', [], (_, { rows: { _array } }) => {
          this.setState({ dataSource: this.state.dataSource.cloneWithRows(_array), datas: _array})
        }
      );
    },
  );
}

render() {
  return (
    <ListView
      dataSource={this.state.dataSource}
      renderRow={(rowData, sectionID, rowID) =>
        // ...
        <TouchableOpacity
          style={{ marginLeft: 12, marginRight: 12 }}
          onPress={() => {
              let data = this.state.datas[rowID]
              this.props.navigation.navigate('Create', { data: data, image: data["picture"] })
            }
          }>
          <Text style={{fontSize: 14, textAlign: 'center', textAlignVertical: "center"}}>수정</Text>
        </TouchableOpacity>
        // ...
    />
  );
}

// ...

위 코드에서 data의 값이 JSON이기 때문에 data["picture"]로 접근할 수 있다.

이제 수정 화면의 부분을 생각해보면..

  1. Text에 글이 채워져 있어야 한다.
  2. Image에 이미지가 보여야 한다. 이전 글에서 componentWillReceiveProps을 구현해 두어서 그대로 놔두어도 된다.
  3. 사용자는 내가 선택한 User로 선택되어 있어야 한다.

그대로 코드로 구현하면 아래와 같다.

//...

constructor(props) {
  super(props);

  let updated = this.props.navigation.getParam("data", null);
  let text = '';
  if(updated !== null) {
    text = updated["content"]
  }
  this.state = {text: text, height: 0, users: null, user: null, loading: true, updated: updated};
  _this = this;
  // ...
}

componentWillMount() {
  db.transaction(
    tx => {
      tx.executeSql('SELECT * FROM users', [], (_, { rows: { _array } }) => {
          let user = _array[0]
          if(this.state.updated !== null) {
            for(var i in _array) {
              if(this.state.updated["user_id"] === _array[i]["id"]) {
                user = _array[i]
                break
              }
            }
          }
          this.setState({ users: _array, loading: false, user: user })
        }
      );
    },
  );
}

add() {
  if(_this.state.updated !== null) {
    db.transaction(
      tx => {
        tx.executeSql('UPDATE contents SET content = ?, picture = ?, user_id = ?, user_name = ?, user_pic = ? WHERE id = ?', [_this.state.text, _this.image.state.image, _this.state.user.id, _this.state.user.name, _this.state.user.picture, _this.state.updated["id"]]);
      },
    );
  }
  else {
    // INSERT Code...
  }
}
// ...

삭제 부분

삭제 부분에서 가장 고민해야 할 부분은 즉시, ListView에 반영하는 것이다.

this.state.dataSource를 그대로 사용하면 이상하게 rowHasChanged가 호출되지 않아 찾아본 결과 아래와 같이 해야했다. (원인을 알면 댓글이나 연락 부탁드립니다.)

let ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});

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

  constructor() {
    super();

    this.state = {
      dataSource: ds.cloneWithRows([]),
      datas: null
    };
  }

  delete(contentId) {
    db.transaction(
      tx => {
        tx.executeSql(`DELETE FROM contents WHERE id = ?;`, [contentId]);
      },
    )
  }

  deleteRow(rowId) {
    this.state.datas.splice(rowId, 1)
    this.setState({
      dataSource: ds.cloneWithRows( this.state.datas ),
    })
  }

  render() {
    return (
      <ListView
        dataSource={this.state.dataSource}
        renderRow={(rowData, sectionID, rowID) =>
          // ...
          <TouchableOpacity
            onPress={() => {
                let data = this.state.datas[rowID]
                this.delete(data["id"])
                this.deleteRow(rowID)
              }
            }>
            <Text style=>삭제</Text>
          </TouchableOpacity>
          // ...
      />
    );
  }
}

위와 같이 구현하면 수정, 삭제 후의 모든 값이 반영된다.

이렇게 리액트 네이티브(React Native)로 인스타그램의 메인 뷰 부분을 그대로 따라 만드는 나만의 구성대로 만들어 보았다.

전체 소스는 링크로 가면 볼 수 있다.