Filter Photos in React

To run the sample, first make sure you install the backend node server that will give you the images:

Node backend download

Then install the front end, which is what this tutorial is about:

React project download

Create the React App


create-react-app photo-filter
cd photo-filter
npm start

Import Theme

Under src, create img folder. Then copy avatar.jpg and bg.jpg into that folder.

App.css
In the visualize template, copy the CSS from main.css into your App.css:

– remove the @import of awesome font at the very top of the
– Then look for background-image and replace url with:

this is so that we reference the imported image.

Under src, create components folder. Then under components folder, create ui folder. In ui, create Header.js

Let’s make use of hooks in our app so that we can fetch the data text from our backend.

App.js

Once you’re done, you should be able to see the background and avatar image appear. You should also see the logged data retrieved from the backend.

Import Redux

npm i redux@4.0

Creating Reducer
Now that we know how actions are put together, we need to write a function in order to decide how we going to properly receive these actions.

In src, create a store folder.

Know that actions is what drives the store to keep your data.

It looks like this:

So as you can see, it’s basically an object with properties type and payload.

Create a file called action.js

We first create macros for actions that will be happening, such as fetching photos, error, fetching finished, etc.
When then go on to create action objects. As mentioned, each action has properties type and payload
So we create action objects for when getting photos start, finished, runs into an error, etc.

Now create a new file called reducer.js.

A reducer is a function with parameters state and action. The role of the reducer is to return new state, based on incoming actions.

We first have our state reference a default object with properties:
– photoData
– photoLoading
– photoError

This is the default of our photo array.

We then take care of all the actions objects that will be passed into our photoReducer.

reducer.js

Finally create our store. We stick our reducer as a function parameter so the store knows how to keep data.
The reducer has been defined to take certain actions. These action objects will be used in our main file to apply certain actions to the store.

src/store/store.js

Storing and getting data in our App

We import the store in our app. We import action objects and pass them as parameter to dispatch these actions to the store. Before fetching data from our API, we let the store know we’re going to do that.
In turn, the store will update its isLoading property to true. Thus, any other place that needs to show a load image will depend on this isLoading property.

Once the data fetch is complete, we dispatch action requestPhotosFinishAction, which updates the store’s state object photoData property to the data, and flips isLoading to false.

App.js

Displaying the Data

Displaying the data requires us to write a component that loops through the data. Then another component that simply outputs the UI.

We first create a PhotoGrid that takes the array of photos and use map on it. The map() method creates a new array with the results of calling a provided function on every element in the calling array. In other words, map() calls a provided callback function once for each element in an array, in order, and constructs a new array from the results.

Thus, in this way, map() does not modify the original array. It creates a new array, take in the custom modifications, and apply those changes onto the new array. Then returns it to the JSX to render.

PhotoGrid.js

Notice the main job PhotoGrid does is to map the items. It uses another component PhotoItem that solely does the UI rendering.

PhotoItem.js

PhotoItem is straightforward as it only displays the data.

So in App, we need to set our state after receiving the data from the store. We set the image data array, and then the isLoading property.

When the state is set, it will trigger a render. The render will process PhotoGrid, which then renders our images onto the screen.

App.js

Filtering the Images

First we create a Search component

src/components/ui/Search.js

This components is a controlled component. Which means its input control has its own value. We connects its value to our state and keep tract of it there. This state comes from the parent component App. We then update the parent component’s state by executing a callback function whenever a letter is typed. That way we pass the query text up to the parent component to be processed.

When we receive updated query, we simply set the control text, and set query. This will drive the app to re-render.
src/App.js

Filtering using AVL tree

For filtering, we will use an AVL tree for data structure. It filters data for you according to text that you input. It finds all the urls with the text that you give it. Then return them as an array for you to use.

Under src, create custom folder. Then drag AVLtree.js and BadHeuristic.js into it.

Then in App.js, import it:

we first define useEffect. Basically it runs when the DOMS has been updated with its batch of UI updates. It acts as componentDidMount, and componentDidUpdate. So whatever effect we put in here will be run after a render, and in the beginning with the component mounts.

Let’s then define fetchData function to fetch data.

We dispatch action to the store to let it know that we’re starting to retrieve data. We then use built-in fetch to get the data. After receiving the data, we dispatch a finish action to the store and pass in the resulting array to store it.

We then use the hooks to set the state.

Notice that every setState call will trigger a re-render. And a useEffect will be paired with a render. This will create an infinite loop. In order to solve this, we just test to see if the item length and query is empty. If they are, then we retrieve from online API.

If there is existing items or if there is a query to filter, then we DO NOT fetch data. We simply filter our existing data and reset the items.

Take note that the AVL tree class itself will return an array of objects, which houses various data. Our state object property items take in an array of urls (strings). So after we filter the data from the tree, we need to extract just the urls and place them into another array. Then setItems of that array of urls.

Now you can type in text, it uses that text and filters all the image titles with that text from the tree. It puts all the search results into an array and returns it for your UI to render.