0

In my React Native application I want to be able to search and display songs, I have tried implementing search function, but none seems to work. The code below only allows me to type one letter, and when I type one letter my android device keyboard disappears my list will still remain (no search will be triggered).

Please I need help to make the search feature work in order for my search songs to display when User searches for songs.

Here's my player list code:

import React, { useEffect, useState } from 'react';
import { 
    SafeAreaView, 
    View, 
    FlatList,  
    ScrollView, 
    TouchableOpacity,
    ActivityIndicator,
    TextInput,
    } from 'react-native';
import Sound from 'react-native-sound';
import { Avatar, Text } from '@ui-kitten/components';
import Ionicons from 'react-native-vector-icons/Ionicons';
import filter from 'lodash.filter';

import songs from '../../SongData';

import styles from './styles';



const SonglistScreen = () => {
    const [isDisabled, setisDisabled] = useState(false);
    const [loading, setLoading] = useState(false);
    const [isPlayed, setIsPlayed] = useState(false);
    const [data, setData] = useState([]);
    const [query, setQuery] = useState('');
    const [song, setSong] = useState([]);
    const [fullData, setFullData] = useState([]);


    useEffect(() => {
      getData();
    }, []);

    const getData = () => {
      const res = (songs);
      setData(res);
      setFullData(res);
    };

.....//
......//

const renderItem = ({ item, index }) => (
    <ScrollView style={{flex: 1}}>
        <View style={styles.item}>
          <Avatar
                source={{ uri: item.picture }}
                style={{ marginRight: 16 }} 
                size='giant'
          />
          <Text style={styles.title} category='s1'> {item.title} </Text>
        </View>
        <Text 
          style={{ 
            color: '#444',
            fontSize: 11, 
            marginLeft: 112, 
            marginVertical: -20, 
            bottom: 27 
          }}
          category='s1'
          >{item.ArtistName} 
        </Text>
        <Text 
          style={{ 
            color: '#999', 
            marginLeft: 110, 
            marginVertical: -20,
            top: 10
          }}
          category='s1'
          >Genre: {item.genre} 
        </Text>
        <View style={{flexDirection: 'row', left: '230%', bottom: '7%'}}>
              <TouchableOpacity 
                onPress={()=>playSound(item, index)} 
                style={{padding: 10, top: 30, left: 30}}
              >
                <Ionicons name="play" color={isPlayed ? 'red' : '#555555'} size={22} />
              </TouchableOpacity>
              <TouchableOpacity 
                onPress={()=>stopSound(index)} 
                style={{padding: 10, top: 30, left: 20}}
              >
                <Ionicons name="stop" color="#555555" size={22} />
              </TouchableOpacity>
            </View>
    </ScrollView>
  );

  const handleSearch = text => {
    let newData = songs.filter(item =>{
      const itemData = `${item.title.toUpperCase()}`;
      const textData = text.toUpperCase();

      if (text.length > 0) {
        return itemData.indexOf(textData) > -1;
      }
    });
    setData(newData);
    setQuery(text);
  };


  const renderHeader = () => {
    return (
      <View
        style={{
          backgroundColor: '#fff',
          padding: 10,
          marginVertical: 10,
          borderRadius: 20
        }}
      >
        <TextInput
          autoCapitalize="none"
          autoCorrect={false}
          clearButtonMode="always"
          value={query}
          onChangeText={text => handleSearch(text)}
          placeholder="Search songs"
          style={{ backgroundColor: '#fff', paddingHorizontal: 20 }} 
        />
      </View>
    )
  };



  const renderFooter = () => {
    if (!loading) return null

    return (
        <View
            style={{
                paddingVertical: 15,
                borderTopWidth: 1,
                borderColor: '#CED0CE',
            }}
        >
            <ActivityIndicator animating size='large' />
        </View>
    );
  }

...../
...../

return (
    <SafeAreaView style={styles.container}>
      <FlatList
        data={songs}
        renderItem={renderItem}
        keyExtractor={item => item.id}
        ItemSeparatorComponent={renderSeparator}
        ListHeaderComponent={renderHeader}
        ListFooterComponent={renderFooter}
      />
    </SafeAreaView>
  );
}

