Search code examples
javascripttypescriptreact-nativeflatlist

TextInput doesn't filter FlatList items


I'm using Typescript, form some reason the function below gives me the error: Property 'title' doesn't exist on type 'never'. If i write the same function in JS it doesn't give me error, only in Typescript. I don't know why, but my TextInput doens't filter the FlatList items.

    const searchFilter =(text) => {
        if(text){
            const newData = masterData.filter((item) => {
                //the error appears in the next line below in 'item.title'
                const itemData = item.title ? item.title.toUpperCase() : ''.toUpperCase(); 
                const textData = text.toUpperCase();
                return itemData.indexOf(textData) > -1;
            });
            setFilteredData(newData);
            setSearch(text);
        } else {
            setFilteredData(masterData);
            setSearch(text);
        }
    }

My FlatList works and show the data from the JSON fetch. The only problem is when i start typing in the TextInput and the FlatList disappears.

Full code below:

const ManageCustomersScreen =(props: ManageCustomersScreen) =>{
    //navigation
    const backPage = () => props.navigation.navigate("Home");
    const callCustomer = () => props.navigation.navigate("Customer");

    const [filteredData, setFilteredData] = useState([]);
    const [masterData, setMasterData] = useState([]);
    const [search, setSearch] = useState('');

    useEffect(() => {
        fetchPosts();
        return() => {

        }
    }, [])
  
    const fetchPosts = () => {
        const apiUrl = 'https://jsonplaceholder.typicode.com/users';
        fetch(apiUrl)
        .then((response) => response.json())
        .then((responseJson) => {
            setFilteredData(responseJson);
            setMasterData(responseJson);
        }).catch((error) => {
            console.error(error);
        })
    }

    const ItemView = ({item}) => {
        return(
            <View style={manageCustomersStyle.tableBody}>
            <View style={manageCustomersStyle.customerCard}>
                <TouchableOpacity
                style={manageCustomersStyle.customerCardContent}
                onPress={callCustomer}>
                    <View style={manageCustomersStyle.customerCardInfo}>
                        <Text style={manageCustomersStyle.customerCardInfoName}>{item.name}</Text>
                        <Text style={manageCustomersStyle.customerCardInfoId}>{item.id}</Text>
                    </View>
                    <Icon
                        name="angle-double-right"
                        size={40}
                        color="grey"
                    />
                </TouchableOpacity>
            </View>
        </View>
        )
    }

    const searchFilter =(text) => {
        if(text){
            const newData = masterData.filter((item) => {
                const itemData = item.title ? item.title.toUpperCase() : ''.toUpperCase();
                const textData = text.toUpperCase();
                return itemData.indexOf(textData) > -1;
            });
            setFilteredData(newData);
            setSearch(text);
        } else {
            setFilteredData(masterData);
            setSearch(text);
        }
    }
    
    return(
        <SafeAreaView style={manageCustomersStyle.safeAreaView}>
            <Appbar.Header>
                <Appbar.BackAction onPress={backPage} />
                <Appbar.Content title ="Manage Customers" />         
            </Appbar.Header>
            <View style={manageCustomersStyle.searchBarView}>
                <Icon 
                name="search"
                size={30}
                color="grey"
                style={manageCustomersStyle.searchBarIcon}/>
                <TextInput
                style={manageCustomersStyle.searchBar}
                placeholder={'Search'}
                value={search}
                onChangeText={(text) => searchFilter(text)}/>
            </View>
            <FlatList 
                data={filteredData}
                keyExtractor={(item, index) => index.toString()}
                renderItem={ItemView}
            />
        </SafeAreaView>
    );
}

export default ManageCustomersScreen;

These screenshots shows when i start typing in the TextInput the FlatList simply disappears.

enter image description here

enter image description here


Solution

  • General

    Typescript is meant for writing typed javascript. Currently, you're just writing javascript in a Typescript file. You're not really using Typescript.

    You'd greatly benefit from reading: https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html

    For this specific problem

    I would start by changing this line to actually include type data:

    const [masterData, setMasterData] = useState([]);
    

    Currently, the most strict type Typescript can determine for masterData would be any[], which is bad. You want to avoid any whenever you can, as it means that Typescript cannot perform any type check for this variable.

    For example, if title is an optional string, you could write:

    const [masterData, setMasterData] = useState<{ title?: string }[]>([]);
    

    Or even better, you could define this as a type:

    interface MasterDataItem {
      title?: string
    }
    

    And then use it like this:

    const [masterData, setMasterData] = useState<MasterDataItem[]>([]);