Jason
Jason

Reputation: 4130

React Native FlatList Rerender from Descendant

Sorry, folks, I did attempt to do my research on previous questions.

I have the following Component implemented as a screen in my application:

class DivisionManagmementScreen extends Component {

    constructor(props) {
        super(props);

        this.state = {
            divisions: [],
            toggle: true
        };
    }

    async componentDidMount() {

        let programId = this.props.navigation.getParam("programId", 1);

        this.db = await open({name: "stats.db",createFromLocation: "~soccerstats.db"});

        const sql = "SELECT * FROM DIVISION where DIVISION_PROGRAM_ID = ? ORDER BY DIVISION_NAME";

        let result = await query(this.db, sql, [programId]); 

        this.setState({divisions: result.result});
    }

    async componentWillUnmount() {
        await close(this.db);
    }

    _renderItem = ({item}) => (
        <DivisionManagementRow divisionId={item.DIVISION_ID} divisionName={item.DIVISION_NAME} />
    );


    render() {
        return (
            <View style={styles.component}>
                <View style={styles.listArea}>
                    <FlatList 
                        data={this.state.divisions}
                        renderItem={this._renderItem}
                        keyExtractor={(item) => item.DIVISION_ID.toString() }
                        extraData={this.state.toggle}/>
                </View>
                <View style={styles.bottomButtonArea}>
                    <View style={styles.bottomButtonSection}>
                        <MenuNavigationButton label="Add Division" target="AddDivisionScreen" />
                    </View>
                </View>
            </View>
        );
    }
}

export default withNavigation(DivisionManagmementScreen);

Note that I am using the passing this.state.toggle as the parameter for my FlatList extraData parameter.

Below is the implementation of the Row of data that FlatList renders (minus styles, proptypes, etc):

class DivisionManagementRow extends Component {

constructor(props) {
    super(props);

    this.state = {toggle: true};

}

_btnTeams = () => {

    // navigate to Team Management
}

_btnEditDivision = () => {
    this.props.navigation.navigate("EditDivisionScreen", {divisionId: this.props.divisionId});
}

_btnDeleteDivision = () => {

    console.log("Entered _btnDeleteDevision");
    Alert.alert(
        "Are you sure?",
        "This will delete all teams that are assigned to this division. Are you sure you want to do this?",
        [
            {text: "Cancel"},
            {text: "Ok", onPress: () => this._deleteDivision()}
        ]
    );
}

_deleteDivision = async () => {

    console.log("Entered _deleteDivision, divisionId: " + this.props.divisionId);
    const sql = "DELETE FROM DIVISION WHERE DIVISION_ID = ?";

    let divisionId = this.props.divisionId;
    let db = await open({name: "stats.db",createFromLocation: "~soccerstats.db"});
    let result = await execute(db, sql, [divisionId]);

    console.log("Rows affected: " + result.rowsAffected);

    await close(db);

    this.setState({toggle: !this.state.toggle});

}

render() {

    return (
        <View style={styles.component}>
            <Text>{this.props.divisionName}</Text>
            <View style={styles.buttonSection}>
            <TouchableOpacity style={styles.button}
                onPress={this._btnAddTeam}>
                <Text style={styles.buttonText}>Teams</Text>
            </TouchableOpacity>
            <TouchableOpacity style={styles.button}
                onPress={this._btnEditDivision}>
                <Text style={styles.buttonText}>Edit</Text>
            </TouchableOpacity>
            <TouchableOpacity style={styles.button}
                onPress={this._btnDeleteDivision}>
                <Text style={styles.buttonText}>Delete</Text>
            </TouchableOpacity>
            </View>
        </View>
    );
}

}

The first two "buttons" (TouchableOpacitys) work fine, as they simply navigate to different screens. However, the third, "Delete", doesn't have a screen; it shows an alert to confirm the operation, then runs the delete code in the _deleteDivision() method. Unfortunately, the execution of that method does not force a rerender of the FlatList.

Can anyone help?

Upvotes: 0

Views: 122

Answers (1)

bennygenel
bennygenel

Reputation: 24660

Since you delete a record from your data array that you query, you need to refech/query the items and update the state so that your FlatList shows the new items. To achieve this you can do the steps below;

  1. Move your query functions out of the componentDidMount life-cycle method to a separate function then call that function in componentDidMount
  2. Pass down a property to your DivisionManagementRow. Assign a function to this property.
  3. Rerun the query function in this function you passed down.
  4. Run this function after you successfully deleted your item.

Sample (removed some parts for simplicity)

class DivisionManagmementScreen extends Component {
  componentDidMount() {
    this.db = await open({name: "stats.db",createFromLocation: "~soccerstats.db"});
    this.fetchItems()
  }
  async componentWillUnmount() {
    await close(this.db);
  }
  fetchItems = () => {
    let programId = this.props.navigation.getParam("programId", 1);
    const sql = "SELECT * FROM DIVISION where DIVISION_PROGRAM_ID = ? ORDER BY DIVISION_NAME";
    let result = await query(this.db, sql, [programId]); 
    this.setState({divisions: result.result});
  }
  _renderItem = ({item}) => (
    <DivisionManagementRow 
      divisionId={item.DIVISION_ID} 
      divisionName={item.DIVISION_NAME}
      onDelete={this.fetchItems}
    />
  )
  render() {
    return(
      <View style={styles.component}>
        <View style={styles.listArea}>
          <FlatList 
              data={this.state.divisions}
              renderItem={this._renderItem}
              keyExtractor={(item) => item.DIVISION_ID.toString() }
              extraData={this.state.toggle}/>
        </View>
        <View style={styles.bottomButtonArea}>
          <View style={styles.bottomButtonSection}>
              <MenuNavigationButton label="Add Division" target="AddDivisionScreen" />
          </View>
        </View>
      </View>
    )
  }
}

_deleteDivision = async () => {
  const sql = "DELETE FROM DIVISION WHERE DIVISION_ID = ?";
  let divisionId = this.props.divisionId;
  let db = await open({name: "stats.db",createFromLocation: "~soccerstats.db"});
  let result = await execute(db, sql, [divisionId]);
  await close(db);
  this.setState({toggle: !this.state.toggle}, () => {
    this.props.onDelete(); // running passed down function
                           // after state set   
  });
}

Upvotes: 1

Related Questions