This is the data I want to search and display songdata.js:

const songs = [
    {
        id: 1,
        title: 'Hero',
        ArtistName: 'Bethany Dilon',
        genre: 'pop',
        isRequire: true,
        picture: 'https://scontent-los2-1.xx.fbcdn.net/v/t1.18169-9/16195530_10211997136709517_8578854309931959016_n.jpg?_nc_cat=101&ccb=1-3&_nc_sid=09cbfe&_nc_eui2=AeEvt5zlNj1bM87SMIgRXz8VjFbfh8f8mfyMVt-Hx_yZ_ISR6pzt6j1tOqssNCwDfnM&_nc_ohc=oQeQeYLPRz8AX_n81Yh&_nc_ht=scontent-los2-1.xx&oh=d87b3097c543a39067095bacfbeb004d&oe=609BF1DC',
        url: require('../../assets/songs/Hero.mp3'),
    },

    {
        id: 2,
        title: 'Advertising URL',
        ArtistName: 'Bethany Dilon',
        genre: 'Soft Rock',
        picture: 'https://scontent-los2-1.xx.fbcdn.net/v/t1.6435-0/s640x640/169534769_1185223728571457_6192837830233317030_n.jpg?_nc_cat=101&ccb=1-3&_nc_sid=9267fe&_nc_eui2=AeH2splO8pf4k-atqrUeWWApKjkWnAsELXsqORacCwQte9doNY5rNtBrWht-o_CYYR4&_nc_ohc=plbZA_Pv91EAX_quQhG&_nc_ht=scontent-los2-1.xx&tp=7&oh=abec7b3bd8ddc13b6c3c0c510a33d8dc&oe=60997696',
        url:
            'https://raw.githubusercontent.com/zmxv/react-native-sound-demo/master/advertising.mp3',
    },

    {
        id: 3,
        title: 'Stronger',
        ArtistName: 'Bethany Dilon',
        genre: 'Country',
        isRequire: true,
        picture: 'https://scontent-los2-1.xx.fbcdn.net/v/t1.6435-0/s640x640/169534769_1185223728571457_6192837830233317030_n.jpg?_nc_cat=101&ccb=1-3&_nc_sid=9267fe&_nc_eui2=AeH2splO8pf4k-atqrUeWWApKjkWnAsELXsqORacCwQte9doNY5rNtBrWht-o_CYYR4&_nc_ohc=plbZA_Pv91EAX_quQhG&_nc_ht=scontent-los2-1.xx&tp=7&oh=abec7b3bd8ddc13b6c3c0c510a33d8dc&oe=60997696',
        url: require('../../assets/songs/Stronger.mp3'),
    },

    {
        id: 4,
        title: 'Faded',
        ArtistName: 'Luchee',
        genre: 'Techno',
        picture: 'https://scontent-los2-1.xx.fbcdn.net/v/t1.6435-0/s640x640/169534769_1185223728571457_6192837830233317030_n.jpg?_nc_cat=101&ccb=1-3&_nc_sid=9267fe&_nc_eui2=AeH2splO8pf4k-atqrUeWWApKjkWnAsELXsqORacCwQte9doNY5rNtBrWht-o_CYYR4&_nc_ohc=plbZA_Pv91EAX_quQhG&_nc_ht=scontent-los2-1.xx&tp=7&oh=abec7b3bd8ddc13b6c3c0c510a33d8dc&oe=60997696',
        url: 'https://github.com/ShivamJoker/sample-songs/raw/master/Faded.mp3',
    },

    {
        id: 5,
        title: 'Solo',
        ArtistName: 'Solo Cosmos',
        genre: 'Afrobeat',
        isRequire: true,
        picture: 'https://scontent-los2-1.xx.fbcdn.net/v/t1.6435-0/s640x640/169534769_1185223728571457_6192837830233317030_n.jpg?_nc_cat=101&ccb=1-3&_nc_sid=9267fe&_nc_eui2=AeH2splO8pf4k-atqrUeWWApKjkWnAsELXsqORacCwQte9doNY5rNtBrWht-o_CYYR4&_nc_ohc=plbZA_Pv91EAX_quQhG&_nc_ht=scontent-los2-1.xx&tp=7&oh=abec7b3bd8ddc13b6c3c0c510a33d8dc&oe=60997696',
        url: 'https://github.com/ShivamJoker/sample-songs/raw/master/Solo.mp3',
    },

    {
        id: 6,
        title: 'Death Bed',
        ArtistName: 'Omowunmi feat Wizkid',
        genre: 'Afrocentric',
        picture: 'https://scontent-los2-1.xx.fbcdn.net/v/t1.6435-0/s640x640/169534769_1185223728571457_6192837830233317030_n.jpg?_nc_cat=101&ccb=1-3&_nc_sid=9267fe&_nc_eui2=AeH2splO8pf4k-atqrUeWWApKjkWnAsELXsqORacCwQte9doNY5rNtBrWht-o_CYYR4&_nc_ohc=plbZA_Pv91EAX_quQhG&_nc_ht=scontent-los2-1.xx&tp=7&oh=abec7b3bd8ddc13b6c3c0c510a33d8dc&oe=60997696',
        url: 'https://github.com/ShivamJoker/sample-songs/raw/master/death%20bed.mp3',
    },

    {
        id: 7,
        title: 'Hero',
        ArtistName: 'Bethany Dilon',
        genre: 'pop',
        isRequire: true,
        picture: 'https://scontent-los2-1.xx.fbcdn.net/v/t1.18169-9/16195530_10211997136709517_8578854309931959016_n.jpg?_nc_cat=101&ccb=1-3&_nc_sid=09cbfe&_nc_eui2=AeEvt5zlNj1bM87SMIgRXz8VjFbfh8f8mfyMVt-Hx_yZ_ISR6pzt6j1tOqssNCwDfnM&_nc_ohc=oQeQeYLPRz8AX_n81Yh&_nc_ht=scontent-los2-1.xx&oh=d87b3097c543a39067095bacfbeb004d&oe=609BF1DC',
        url: require('../../assets/songs/Hero.mp3'),
    },

    {
        id: 8,
        title: 'Advertising URL',
        ArtistName: 'Bethany Dilon',
        genre: 'Soft Rock',
        picture: 'https://scontent-los2-1.xx.fbcdn.net/v/t1.6435-0/s640x640/169534769_1185223728571457_6192837830233317030_n.jpg?_nc_cat=101&ccb=1-3&_nc_sid=9267fe&_nc_eui2=AeH2splO8pf4k-atqrUeWWApKjkWnAsELXsqORacCwQte9doNY5rNtBrWht-o_CYYR4&_nc_ohc=plbZA_Pv91EAX_quQhG&_nc_ht=scontent-los2-1.xx&tp=7&oh=abec7b3bd8ddc13b6c3c0c510a33d8dc&oe=60997696',
        url:
            'https://raw.githubusercontent.com/zmxv/react-native-sound-demo/master/advertising.mp3',
    },

    {
        id: 9,
        title: 'Stronger',
        ArtistName: 'Bethany Dilon',
        genre: 'Country',
        isRequire: true,
        picture: 'https://scontent-los2-1.xx.fbcdn.net/v/t1.6435-0/s640x640/169534769_1185223728571457_6192837830233317030_n.jpg?_nc_cat=101&ccb=1-3&_nc_sid=9267fe&_nc_eui2=AeH2splO8pf4k-atqrUeWWApKjkWnAsELXsqORacCwQte9doNY5rNtBrWht-o_CYYR4&_nc_ohc=plbZA_Pv91EAX_quQhG&_nc_ht=scontent-los2-1.xx&tp=7&oh=abec7b3bd8ddc13b6c3c0c510a33d8dc&oe=60997696',
        url: require('../../assets/songs/Stronger.mp3'),
    },

    {
        id: 10,
        title: 'Faded',
        ArtistName: 'Luchee',
        genre: 'Techno',
        picture: 'https://scontent-los2-1.xx.fbcdn.net/v/t1.6435-0/s640x640/169534769_1185223728571457_6192837830233317030_n.jpg?_nc_cat=101&ccb=1-3&_nc_sid=9267fe&_nc_eui2=AeH2splO8pf4k-atqrUeWWApKjkWnAsELXsqORacCwQte9doNY5rNtBrWht-o_CYYR4&_nc_ohc=plbZA_Pv91EAX_quQhG&_nc_ht=scontent-los2-1.xx&tp=7&oh=abec7b3bd8ddc13b6c3c0c510a33d8dc&oe=60997696',
        url: 'https://github.com/ShivamJoker/sample-songs/raw/master/Faded.mp3',
    },

    {
        id: 11,
        title: 'Solo',
        ArtistName: 'Solo Cosmos',
        genre: 'Afrobeat',
        isRequire: true,
        picture: 'https://scontent-los2-1.xx.fbcdn.net/v/t1.6435-0/s640x640/169534769_1185223728571457_6192837830233317030_n.jpg?_nc_cat=101&ccb=1-3&_nc_sid=9267fe&_nc_eui2=AeH2splO8pf4k-atqrUeWWApKjkWnAsELXsqORacCwQte9doNY5rNtBrWht-o_CYYR4&_nc_ohc=plbZA_Pv91EAX_quQhG&_nc_ht=scontent-los2-1.xx&tp=7&oh=abec7b3bd8ddc13b6c3c0c510a33d8dc&oe=60997696',
        url: 'https://github.com/ShivamJoker/sample-songs/raw/master/Solo.mp3',
    },

    {
        id: 12,
        title: 'Death Bed',
        ArtistName: 'Omowunmi feat Wizkid',
        genre: 'Afrocentric',
        picture: 'https://scontent-los2-1.xx.fbcdn.net/v/t1.6435-0/s640x640/169534769_1185223728571457_6192837830233317030_n.jpg?_nc_cat=101&ccb=1-3&_nc_sid=9267fe&_nc_eui2=AeH2splO8pf4k-atqrUeWWApKjkWnAsELXsqORacCwQte9doNY5rNtBrWht-o_CYYR4&_nc_ohc=plbZA_Pv91EAX_quQhG&_nc_ht=scontent-los2-1.xx&tp=7&oh=abec7b3bd8ddc13b6c3c0c510a33d8dc&oe=60997696',
        url: 'https://github.com/ShivamJoker/sample-songs/raw/master/death%20bed.mp3',
    },
    
  ];

  export default songs;
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Benjamin Ikwuagwu
  • 377
  • 1
  • 9
  • 28

