
No Comments Yet
Be the first to share your thoughts and start the conversation.
Be the first to share your thoughts and start the conversation.
How did you manage to remove the blur property and reach here?
Upgrading gives you access to quizzes so you can test your knowledge, track progress, and improve your skills.
By logging in, you'll unlock full access to this and other free tutorials on JSM Pro.
Why? Logging in lets us personalize your learning experience, track your progress, and keep you in the loop with new workshops, coding tips, and platform updates.
You'll also be the first to know about upcoming launches, events, and exclusive discounts.
No spam—just helpful content to level up your skills.
If that sounds fair, go ahead and log in to continue →
Enter your name and email to get instant access
In this lesson, we explore the process of developing a search screen in a React Native application. By leveraging a custom hook to fetch movie data and implementing a flat list for rendering, we ensure an efficient and functional interface that reflects user input in real-time.
FlatList
for rendering movie items while ensuring the search bar stays visible at the top.useFetch
is employed to retrieve movie data, enabling dynamic updates based on user queries.ListEmptyComponent
prop to show meaningful messages when no search results are found, promoting better user engagement.00:00:02 Similar to what we did on the home screen, now is the time to develop the search screen.
00:00:07 So let's open up our file explorer and head over into tabs, search.tsx.
00:00:13 Now we could totally just copy the entire index.tsx and just paste it over into the search.
00:00:21 but I want to take the time to guide you through the process of building it out one more time, because although it'll be very similar,
00:00:29 there will be some differences here, and it's just a beautiful chance to recap everything we've learned so far, just to make sure you totally get it.
00:00:37 I've made the font a bit smaller, just so you can nicely see everything, and we are ready to get started.
00:00:44 First, this view that we're wrapping everything with will have a class name of flex-1 and bg-primary, just so we don't have to stare at that blank screen anymore.
00:00:57 Next, within it, I'll display another image, which I'll import from React Native.
00:01:03 And it'll have a source equal to images coming from constants, .bg, so this is our background, with a class name of flex-1,
00:01:15 absolute, w-full, and z of zero, as well as a resize mode equal to cover.
00:01:24 similar to as on the homepage.
00:01:26 Next, in this case, instead of wrapping everything with the scroll view as we did on the index, here, we don't want to scroll the entire view because the
00:01:36 search bar always has to remain on top.
00:01:38 So here, I will actually just render a flat list.
00:01:43 pass the movies to it, and pass the similar render item as before, where we will destructure the each individual movie, and for each one,
00:01:53 we'll simply render a movie card, to which we will spread the item property, or in this case, the movie itself.
00:02:03 So let me put these props in a new line so it's easier to see.
00:02:06 And we are rendering this movie card right here.
00:02:09 There we go.
00:02:10 Now, of course, where are these movies coming from?
00:02:13 Well, this part we can definitely grab from the index.
00:02:16 We're getting them by simply calling our useFetch, fetchMovies.
00:02:22 This is a custom hook we already created.
00:02:24 We might as well grab this useRouter at the top.
00:02:26 We'll surely need to use it.
00:02:28 And I'll paste it right here at the top of our search.
00:02:32 Make sure to also get all the imports, such as this movie card right here.
00:02:36 And now, if we go back to the search, you'll see we have, well, something, right?
00:02:42 A broken layout so far.
00:02:43 Next, as before, we need a key extractor to let React Native...
00:02:48 Which ID does each one of these elements have?
00:02:52 And here we can just say that each one will get it from item.id.toString.
00:03:00 We'll also render a class name and give it a padding X of five.
00:03:06 So some spacing.
00:03:08 A number of columns will be set to three as before, and we have to reload our application for the changes to take effect.
00:03:16 There we go.
00:03:16 This is a bit better.
00:03:18 As well as we can provide the column wrapper style, which will be an object that'll say justify content is equal to center.
00:03:28 A gap of 16 in between the elements.
00:03:31 as well as a margin vertical to create some space for the search bar of 16 as well.
00:03:38 We can also render the content container style as before, which will simply have a padding bottom of something like 100. And now bear with me.
00:03:51 Before, we displayed the search bar at the top of the flat list, because we were able to scroll the whole thing, we didn't need to see the search bar.
00:04:00 But in this case, we'll render the search bar as a part of the flat list.
00:04:05 So...
00:04:06 I'll do that with yet another special FlatList prop called ListHeatherComponent.
00:04:13 I'll open up an empty ReactFragment and whatever you put within it will be displayed at the top of our list.
00:04:21 So let me say something like view.
00:04:25 And this view will have a class name of w-full, flex-row, justify-center, margin-top of 20, and items-center.
00:04:39 And within it, we can display our logo.
00:04:41 That'll be an image with a source equal to icons.
00:04:46 Make sure to import them from icons constants.
00:04:50 dot logo with a class name of W12 and H10.
00:04:56 So now we get the logo at the top.
00:04:58 As I promised, whatever you put within the list header component, even though it's just a part of the flat list, it'll actually show on top as that's what
00:05:06 this component does, rendered at the top of all the items.
00:05:09 Now below the view that's wrapping the image, we can render another view.
00:05:15 This one will have a class name.
00:05:18 equal to margin Y of five.
00:05:21 And here we can render the search bar coming from components search bar.
00:05:27 And we can give it a placeholder of something like search movies dot dot dot.
00:05:33 There we go.
00:05:34 It appeared right on top, right below the view.
00:05:36 We can also check whether we are currently loading.
00:05:39 So I'll say something like movies loading.
00:05:42 And if movies are loading, then we can show an activity indicator.
00:05:48 with a size is equal to large, a color is equal to hash 0000FF, and a class name equal to marginY of three.
00:06:02 So now we have to get access to this movies loading.
00:06:06 And we're getting it right here through this useFetch call, but I have to spell it properly.
00:06:12 The L in loading is uppercased.
00:06:14 Now, if we go back.
00:06:17 If you're paying close attention, you would have been able to see the loading for a short period of time.
00:06:23 But if we don't have a loading, then it is possible that we might have some kind of an error.
00:06:28 So I'll say if movies error, in that case, we can render just a single piece of text that'll have a class name of text-red-500,
00:06:40 padding X of 5 and margin Y of 3. And the text will say error.
00:06:49 And then we'll render the movies error dot message.
00:06:54 No errors for now.
00:06:55 And finally below it, I'll open another dynamic block of code.
00:07:00 And if we're neither loading nor there is an error, then we can take a look into the current search term.
00:07:08 Let's say that the search term right now is hard coded search term.
00:07:12 dot trim, so we want to trim any extra spaces.
00:07:16 And if the search query exists as well, we want to check if there are any movies dot length.
00:07:21 So movies question mark dot length for that search term.
00:07:25 And if that is greater than zero, in that case, we can finally return a piece of text.
00:07:32 And this piece of text will say, search results for.
00:07:38 We can put an empty string right here and then render another piece of text that'll say search term.
00:07:46 And I'll style the second piece of text by giving it a text-accent class name.
00:07:51 And I'll style the first piece of text by giving it a class name of text-excel, text-white.
00:08:01 Oh, and now I see that I call this just loading an error instead of movies errors and movies loading.
00:08:07 So you know what?
00:08:08 It might be just simpler to call it like that.
00:08:10 It makes sense that we're loading the movies.
00:08:13 So I'll just not rename the loading and the error to loading movies and loading error.
00:08:18 I'll just keep them as loading and error.
00:08:21 So here I can now change the movies loading to loading and error to error.
00:08:27 Same thing right here.
00:08:29 And now this message right here makes sense.
00:08:32 If there is no loading and no error and search term exists, and if movies that length is greater than zero, well, then display this piece of text.
00:08:42 So now if we go back.
00:08:45 You can see search results for search term.
00:08:49 So let's make it dynamic.
00:08:51 Right at the top of this component, I'll create another use state.
00:08:56 We don't need the router in this case.
00:08:59 So let me delete it.
00:09:00 And let me define a use state snippet called search query, set search query at the start equal to an empty string.
00:09:09 Also, when calling these movies, In this case, I want to pass a second parameter to the useFetch function of false, because in this case,
00:09:20 we don't want to autofetch.
00:09:23 We just want to let the user actually trigger the fetch on search.
00:09:27 So that's why we initially extended this custom hook to allow for autofetch when we're on the homepage, or to disallow it when we want the user to search first.
00:09:38 And of course, we can now put the search query to use.
00:09:41 And say, give me the movies where the query matches the query that the user currently searched for.
00:09:47 So now that we have access to the search query, right below, we can replace this big search term with search query dot trim.
00:09:57 And also below where we're saying search term, we can render the actual search query.
00:10:03 So now if I open up my keyboard by clicking on the list and I start typing, you'll see nothing is yet changing.
00:10:11 And that's because we haven't passed proper values into the search bar.
00:10:15 So to the search bar, alongside the placeholder, we also have to pass the value.
00:10:24 equal to search query as well as onChangeText equal to a callback function where we simply set query or setSearchQuery to be equal to text that is coming
00:10:38 right here through props.
00:10:40 And we can set the type of this search to a string.
00:10:44 But as you can see, the search bar is still not accepting the value prop.
00:10:48 So let's head into it and let's implement it.
00:10:51 I'll simply say also accept the value and the value will be of a type string.
00:10:56 Once you do accept it, simply forward it here, value, and also forward the onChangeText.
00:11:03 I believe that's how we called it, onChangeText.
00:11:06 So it'll be a function that doesn't return anything, void.
00:11:12 And now we can make that onChangeText right here.
00:11:16 instead of this dummy callback function.
00:11:20 If we now do that, looks like I didn't properly define the type.
00:11:24 Here, we're getting something as the first parameter, and it is text of a type string.
00:11:31 So if I fix that, you can see that the error goes away.
00:11:35 And now, if I start typing something like Batman, you can see that the search results change immediately right here below.
00:11:44 I can type Marvel, that works as well.
00:11:47 It works incredibly well.
00:11:49 And as soon as I delete it, the search results go away.
00:11:52 Great.
00:11:53 But finally, what matters most is rendering the movies that match that search term.
00:11:59 So I'll create a new useEffect right here below.
00:12:04 This useEffect will have a callback function and a dependency array.
00:12:10 We want to recall it every time that the search query changes.
00:12:15 So I'll say if searchQuery So if there is something that the user has searched for, then await, and as a return value of the fetch,
00:12:27 we're also getting access to the refetch function, which we can rename to load movies because that's exactly what it does.
00:12:35 And also we get access to reset.
00:12:38 So now if there is a search term, whenever the search query changes, just await load movies like this, else simply reset the state.
00:12:50 And since we're using await right here, we'll have to wrap this into an asynchronous function.
00:12:56 So I'll say const func.
00:12:59 And I'll make it equal to an asynchronous function that then has this if within it, that'll look something like this.
00:13:06 And now we can call this function just below.
00:13:08 If I do that, initially you'll see no movies.
00:13:12 But if I search for iron.
00:13:15 You can see all the movies that match that search term.
00:13:18 And if you start deleting it, you can see that we get back to, well, I think we got back to whatever the letter was there before we actually removed it.
00:13:28 So this is the letter I movies.
00:13:30 And then, yeah, if we remove it, we have nothing.
00:13:33 Now, there's a couple of problems with this, and that is that let's say that I want to type a normal movie name, like Iron Man.
00:13:41 What happened here is that the app made a request to the API for every single letter that I type.
00:13:51 That is a lot of different requests.
00:13:55 And for each one, we're getting many movies, but hey, I just wanted to see the ones that I get back once I typed the entire search term.
00:14:02 So to not overload our application with too many API requests for every single keystroke, what we need to do is something called debouncing.
00:14:13 We can debounce a search term by wrapping it into a timeout function.
00:14:19 So I'll say const timeout ID is equal to set timeout.
00:14:28 And then we have this function and we can close it here as well.
00:14:33 And in this case, you don't have to call it, but we do have to give it a second parameter, which is a number of milliseconds of how long it'll wait for
00:14:41 the next keystroke.
00:14:43 I'll explain what that means soon.
00:14:46 And now we can return a callback function where we can clear the timeout with that specific timeout ID.
00:14:56 This is just to make our application work more efficiently.
00:14:59 We need to clear out all the timeouts.
00:15:01 But with that said, check this out.
00:15:04 If I type very quickly, You'll see that as long as I continue typing, no requests will be made.
00:15:11 But as soon as I stop typing for 500 milliseconds, which is half a second, immediately I'll get the results.
00:15:17 This means that a user that types fast can only make a single request and get the data they would usually get as well, but this time in a single request
00:15:26 instead of a dozen.
00:15:28 Great.
00:15:29 But now, what happens if I search for something that just doesn't exist?
00:15:34 We're left with a completely empty screen.
00:15:38 Something that you never want to see within your application.
00:15:41 You always want to show some kind of an empty state.
00:15:44 And believe it or not, once again, I think you can now start seeing what I was saying from the start.
00:15:51 In React Native Components, there is a prop for everything.
00:15:56 From the data, render item, key extractor, to defining how a header will look like, to even defining how an empty state will look like.
00:16:07 Yep, ListEmptyState is a valid flat list prop that allows us to specify what the user will see if there's nothing in the list.
00:16:17 So I'll say if there is no loading, And if there is no error in that case, and of course, if we're empty, we'll show a view with a class name.
00:16:31 equal to margin top of 10 and padding X of 5, a text element with a class name of text-center and text-gray 500. And within it,
00:16:45 we can say search query dot trim.
00:16:48 If the search query exists, we'll say no movies found.
00:16:53 But if a search query doesn't exist, we'll say search for a movie.
00:16:59 And of course, if there is a loading on error, we'll just return null for the empty list component because we want to display something else.
00:17:10 So currently, we get no movies found.
00:17:14 And if you wanted to, you could display a nice illustration right here and say, you know, go ahead and search for something cool or Here's a movie recommendation
00:17:23 that you might want to watch based on your history.
00:17:26 There's a lot of things you can do on empty states, but hopefully the users won't be staring at this for too long as they're going to put our search to use.
00:17:35 Great.
00:17:35 So in this lesson, we recapped what it means to create a React Native screen that uses our custom hook, fetches data from a third-party API.
00:17:48 changes it based on the search query that the user types in using their keyboard, as well as how to debounce a search term to optimize it further and not
00:17:58 make too many requests.
00:17:59 And finally, how to use a flat list component with all of its props to properly render the layout.
00:18:05 Doesn't get much better than this.
00:18:06 Now, in the next lesson, we'll bring our app to the next level.
00:18:11 We'll implement an algorithm that displays trending movies.
00:18:16 So I'm not talking popular or searched, but rather show the movies that match what the users within our app are searching for.
00:18:27 That can kind of give you an idea of who your users are and how we can further customize the app.
00:18:33 So let me show you how to implement it in the next lesson.