I came about this idea when talking with my husband. He tells me that I need to get his grandmother's recipe for one particular chicken dish that he likes from his mom. I had said that I'll just copy it from her notebook the next time I see her. I was hesitant about asking my mother-in-law for recipes through a text message because she would send me pictures of these recipes that were very hard to read. I would rather just copy it down myself. It dawned me that I could make an application that keeps track of family recipes and the idea grew from there.
It started out as a simple application where a user can submit recipes. My potential for this will be to create a social media type platform where users can share family recipes and users can scroll through a list of recipes and comment on them. I didn't quite get around to implementing commenting on recipes for this project but I would like to add this feature in the future. I ran into a few roadblocks so I'd like to highlight three of them. The three roadblocks I want to highlight are the following:
- Rendering the correct links in my navigation bar when a user is logged in.
- Understanding Redux store and implementing asynchronous fetch using Redux Thunk.
- Managing changes in a nested form.
The last roadblock was a particularly tough one and it made me feel really accomplished when I found the solution. I will touch on these roadblocks and elaborate on what I did to achieve the solution.
I wanted to build a navigation bar so I built it using react-bootstrap. I also wanted it to be dynamic where it would render different links when a user signed in. I used a conditional to render different results and stored it inside an arrow function called
I then pass in the state of current user from my Redux store on line 60 and set it to a boolean value using
!!. When a user is logged in, they should be able to view the recipes they've added, add a new recipe, and edit their own recipes. It also shows a logout link when a user is logged in and when they log out, it will show a sign up and log in link.
In order to fix the first roadblock, I had to understand the second roadblock first. The example I will use for this will be the
currentUser reducer. The
currentUser is a reducer in the Redux store. I utilized
Redux Thunk to make fetch requests to my Rails API to get the current user stored there. First, I have a file called
store.jsx that stores of my reducers and combine them together with
combineReducer like so...
I made an asynchronous action creator inside my middleware folder called
actions and make a fetch request to a custom action inside my
SessionsController in my Rails API.
logged_in? on line 16 is defined in
In addition to using
currentUser state from Redux store in
NavBar.jsx, I also used it in my
RecipeCard component to render a link to edit a recipe only if a
userId is equal to currentUserId`. This authorizes a user to edit only a recipe that belongs to them.
This application has three forms total: 1) signup form, 2) login form, 3) recipe form. Both signup and login form state are managed by Redux. I originally was going to use Redux to manage recipe form as well. The problem I ran into with recipe form was that it was a nested form. The three main models in the backend have these relationships:
- User has many recipes
- Recipe has many ingredients and belongs to user
- Ingredient belongs to recipe
I was running into a problem handling changes in the ingredient field form. It wouldn't show the changes in the correct attributes in Redux store. Because a recipe has many ingredients, I also wanted there to be a button that will continually render ingredient fields as I needed to add more ingredients to the recipe. One of the things that fixed it was adding this line of code in
recipe.rb in API.
I had completely forgot about
accepts_nested_attributes_! I also needed to add this to
strong params in
I added an attribute called
ingredients_attributes as an array with attributes from ingredients table. By doing this, the
RecipeController is in charge of creating ingredients in the form. Previously, I had both controller in charge of their own CRUD actions. One of the ways I tried to work around this was creating a separate recipe form component and an ingredient form component and importing ingredient form component into recipe form. Now that recipe is responsible for creating ingredients, there was no need for two separate form components. I made a new recipe form that handled form state locally but still submits form data to Redux store where recipe data is stored. Inside the local state in
RecipeForm, I assigned ingredients as an array of objects.
ingredients to an array, I was able to map through it and each time the button "add a new ingredient" is clicked, a new ingredient is added to the array.
Add a new ingredient takes in onClick
addIngredient handles adding a new ingredient instance to the previous state of ingredients.
The recipe form renders with one ingredient field with a button to allow a user to add as many ingredients as they would like.
I am glad that I was able to get through these roadblocks. I have so many more plans for this application and I will continue to work on this project. I do plan on deploying this application on Heroku once I have decide how I want the RecipeCard component to look. It has been an insane ten months and I have come a long way. I came into Flatiron School not knowing how to code and now I am able to build a full-scale web application. I am excited to learn more and continue on this journey!