1 Answers1

1

So, the thing here is that everything is inside the same component and when you run a setData or setQuery you update the whole component, and your keyboard is reseted.

About your list not been updated, it seems to be a small typo on your code:

return (
  <SafeAreaView style={styles.container}>
    <FlatList
      // data={songs} <-- here you should be using data
      data={data} // something like this
      renderItem={renderItem}
      keyExtractor={item => item.id}
      ItemSeparatorComponent={renderSeparator}
      ListHeaderComponent={renderHeader}
      ListFooterComponent={renderFooter}
    />
  </SafeAreaView>
);

But you would also need to update your state declarations as well so you don't start with an empty list, from this:

const [data, setData] = useState([]);

to this:

const [data, setData] = useState(songs);

Ok, this should solve the no filter on the list as now you will be using the variable you set on your filter function, but the problem about your keyboard dismissing will keep happening as you update your whole component each text change. Here's a good solution in my opinion:

Use some sort of global state management (Eg.: Context or Redux, you could also achieve this solution with mobx) this way you can create a separated component called <ListHeader /> for example and put your query update there and update the list value on the global state, this will not cause a full reload on your component, just the list will update and you can keep the field focus, maybe it's a new concept for you so I'm sharing an example using Context here:

Working Example:

https://codesandbox.io/s/contextexample-p28z5?file=/src/App.js

