react-nativeuse-statenested-listsitemsreact-native-sectionlist

Nested lists - How to modify (delete, add, update) items from a nested list using item's buttons and useState?


My first idea was to try to delete items from a nested list. I've started using SectionList and a component that has an useState that manages the data changes of the SectionList. I'm trying to solve it using SectionList but it'd be great to display all possible alternatives (FlatList, ViewList, etc).

I figured out how to delete a whole section list (and his items) but not one item by one. I've read plenty of posts, but I did find nothing related to managing SectionList items. Maybe there's an answer out there for FlatList nested items.

Here I left an example code ready to use (without styles) based on React Native official docs:

import React, { useEffect } from "react";
import { StyleSheet, Text, View, SafeAreaView, SectionList, StatusBar, Button } from "react-native";
import { useState } from "react";


const dataResource = [
  {
    title: "Main dishes",
    data: ["Pizza", "Burger", "Risotto"],
    n: "delete",
    k: 1
  },
  {
    title: "Sides",
    data: ["French Fries", "Onion Rings", "Fried Shrimps"],
    n: "delete",
    k: 2
  },
];



function App() {
  const [state, setState] = useState(dataResource)


  const Item = ({ dish, obj, i}) => (
    <View >

      <Text >{dish} </Text>
          <Button title={obj.n} onPress={() => {}} />     // add the handler
    </View>
  );

  const SectionComponent = ({ t, k }) => (
    <View>
      <Text >{t} </Text>
      <Button title={'section'} onPress={() => { setState(state.filter(e => e.k != k)) }} />
    </View>
  );

  return (
    <SafeAreaView >
      <SectionList
        sections={state}
        keyExtractor={(item, index) => item + index}
        renderItem={({ item, section, index}) => <Item dish={item} obj={section} i={index}/>}
        renderSectionHeader={({ section: { title, k } }) => <SectionComponent k={k} t={title} />}
      />
    </SafeAreaView>
  );
}



export default App;



Solution

  • I think how you display the data is trivial. The component you use will just change how you access the data, not how you update it. What you need is helper functions for editing the data. With those in place you can do things like add/remove section items, and editing section items themselves:

    import React, { useState } from 'react';
    import {
      Text,
      View,
      StyleSheet,
      SafeAreaView,
      SectionList,
      Button,
      TextInput,
    } from 'react-native';
    
    const dataResource = [
      {
        title: 'Main dishes',
        data: ['Pizza', 'Burger', 'Risotto'],
        id: 1,
      },
      {
        title: 'Sides',
        data: ['French Fries', 'Onion Rings', 'Fried Shrimps'],
        id: 2,
      },
    ];
    export default function App() {
      const [state, setState] = useState(dataResource);
      const [sectionTitle,setSectionTitle] = useState('Drinks')
      
      const editItem = (itemId, newValue) => {
        let newState = [...state];
        let itemIndex = newState.findIndex((item) => item.id == itemId);
        if (itemIndex < 0) return;
        newState[itemIndex] = {
          ...newState[itemIndex],
          ...newValue,
        };
        setState(newState);
      };
      const addSectionItem = (title)=>{
        let newState = [...state]
        newState.push({
          title,
          data:[],
          id:newState.length+1
        })
        setState(newState)
      }
    
      const removeFood = (itemId, food) => {
        let currentItem = state.find((item) => item.id == itemId);
        console.log(currentItem);
        currentItem.data = currentItem.data.filter((item) => item != food);
        console.log(currentItem.data);
        editItem(itemId, currentItem);
      };
      const addFood = (itemId, food) => {
        let currentItem = state.find((item) => item.id == itemId);
        console.log(currentItem.data);
        currentItem.data.push(food);
        console.log(currentItem.data);
        editItem(itemId, currentItem);
      };
    
      const Item = ({ item, section, index }) => {
        return (
          <View style={styles.row}>
            <Text>{item} </Text>
            <Button
              title={'Delete'}
              onPress={() => {
                removeFood(section.id, item);
              }}
            />
          </View>
        );
      };
    
      const SectionHeader = ({ title, id }) => {
        return (
          <View style={styles.header}>
            <Text style={{ fontSize: 18 }}>{title} </Text>
            <Button
              title={'X'}
              onPress={() => {
                setState(state.filter((e) => e.id != id));
              }}
            />
          </View>
        );
      };
      const SectionFooter = ({ id }) => {
        const [text, setText] = useState('');
        return (
          <View style={[styles.row, styles.inputWrapper]}>
            <Text>Add Entry</Text>
            <TextInput
              value={text}
              onChangeText={setText}
              style={{ borderBottomWidth: 1 }}
            />
            <Button title="Add" onPress={() => addFood(id, text)} />
          </View>
        );
      };
      return (
        <SafeAreaView>
          <Button title="Reset list" onPress={() => setState(dataResource)} />
          <View style={[styles.row, styles.inputWrapper]}>
            <Text>Add New Section</Text>
            <TextInput
              value={sectionTitle}
              onChangeText={setSectionTitle}
              style={{ borderBottomWidth: 1 }}
            />
            <Button title="Add" onPress={() => addSectionItem(sectionTitle)} />
          </View>
          <View style={styles.sectionListWrapper}>
            <SectionList
              sections={state}
              keyExtractor={(item, index) => item + index}
              renderItem={Item}
              renderSectionHeader={({ section }) => <SectionHeader {...section} />}
              renderSectionFooter={({ section }) => <SectionFooter {...section} />}
            />
          </View>
        </SafeAreaView>
      );
    }
    
    const styles = StyleSheet.create({
      row: {
        flexDirection: 'row',
        width: '100%',
        justifyContent: 'space-between',
        padding: 5,
        alignItems: 'center',
      },
      header: {
        flexDirection: 'row',
        paddingVertical: 5,
        width: '100%',
        borderBottomWidth: 1,
      },
      inputWrapper: {
        paddingVertical: 15,
        marginBottom: 10,
      },
      sectionListWrapper: {
        padding: 5,
      },
    });
    

    Here's a demo