Search code examples
react-nativeasynchronousreact-native-flatlistasyncstorage

React Native Flatlist Remove Item Removing Last Item


I am using "react-native-swipe-list-view" to try to remove an item. For some reason when I try to remove an item it is removing the last item from the flatlist even though that item is still showing in the array and the correct item is removed.

Basically always the last item is removed from the flatlist when I remove an item even though that item is still present in the array of data. Very weird activity. And when I leave and come back to the page the correct data shows.

CoinList.js

import React, { Component } from "react";
import { FlatList, StyleSheet, Text, View , AsyncStorage } from "react-native";
import CoinListRow from './CoinListRow';


class CoinList extends Component {
  constructor(props) {
    super(props);
    this.state = {
      coinList: null
    };
  }


  updateCoinList = (updatedCoins) => {
    this.setState(
      {coinList: updatedCoins}
    )
  }

  componentDidMount() {
    AsyncStorage.getItem("coins").then(value => {
      // need to convert returned object into an array
      this.setState({coinList: Object.values(JSON.parse(value))})
    });
  }

  render() {
    return (
      <View style={styles.container}>
        {this.state.coinList !== null ? <FlatList
          data={this.state.coinList}
          keyExtractor={(item, index) => index.toString()}
          extraData={this.state}
          renderItem={({item}) => <CoinListRow style={styles.item} item={item} updateCoinList={this.updateCoinList}/>}
        /> : null}
      </View>
    );
  }
}

export default CoinList;

CoinListRow:

import React, { Component } from "react";
import { withNavigation } from "react-navigation";
import { AsyncStorage } from "react-native";
import { View, StyleSheet, Text, TouchableOpacity } from "react-native";
import { ListItem } from "react-native-elements";
import { SwipeRow } from "react-native-swipe-list-view";

import images from "../../assets/logos/coinLogos";

class CoinListRow extends Component {
  constructor() {
    super();
    this.state = {
      coinInfo: null,
      disableInterstitialBtn: false
    };
  }

  componentDidMount() {
    const { item } = this.props;
    return fetch(`https://api.coincap.io/v2/assets/${item.card.slug}`)
      .then(response => response.json())
      .then(responseJson => {
        console.log(responseJson);
        this.setState({
          isLoading: false,
          coinInfo: responseJson.data
        });
      })
      .catch(error => {
        console.error(error);
      });
  }


  removeCoin = () => {
  AsyncStorage.getItem('coins')
    .then((coins) => {
      AsyncStorage.removeItem('coins');
      let c = coins ? JSON.parse(coins) : {};
      delete c[this.props.item.card.slug]
      AsyncStorage.setItem('coins', JSON.stringify(c));
      this.props.updateCoinList(Object.values(c));
    })
    .catch((error)=> {
      console.log(error);
      alert(error);
    }
    )
  }

  render() {
    const { coinInfo } = this.state;
    const { item } = this.props;
    console.log(this.state.coinInfo);
    return (
      <View>
        {this.state.coinInfo ? (
          <SwipeRow disableRightSwipe={true} rightOpenValue={-120}>
            <View style={styles.standaloneRowBack}>
              <Text style={styles.backTextWhite}></Text>
              <TouchableOpacity onPress={this.removeCoin}><Text style={styles.backTextWhite}>Remove Item</Text></TouchableOpacity>
            </View>
            <View>
              <ListItem
                key={item.card.slug}
                leftAvatar={{ source: images[item.card.slug] }}
                title={coinInfo.name}
                titleStyle={{ fontWeight: "bold" }}
                subtitle={coinInfo.symbol}
                onPress={this._openInterstitial}
                chevron
                bottomDivider={true}
              />
            </View>
          </SwipeRow>
        ) : null}
      </View>
    );
  }
}

export default withNavigation(CoinListRow);

Solution

  • I forgot to run a componentDidUpdate based on the changing props. I was going to make a snack and then realized I forgot to save my code when I did a git stash:

    I was thinking if I updated the props in the parent component that it would then automatically trigger a refresh by passing new props, but since the component is already mounted I needed to do the refresh after the fact.

     componentDidUpdate(prevProps) {
        const { item } = this.props;
        if (prevProps.item !== this.props.item) {
          return fetch(`https://api.coincap.io/v2/assets/${item.card.slug}`)
          .then(response => response.json())
          .then(responseJson => {
            this.setState({
              coinInfo: responseJson.data
            });
          })
          .catch(error => {
            console.error(error);
          });
        }
      }