Static Code:

    import React, { useContext, useState } from "react";
import { Text, View, FlatList, TextInput } from "react-native";

const data = [
  { name: "qweqwe" },
  { name: "aaaaaa" },
  { name: "eeeeeeeee" },
  { name: "4" }
];

// List Context and ListProvider can be togheter in the same file
const ListContext = React.createContext({
  list: [],
  setList: () => {}
});
const ListProvider = ({ children }) => {
  const [list, setList] = useState(data);

  return (
    <ListContext.Provider value={{ list, setList }}>
      {children}
    </ListContext.Provider>
  );
};

const Item = ({ item }) => {
  return (
    <View>
      <Text>{item.name}</Text>
    </View>
  );
};

const Header = () => {
  const [text, setText] = useState("");
  const listContext = useContext(ListContext);

  const updateQuery = (str) => {
    listContext.setList(data.filter((d) => d.name.indexOf(str) > -1));
    setText(str);
  };

  return (
    <View>
      <TextInput value={text} onChangeText={updateQuery} />
    </View>
  );
};

const ListScreen = () => {
  return (
    <ListContext.Consumer>
      {(context) => (
        <View style={{ flex: 1 }}>
          <FlatList
            data={context.list}
            keyExtractor={(i) => i.name}
            renderItem={({ item }) => <Item item={item} />}
            ListHeaderComponent={Header}
          />
        </View>
      )}
    </ListContext.Consumer>
  );
};

