Ignis
Ignis

Reputation: 21

How to Filter List Without Losing Data

The filter buttons on my React Native work in terms of filtering the data, but it doesn't keep the data that was filtered out, so I can only filter the data once and would have to input them again. I would like it to be able to filter without losing the data, so I can continuously switch between the different filters and show that they all work without needing to add new data.

import React, { useState } from 'react';
import {StyleSheet,Text,View,TextInput,TouchableOpacity,FlatList,Alert,Modal, Dimensions} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
import AsyncStorage from '@react-native-async-storage/async-storage';

function App() {
  const [todoInput, settodoInput] = React.useState('')

  const [Todos, setTodos] = React.useState([]);
  const [FilterTodos, setFilterTodos] = React.useState([Todos]);

  const StatusTab = [
    {
      status: 'All',
      completed: 'All'
    },
    {
      status: 'Complete',
      completed: true
    },
    {
      status: 'Incomplete',
      completed: false
    }
  ]

  const [Status,setStatus] = useState('All');

  const setStatusFilter = Status => {
    if(Status !== 'All') {
      setTodos([...Todos.filter(e => e.completed === Status)])
    } else {
      setTodos(Todos)
    }
    setStatus(Status);
  };

  const [isRender,setisRender] = useState(false);
  const [modalVisible, setmodalVisible] = useState(false);
  const [editText, seteditText] = useState();
  const [editItem, seteditItem] = useState();

  React.useEffect(()=>{
    GetTasks();
  },[]);

  React.useEffect(()=>{
    SaveTasks(Todos);
  },[Todos]);

  const ListItem = ({Todo}) => {
    return <View style={styles.ListItem}>
      <View style={{flex:1}}>
        <Text style={[styles.TaskText,{textDecorationLine: Todo?.completed?"line-through":"none"}]}>{Todo?.task}</Text>
      </View>
      {!Todo?.completed && (
        <TouchableOpacity style={[styles.EditIcon]} onPress={()=>completeTodo(Todo?.id)}>
          <Icon name='done' size={20} color='#FFFFFF'/>
        </TouchableOpacity>
      )}

      <TouchableOpacity style={[styles.EditIcon,{backgroundColor:'#5D76E8'}]}
      onPress={()=>editTodo(Todo?.id)}>
        <Icon name='edit' size={20} color='#FFFFFF'/>
      </TouchableOpacity>
      
      <TouchableOpacity style={[styles.EditIcon,{backgroundColor:'#D30404'}]} 
      onPress={()=>deleteTodo(Todo?.id)}>
        <Icon name='delete' size={20} color='#FFFFFF'/>
      </TouchableOpacity>
    </View>
  }

  const SaveTasks = async Todos =>{
    try {
      const stringifyTodos = JSON.stringify(Todos)
      await AsyncStorage.setItem('Todos', stringifyTodos)
    } catch (e) {
      console.log(e);
      // saving error
    }
  };

  const GetTasks = async () => {
    try {
      const Todos = await AsyncStorage.getItem('Todos');
      if(Todos != null){
        setTodos(JSON.parse(Todos));
      }
    } catch (error) {
      console.log(error);
    }
  };

  const addTodo = () =>{
    if(todoInput == ""){
      Alert.alert("Error","Please Input Task");
    }
    else{
    // console.log(todoInput);
    const newTodo = {
      id:Math.random(),
      task: todoInput,
      completed: false,
    };
    setTodos([...Todos,newTodo])
    settodoInput('');
    }
  }

  const completeTodo = (todoID) => {
    console.log(todoID);
    const newTodos = Todos.map((item)=>{
      if(item.id == todoID){
        return {...item,completed:true}
      }
      return item;
    });
    setTodos(newTodos);
  };

  const editTodo = (item) => {
    setmodalVisible(true);
    seteditText(item.text);
    seteditItem(item.id);
  };

  const handleEditItem = (editItem) =>{
    const newData =Todos.map(item =>{
      if (item.id == editItem) {
        item.text = editText;
        return item
      }
      return item;
    })
    setTodos(newData);
    setisRender(!isRender);
  }

  const onPressSaveEdit = () => {
    handleEditItem(editItem);
    setmodalVisible(false);
  }

  const deleteTodo = (todoID) =>{ 
    Alert.alert("Confirm","Delete Task?",[{
      text:"Yes",
      onPress: () => {const newTodos = Todos.filter(item => item.id != todoID);
       setTodos(newTodos)}
    },
    {text:"No"}
  ])
  };

  return (
    <View style={styles.Container}>
      <View style={styles.Header}>
        <TextInput style={styles.SearchBar} placeholder='Add Task' 
        value={todoInput} onChangeText={(text)=>settodoInput(text)}/>
        <TouchableOpacity onPress={addTodo}>
          <View style={styles.IconContainer}>
            <Icon name="add" color='#FFFFFF' size={30}/>
          </View>
        </TouchableOpacity>
      </View>

      <View style={styles.TaskList}>
        <FlatList
        showsVerticalScrollIndicator={false}
        contentContainerStyle={{padding:20,paddingBottom:100}} 
        data={Todos}
        keyExtractor={(item) => item.id.toString()} 
        renderItem={({item})=><ListItem Todo={item}/>}
        extraData={isRender}/>

        <Modal
        animationType='fade'
        visible={modalVisible}
        onRequestClose={() => setmodalVisible(false)}
        >
          <View style={styles.ModalView}>
            <Text style={styles.ModalText}>Change Task:</Text>
            <TextInput style={styles.ModalInput}
            onChangeText={(text) => seteditText(text)}
            defaultValue={editText}
            editable={true}
            multiline={false}
            maxLength={200}/>
            <Text style={styles.ModalText}>Task Status:</Text>
            <TouchableOpacity style={styles.FilterTab}>
              <Text style={styles.TabText}>Complete</Text>
            </TouchableOpacity>
            <TouchableOpacity style={styles.FilterTab}>
              <Text style={styles.TabText}>Incomplete</Text>
            </TouchableOpacity>
            <TouchableOpacity
            onPress={()=> onPressSaveEdit()}
            style={styles.SaveButton}>
              <Text style={styles.ModalText}>Save</Text>
            </TouchableOpacity>
          </View>
        </Modal>
      </View>

      <View style={styles.Footer}>
        {
          StatusTab.map(e => (
            <TouchableOpacity style={[styles.FilterTab, Status === e.completed && styles.FilterTabActive]}
            onPress={() => setStatusFilter(e.completed)}>
              <Text style={[styles.TabText, Status === e.status && styles.TabTextActive]}>{e.status}</Text>
            </TouchableOpacity>
          ))
        }
      </View>

    </View>
  );
}
 
