BookCritiq: A React Project
Tags: javascript, webdev, react
Published: 2021-10-22 04:00:40

Cover Photo by <a href="https://unsplash.com/@ugur?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Ugur Akdemir</a> on <a href="https://unsplash.com/s/photos/books?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>


  I have made it! This is my final project for the Flatiron School software engineering bootcamp! I was so excited to start this project, I flew through the last section of the course. Partially because I know from my experience building all of my previous projects that project mode is where I learn the most. And React is a handful - I did *so* much googling and reading and researching.

  I landed on the idea of creating a book review app for this project after sifting through some other ideas that turned out to be a bit complicated for a first foray into building with React. The basic idea is to create a space where people can find or add a book in the database, then add their review to it. My MVP requires just the book and review models, but I built out my Rails API back end with the full app in mind -- I used the Rails scaffold generator to spin up four models: User, Book, Review, and Comment (no test framework, please). This was so quick that it felt like cheating. I added some books and a couple of reviews to the seed file, sorted out model associations, and set up my serializers.

> A note on serializers here. I used `fast-jsonapi` for this project because I was familiar with it. I *highly* recommend that you use a different serializer like ActiveModel Serializer, because fast-jsonapi nests your serialized data inside a data object, which makes your data more complicated to work with on the front end. I spent a lot of time console-logging and sorting out how to access the pieces of data I wanted, and using a less deeply nested set of data would have made things a lot easier.

After playing around with creating and relating objects and seeing what I could access at different endpoints, I moved on to the front end.

I created a boilerplate react app using the `create-react-app` command. I then spent a few minutes removing the bits I wasn't going to need. Then I tackled adding some dependencies. The project requirements state that I have to make use of Redux and Thunk, and I knew I wanted to tackle adding styling with bootstrap, as well as handling client-side routing with React Router, so I added those dependencies with npm.

```
npm install redux
npm install react-redux
npm install redux-thunk
npm install bootstrap
npm install react-bootstrap
npm install react-router-dom
```

The next step was to tackle setting up my App component, which I refactored from the boilerplate functional component to a class component. I then moved over to the Index component and set up the Redux store.

```js
import React from 'react';
import ReactDOM from 'react-dom';
import {createStore, applyMiddleware, compose} from 'redux';
import thunk from 'redux-thunk';
import {Provider} from 'react-redux'
import './index.css';
import App from './App';

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

// store is where data is stored globally, reducers tell us what to do with our store based on certain actions.
let store = createStore(booksReducer, composeEnhancers(applyMiddleware(thunk)));


ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);
```

And then create a reducer:

```js
export default function booksReducer(state = {books: []}, action) {
  return state;
}
```

This is where the app will process actions and modify the Redux store. 

With the store and reducer set up, I moved on to creating some book components. I decided that the main component will be the BookContainer, which will render BooksList, BookCreateForm, and BookShow components. BooksList contains links to all of the books, BookShow renders a single book, and BookCreateForm contains a controlled form for creating a new book. 

> I ran into some trouble getting the correct book to render on the BookShow component. I ended up using the `useParams()` hook that comes built into React to access the id in the URL the user clicked in order to render the component, then used that id to filter out the correct book from state. Using `let book = props.books[props.match.params.id - 1]` selected the book based on its position in the array, and I wanted the id of the book rendered to match the id in the URL, not a position in the props array. The final code I used to achieve this looks like this:

```js
const {id} = useParams(); // This pulls the id value directly from the URL.
let book = props.books.filter(book => book.id === id) // This line filters out the book we're looking for based on the ID in the URL
if (book.length === 0) return null; // check if the book was found. If not, return null (allow time for the fetch to run).
let bookData = (book && book[0].attributes); // This is just to make it so I don't have to say 'book.attributes.property' every time I want to access the book data. Thanks, fast-jsonapi!
```

I followed a flow like this to complete the rest of the project: Create components -> import necessary files -> decide if the component should be a class or functional component -> build out the component -> connect to the store if necessary -> build a new action if necessary -> add a case to the reducer to handle the action if necessary -> connect to other components with React Router if needed -> test everything works. 

![Spongebob Go with the flow](https://media2.giphy.com/media/h8y265b9iKtzKT0pDj/giphy.gif?cid=ecf05e4710zuscoduttqgil6bme708v4wbpxm0m6cs78hf15&rid=giphy.gif&ct=g)

With the Book components set up, I moved on to the Review components. I created a Review container that will be rendered by the BookShow component. The only place reviews will be visible currently is on a book's individual show page. Inside the ReviewContainer component, there are CreateReviewForm and Reviews components. CreateReviewForm contains a controlled form for adding a review to a Book, and the Reviews component is responsible for rendering all of the reviews. 

With the main functionality in place, I moved on to UI design. The first thing I did with bootstrap is add the CDN script to the head of the index.html file. Aside from removing some standard create-react-app code, this was the only time I touched this file. I chose to use the CDN because users may have already come across it and their browsers will already be aware of Bootstrap's styles, which means that the styles for my app will load faster in their browser.

With that set up, I created a Navbar component and added links to the Routes that have been created throughout my app. I then styled it responsively using bootstrap class tags. I'll be honest: I pieced this together based on Bootstrap's documentation examples after a couple of hours of googling and figuring out how to set my own color scheme. 

I found <a href="https://medium.com/swlh/responsive-navbar-using-react-bootstrap-5e0e0bd33bd6">this article</a> to be very helpful for setting up a responsive navbar.

The final component added is a Home component, which serves as a landing page for the app. My final steps were to move through all of my components and apply appropriate styles using bootstrap components and class tags. While I did spend a lot of time learning, implementing the Bootstrap library turned out to be much faster at making the app look appealing than trying to write out the CSS all on my own. 

Overall, I am pleased with the outcome of this project. I am still a little shaky on some of the concepts, but I have a much better grasp on React and Redux (and Bootstrap!) than I did when I started. I am excited to come back to this project after graduation to sort out adding users, authentication, and comments.

If you'd like to check out the repos, you can find the front end <a href="https://github.com/jrrohrer/book-critiq-fe">here</a>, and the back end <a href="https://github.com/jrrohrer/book-critiq-be">here</a>. (And if you do check them out, shoot me a message! I'd love to talk about it, especially if you have suggestions.)