const App = () => {
  return (
    <ListProvider>
      <ListScreen />
    </ListProvider>
  );
};

export default App;

Wish success on your project.

  • Thanks so much Luis, your solution worked. But am having problem with my ListContext.Provider in App.js, because my main code to display the list is in a different file, am just using App.js to display the screen. – Benjamin Ikwuagwu Apr 19 '21 at 01:11
  • Good to know the code helped, you could put de provider on app.js and just use the `Consumer` anywhere down in the code, also you don't nee to put the state management on the App.js itself, I'll update my answer so it use an other component to be the state managment, maybe this can help you to implement on your project. – Luís C Meireles Apr 19 '21 at 03:10
  • Ok, I've updated the code example, now you have the `ListProvider` it's a component responsible for handling the context, you just need to wrap your main component with this provider (like the App component in the example) and you will be able to use the Consumer anywhere in your code. Hope it helps. – Luís C Meireles Apr 19 '21 at 03:17
  • I really appreciate you kind response. Am getting this: ERROR ReferenceError: Can't find variable: ListProvider in my App.js file. Here's my App.js code: – Benjamin Ikwuagwu Apr 19 '21 at 09:35
  • ... ... import { ApplicationProvider } from '@ui-kitten/components'; import { mapping, light as lightTheme } from '@eva-design/eva'; import Playerlist from './src/Screens/Playerlist'; const App = () => { return ( ); }; – Benjamin Ikwuagwu Apr 19 '21 at 09:36
  • ...so all my code is Playerlist.js So I imported Playerlist.js into my App.js and I used , but am now getting ERROR ReferenceError: Can't find variable: ListProvider – Benjamin Ikwuagwu Apr 19 '21 at 09:41
  • You will need to import your ListProvider as well, if it is in the same file Playerlist.js, don't forget to export it there like: `export const ListProvider = ({childr......` and then import the ListProvider in your App.js something like this `import Playerlist, {ListProvider} from './src/Screens/Playerlist'` – Luís C Meireles Apr 19 '21 at 18:14
  • Yes, it's worked not showing Reference error again. You're awesome! Now, it's giving me Warning: Each child in a list should have a unique "key" prop. And when I type on keyboard, I get ERROR TypeError: undefined is not an object (evaluating 'd.name.indexOf'). Please forgive me that am asking too much questions on this code. Thanks. – Benjamin Ikwuagwu Apr 20 '21 at 13:11
  • About the child kay warn, did you applied a `keyExtractor` on the List component? maybe you need to validate if the property exist, or using something different like `keyExtractor={(i, key) => 'item-' + key}` – Luís C Meireles Apr 20 '21 at 21:52
  • On the second error, you need to make sure that the properties you are using to search exist on your objects. If you are not able to find the problem on this one you can open a new issue with your code so we can take a look, because maybe it's another problem. – Luís C Meireles Apr 20 '21 at 21:54
  • I've opened a new issue on it, please here's the link https://stackoverflow.com/q/67188036/5029101 – Benjamin Ikwuagwu Apr 21 '21 at 01:01