DEV Community

Paramanantham Harrison
Paramanantham Harrison

Posted on • Originally published at learnwithparam.com

Refactor the book search app and separate components, Part 3

Until part 2, we have created a book search app and handled the loading and error state. Now we are going to see how we can split the app structure into components.

We have written the whole app in App.js. It's still a small example, so there is no real need to split it into folders. This post will just showcase how to split the large applications without causing a mess to debug later on.

There are no common best practices for folder structure, it depends mainly on two factors

  • How big is the project?
  • How large is your team?

For large projects and large teams, feature or domain based folder structure will work well.
For small projects, file type based folder structure will work easily.

You can read more about it in react docs here

My personal opinion is to keep it simple, flat and scale only when you need. You should always refactor to sophisticated folder structure when each file grows in length.

Let's move on to the code,

In our books search application, we can create these components

  • BookSearchForm
  • Loader
  • BooksList
  • Books

Let's create a component folder and also create three JS files for our component.

// booksSearchForm.js
import React from 'react';

const BookSearchForm = ({
  onSubmitHandler,
  searchTerm,
  onInputChange,
  error,
}) => {
  return (
    <form onSubmit={onSubmitHandler}>
      <label>
        <span>Search for books</span>
        <input
          type="search"
          placeholder="microservice, restful design, etc.,"
          value={searchTerm}
          onChange={onInputChange}
          required
        />
        <button type="submit">Search</button>
      </label>
      {error && (
        <div style={{ color: `red` }}>
          some error occurred, while fetching api
        </div>
      )}
    </form>
  );
};

export default BookSearchForm;
Enter fullscreen mode Exit fullscreen mode

We have split the component into its own file and pass the necessary functions and state values as props.

Now in App.js

// App.js
import React, { useState } from 'react';
import axios from 'axios';

import BookSearchForm from './components/bookSearchForm';
import './App.css';
...

const App = () => {
  ...
  return (
    <section>
      <BookSearchForm

        onSubmitHandler={onSubmitHandler}
        onInputChange={onInputChange}
        searchTerm={searchTerm}
        error={error}
      />
      {
        loading && <div style={{color: `green`}}>fetching books for "<strong>{searchTerm}</strong>"</div>
      }
      ...
    </section>
  )
}
Enter fullscreen mode Exit fullscreen mode

Lets split the other components as well in the same way.

// Loader.js
import React from 'react';

const Loader = ({ loading, searchTerm }) => {
  return (
    <>
      {loading && (
        <div style={{ color: `green` }}>
          fetching books for "<strong>{searchTerm}</strong>"
        </div>
      )}
    </>
  );
};

export default Loader;
Enter fullscreen mode Exit fullscreen mode

As for BooksList and Book component, I didn't split into files and put it in the same file. Will split it when the functionality grows.

// booksList.js
import React from 'react';

// Separate the UI specific transforming logic to utils folder
import { bookAuthors } from '../utils';

const Book = ({ book }) => {
  return (
    <li>
      <div>
        <img
          alt={`${book.volumeInfo.title} book`}
          src={`http://books.google.com/books/content?id=${
            book.id
          }&printsec=frontcover&img=1&zoom=1&source=gbs_api`}
        />
        <div>
          <h3>{book.volumeInfo.title}</h3>
          <p>{bookAuthors(book.volumeInfo.authors)}</p>
          <p>{book.volumeInfo.publishedDate}</p>
        </div>
      </div>
      <hr />
    </li>
  );
};

const BooksList = ({ books }) => {
  return (
    <ul>
      {books.items.map((book, index) => {
        return <Book book={book} key={index} />;
      })}
    </ul>
  );
};

export default BooksList;
Enter fullscreen mode Exit fullscreen mode

And add all these together in App.js

return (
  <>
    <BookSearchForm
      onSubmitHandler={onSubmitHandler}
      onInputChange={onInputChange}
      searchTerm={searchTerm}
      error={error}
    />
    <Loader searchTerm={searchTerm} loading={loading} />
    <BooksList books={books} />
  </>
);
Enter fullscreen mode Exit fullscreen mode

That's it, folks, we have successfully split all our components. We can optimize it further by moving around states. That's for the next part.

We will see,

  • how to manage state and
  • Different ways to manage state (custom hooks, useReducer)
  • why we are managing all state in App.js instead of the components itself in more detail

Checkout the codebase for this part 3 here and the whole series codebase can be referred here.

Stay in touch!

If you enjoyed this post, you can find me on Twitter for updates, announcements, and news. 🐤

Top comments (1)

Collapse
 
kira679v profile image
kira679v

I also like to read something new and prefer to download books in order to read a lot of new information. Recently, I was using pdfmania.com/ which is a pretty decent service where are represented a lot of various effective solutions in order how to download the book, what is the most interesting option and it is a good service.