const styles = StyleSheet.create({
  Container:{
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center'
  },
  Header:{
    flexDirection: 'row',
    backgroundColor:'#000000',
    alignItems: "center",
    justifyContent: 'center',
    width:'100%'
  },
  SearchBar:{
    borderColor: "#FFFFFF",
    backgroundColor: "#DDDDDD",
    marginHorizontal: 5,
    width: '40%',
    height:'80%',
    borderRadius: 30
  },
  IconContainer:{
    height: 50,
    width: 50,
    backgroundColor: '#061380',
    borderRadius: 25,
    justifyContent: 'center',
    alignItems: 'center'
  },
  TaskList:{
    flex: 10,
    flexDirection: 'row',
    flexWrap: 'wrap',
    justifyContent: 'space-around',
    alignItems: 'center'
  },
  ListItem:{
    padding:20,
    backgroundColor:'#DEDEDE',
    flexDirection:'row',
    elevation:12,
    borderRadius:7,
    marginVertical:10
  },
  TaskText:{
    fontWeight:'bold',
    fontSize:15,
    color:'#000000',
  },
  EditIcon:{
    height:25,
    width:25,
    backgroundColor:'#1BCC48',
    justifyContent:'center',
    alignItems:'center',
    marginLeft:5,
    borderRadius:3
  },
  ModalView:{
    flex:1,
    alignItems:'center',
    justifyContent:'center'
  },
  ModalText:{
    fontSize:25,
    fontWeight:'bold',
    marginVertical:30,
    marginLeft:10
  },
  ModalInput:{
    width:'90%',
    height:70,
    borderColor:'#000000',
    borderWidth:1,
    fontSize:25
  },
  SaveButton:{
    backgroundColor:'#3AE3A0',
    paddingHorizontal:100,
    alignItems:'center',
    marginTop:20
  },
  Footer:{
    flexDirection:'row',
    alignSelf:'center',
    marginBottom:10
  },
  FilterTab:{
    width:Dimensions.get('window').width / 3.5,
    flexDirection:'row',
    borderWidth:1,
    borderColor:'#4A4A4A',
    padding:10,
    justifyContent:'center'
  },
  FilterTabActive:{
    backgroundColor:'#F87861'
  },
  TabText:{
    fontSize:16,
  },
  TabTextActive:{
    color:'#FFFFFF'
  },
});

export default App;

Upvotes: 2

Views: 246

Answers (2)

Nilesh Patel
Nilesh Patel

Reputation: 3317

added a new method and passing it to the Flatlist

const getTodos = (status) => {
    return status === 'All'
      ? Todos
      : Todos.filter((item) => item.completed === status);
  };
<FlatList
          showsVerticalScrollIndicator={false}
          contentContainerStyle={{ padding: 20, paddingBottom: 100 }}
          data={getTodos(Status)}
          keyExtractor={(item) => item.id.toString()}
          renderItem={({ item }) => <ListItem Todo={item} />}
          extraData={isRender}
        />

please check this

demo code

Upvotes: 1

Fapi
Fapi

Reputation: 356

I think you are changing the data array when filtering with:

setTodos([...Todos.filter(e => e.completed === Status)])

How I did it (similar) in my app:

const [filtered, setFiltered] = useState(setStatusFilter());

then you should

setFiltered([...Todos.filter(e => e.completed === Status)])

to have the filtered values in const filtered and the source list stay the same as before.. and you can then use the filtered to show in a mapping ..

Upvotes: 0

Related Questions