Over the last couple months I've been diving deeper into Rails. Learning how to connect a React frontend to a Ruby on Rails backend was honestly pretty smooth. In building out this application I discovered a couple of weak points in my learning and will be able to course correct easier in the future because of this experience.
A lot of the lessons that I got out of this project revolved around establishing a good order of operations. When you're building a house, you start with foundations, then framework, then plumbing and electric, then finally walls and a roof. I think a lot of my early struggles in building my own apps has stemmed from losing sight of this particular lesson. While it can be difficult to stop and pivot to a different task, it really helps to exercise the brain while learning new things to rotate subjects in and out - set up a database model, seed it, switch to React and build it out, repeat. Going back and forth from setting up a database model to making sure that the frontend will receive the data it needs is incredibly helpful. Setting up your frontend components earlier in the process will also give you a better idea of what kind of data you will need to provide from your database. In my case, it would have helped me decide exactly what keys I would want for my movies model, rather than guessing and adding more information than I really needed.
For this application, I built out a database that included Movies, Rentals, Stores and Users, with Rentals set up as a join table for the other 3 models. When a User starts a rental, the User's id, Movie id and Store id are stored together with a Rental id. When fetching rentals from the backend, we also include the associated movie for easy iteration.
Near the end of my project phase, I was tasked to learn a hook, useContext. I found this hook to be incredibly useful with this application. When you implement useContext, you allow your data to move freely around your component tree, rather than having to send it through a hierarchy to reach the components that access it (often passing through components that don't). This hook potentially saves a lot of time in larger applications with many nested components.
To utilize the useContext hook in this app, I followed these steps:
Create a context folder within my src folders (where both components and context share the src as a parent directory). Inside that folder, I made a file for each of the models that I wanted to use with the useContext hook. In this case, I made a movies.js file and a stores.js file.
Once these files are set up, we add the following code to them:
import React, { createContext, useState } from "react";
const MoviesContext = createContext(null);
const MoviesProvider = ({ children }) => {
const [movies, setMovies] = useState([])
const value = [movies, setMovies]
return (
<MoviesContext.Provider value={value}>
{ children }
</MoviesContext.Provider>)
}
export { MoviesProvider, MoviesContext }
First we import React and createContext as well as any other relevant hooks to the context we are creating. The next line just sets up MoviesContext as our newly created context. This can start either empty or with a null value.
Then we need to create our Provider. The provider acts like a component, in which we can set up things like state and determine what gets returned to the Context Provider when it is called in other components. Our provider has a prop of children because we want this provider and the information it holds to be available to any of the children of its element in our index.js file. We'll get to that in a moment.
Within the return block of the Provider, we need to tell the <MoviesContext.Provider>
element what information we want it to provide using the value tag. Here, I am setting movies and setMovies within my value tag. Within the element we include the { children }
prop to specify that the value is available to all children of the element.
Last, we need to export our Provider and Context.
- Next the Provider needs to be added to the main index.js file. Remember, all children of this Provider will be able to access the information, so we want to nest our
<App />
element within the Provider. It looks like this:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { MoviesProvider } from './context/movies';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<MoviesProvider>
<App />
</MoviesProvider>
</React.StrictMode>
);
Notice that we need to import our Provider from our context folder in order to use it in our render.
- Then, we move into our components to access the Context that our Provider is making available to our App. In order to access the information, we first need to import our Context...
import { MoviesContext } from '../context/movies'
...define the props that we will be using...
const [movies, setMovies] = useContext(MoviesContext);
...and then continue to use these variables within our components the same as if they were passed down through our component hierarchy. Here's a full example of a component that uses the movies prop:
import '../styles.css';
import React, { useContext } from 'react';
import { Card } from 'semantic-ui-react';
import { MoviesContext } from '../context/movies'
import MovieCard from './MovieCard';
function MoviesContainer() {
const [movies, setMovies] = useContext(MoviesContext);
return (
<div>
<br></br>
<Card.Group className="movie-cards">
{movies.map(movie => {
return (<MovieCard key={movie.id} movie={movie} />)
})}
</Card.Group>
</div>
)
}
export default MoviesContainer
It's always a challenge to learn a new hook. Luckily useContext is one of the easier ones to implement within an existing application. Just remember to remove any other code that defines, calls or passes down the original state props and you should be good to go!
Top comments (2)
I don't know why my numbered list keeps getting changed but there should be 4 steps instead of 1, 2, 1, 2. Why is this being forced from the way that I edited it?
Well done. Easy to read, understand, and follow along.