DEV Community

MikeDudolevitch
MikeDudolevitch

Posted on • Edited on

Don't Fear the Redux

I am thrilled to say I am at the end of the Flatiron School's Software Development Bootcamp, and just completed the Phase 5 project which required a web app made with React.js and Redux. In this blog post I will walk through some of my challenges, and how I was able to reach the solutions to get my application working, complete with some code snippet examples.

I've mined my passions for music/guitars, reading, and hockey (both professional and amateur) as the subjects of my previous project assignments. I also love to cook so making a recipe site seemed like a natural next step. Once again it started with making a Rails API for my backend, at this point as comfortable as a warm blanket. The :recipe is the model to which :ingredients and :comments belong, and it has native attributes like name, blog post, and instructions (the latter two being text areas- this site is going to spoof cooking websites with obnoxiously long anecdotes by the author so I need some running room to write long blogs for each recipe.)

My next step was to create a new project repository and start my frontend with 'npx create-react-app' which auto-populates the directories for a React project. App.js is my highest level, so that seemed like a fine starting place. 'react-router-dom' has the built in components , , and , to let the client-side web app have the same routing capabilities that we've gotten used to in backend development. encompasses any of the routes that are user chosen; that being the case my NavBar (copied and pasted from 'react-bootstrap' and then customized to my specifics) would sit outside the switch and render to the screen to the user at all times. The specific inside of the switch accept a path/exact path="/to correspond to a url" and a component={to correspond to which component this url path with take a user to}.

Time to make up some components! They really are the basic lego building block of a React web app. There's an extension for VS Code called 'ES7 React/Redux/GraphQL/React-Native snippets' that is fantastic, and I can't recommend enough (it may be available in other text editors as well but I can't speak to that). Using this extension, to create a new class component, I just 1) create a new file in a '/components' folder, 'Recipe.js' say 2) type in rce, hit enter, and 3) watch the component magically appear- importing {Component} from react, creating a new class with the same name as the file, exporting default of the class name, putting in your render() function which returns an empty div- all stuff that at this point we had gotten used to hard coding ourselves but is unnecessarily repetitive to do for every component- hooray for software engineering laziness!

rce [ENTER]:

import React, { Component } from 'react'

export class Test extends Component {
    render() {
        return (
            <div>

            </div>
        )
    }
}

export default Test
Enter fullscreen mode Exit fullscreen mode

This took me to the stage where I could start bringing some backend data in and showing it through my frontend. This meant fetching the recipes I seeded in Rails- and this is where Redux came in to figure into my app- I set up a store at the top level, App.js, and used the inherited from 'react-redux'- I created a store and passed it into the Provider as a prop, which allows me to store a shared global state of my components, and configured that store with the 'thunk' middleware, which gives me the dispatch() functionality, and the ability to pass the dispatch function other functions that I write as parameters. To set up the store, I pass in a reducer as a parameter to createStore- this serves as a central location to update state based on the action that occurs- in the case of my fetch request for recipes, I supplied that with a type of "GET_RECIPES"- just some descriptive string that I can match in my reducer, so when an action with a type of "GET_RECIPES" gets sent, it will update state accordingly (ie storing a bunch of recipe objects as state, that I can use throughout my app!). I call that fetch action on componentDidMount() in my App.js, so as soon as someone opens up my web application, boom, it populates with the relevant data.

My recipes get fetched on a componentDidMount() call when my app gets opened, and then I mapStateToProps() to connect that state change in my store. I wanted the recipes themselves to show up on a Home page, and link to an individual 'show' page of each recipe. This was a matter of iterating over my whole list of recipes and passing each one to a component as props, like so:

const recipesOnDom = this.props.recipe.map(r => <Recipe
recipe={r} key={r.id} />)
        return (
            <div className="content-div">
                <ul id="recipe-list">
                    {recipesOnDom}
                </ul>
            </div>
    )
Enter fullscreen mode Exit fullscreen mode

Each recipe would render a link to that associated show page, by interpolating its ID in the url route:

const { name, id } = this.props.recipe
        return(
            <li key={id} id="recipe">
                <Link to={`/recipes/${id}`}>
                    {name}
                </Link>
            </li>
Enter fullscreen mode Exit fullscreen mode

My show page would be my recipes, the payoff, where a user can, ya know, read a bunch of vaguely associated blather, fight through ads, and then after X-ing out a bunch of spammy offers to subscribe to something, read instructions on how to cook something! Some of that anyway. Here it was just a matter of showing, in JSX elements, the name, ingredients (a nested resource of Recipe in my backend), instructions, and blog post, and style it up on the page. One minor issue I came across: each recipe had a random amount of instruction information. When it rendered to the page, it was ugly, and just a paragraph with step 1) and step 2) etc. all in the same blog text field. It took a minor bit of creativity to format that correctly:

const instArray = recipeObj.instructions && recipeObj.instructions.split("|")
Enter fullscreen mode Exit fullscreen mode

I split it be the seldom used '|' character, popped one of those between each instruction that I seeded, and put all of the split up instructions in a list. The && operator was a bit of error handling, in case no instructions were available upon loa

And now we're on to comments. A user ought to be able to post a comment on the specific recipe, so I styled up a couple of input fields. The onChange= in each element updates the state with the user's input, and then hitting the 'Post Comment' button triggers a handleSubmit() function that I wrote:

 handleSubmit = (e) => {
        console.log("in HS", this.state)
        e.preventDefault()
        this.props.addComment(this.state)
        this.setState({
            name: "",
            content: "",
            recipe_id: `${this.props.recipeObj.id}`
        }) 
    }
Enter fullscreen mode Exit fullscreen mode

It updates the state, and we mapDispatchToProps() and connect to our store to fire it off to my Redux action- ie "POST_COMMENT" to make a fetch request as a "POST" action. The action sends it to the reducer, that sees the "POST_COMMENT" and updates state accordingly- taking the new comment a user posted, and popping it into the array of comments for that recipe. Great. These comments persisted in my backend, were set in the Redux store, and now should show up on the DOM immediately. There weren't showing up in the DOM (well, they only did when they didn't persist to my store and backend) Above, when I call this.props.addComment with the updated state as a parameter, it was with the intention of using the addComment function inherited from my recipe 'show' page, and plunking that user updated state right there on the page! But it wasn't showing on the page!

It turns out I was so focused on getting comments to render correctly on my backend that neglected a logical fix on my front end- just fetch the data all over again! On my recipe's show page, I mapStateToProps()- so anytime state gets an update it connects and communicates with my store- and then I just call another fetch of the now updated data (passing that fetch function in as a connect parameter), and presto- all new data is rendered to a user, including their comment that just got posted!

Top comments (0)