My Personal Guide to My React + Redux Frontend with a Rails API Backend Project

Adriana DiPietro - Aug 24 '21 - - Dev Community

While I am definitely not reinventing the wheel, my project "Silvermark" encompasses a stylistically prettier bookmark manager. Why do this? Most, if not all browsers, have a bookmark manager. Well, because why not? Like I said, I'm not reinventing the wheel, I am just shining it.

Read below to learn more about my project.

Features

  • A user can login, signup, and logout of the application.
  • A user has access to full CRUD functionality for bookmarks.
  • A user can favorite a bookmark.
  • A user can create a new category to store bookmarks.
  • A user can filter by category.
  • A user can click a bookmark's web URL and be redirected to the URL in a new browser tab.

Models & Associations

  • I have three (3) models: User, Bookmark, and Category.
  • A bookmark belongs to a category.
  • A category belongs to a user.
  • A user has many categories.
  • A user has many bookmarks through categories.

New Concepts Learned + Implemented

Managing State

Simply defined, state is data that is mutated in React components. And from this project, I learned a few ways of managing state. Using the React way, state can be mutated and updated through the use of lifecycle methods in class components or lifecycle hooks in functional components. I enjoyed mutating state this way because it was clear and made sense. However, it was not very DRY and took up a lot of time/space. Here is an example from my code using class component lifecycle methods:


class Signup extends React.Component {
    state = {
      email: '',
      name: '',
      password: ''
    }


    handleChange = (e) => {
      this.setState({[e.target.name]: e.target.value})
    }

   render() { ...JSX FORM HERE }

Enter fullscreen mode Exit fullscreen mode

Here, I declare a class component called "Signup". I set the initial state using a JS object syntax and assigning each attribute to an empty string (to later be filled). Within an event handler, "handleChange" I set the state ('this.setState') of each attribute (previously empty) with the value inputted into my signup form. The state then becomes updated.

Yet, within Redux, all the state of an application is stored in a single location outside of the App component in what is called the store. The store diminishes the need for lifecycle methods or lifecycle hooks. My implementation of Redux into my application ultimately dried and abstracted my code, as I did not need to call and set state in various components. The use of the store is definitely efficient for complex applications that may change over time.

Components

In my project, I implemented a couple types of different components. Although there is now not much functional difference due to the introduction of lifecycle hooks and the use of the Redux store, I still made use of both class and functional components.

Here is an example of a class component:

import React from 'react'
import TextField from '@material-ui/core/TextField'
import Button from '@material-ui/core/Button'
import { loginUser } from '../actions/index'
import { connect } from 'react-redux'
import history from '../history'



class Login extends React.Component {
    state = {
      email: '',
      password: ''
    }


     handleSubmit = (e) =>{
        e.preventDefault()
        const { email, password } = this.state
        this.props.loginUser({ email, password })
        history.push('/bookmarks')  
    }


    handleChange = (e) => {
      this.setState({[e.target.name]: e.target.value})
    }

   render() {
     const { email, password } = this.state
     return (
         <div className="login-form">
           <h1>Login</h1>
           <form onSubmit={this.handleSubmit}>
             <div>
               <TextField type="text" name="email" placeholder="Email" onChange={this.handleChange} value={email} />
             </div>
             <div>
               <TextField type="password" name="password" placeholder="Password" onChange={this.handleChange} value={password}/>
             </div><br></br>
             <Button type="submit" value="Login">Login</Button>
           </form>
         </div>
     )
   }
} 
Enter fullscreen mode Exit fullscreen mode

And here is an example of a functional component:

import React from 'react'
import Button from '@material-ui/core/Button'
import { logoutUser } from '../actions/index'
import { connect } from 'react-redux'
import { useHistory } from 'react-router-dom'

const Logout = (props) => {
    const history = useHistory()

    const handleClick = () => {
        props.logoutUser()
        history.push('/')
    }
    return (
        <div className="logout-confirmation">
            <h4>Are you sure you want to logout?</h4>
            <Button type="submit" value="Logout" onClick={handleClick}>Logout</Button>
        </div>
    )
}



const mapDispatchToProps = (dispatch) => {
    return {
      logoutUser: () => dispatch(logoutUser())
    }
}

export default connect(null, mapDispatchToProps)(Logout)
Enter fullscreen mode Exit fullscreen mode

As you may see, both syntaxes of either component achieve what needed to be achieved. For your own project, you may implement both, like I, or stick to the one you favor most!

Accessing Props

The principle purpose of props is to pass data from parent component to child component. Props are data, like state, that however cannot be mutated and props, like state, represent a plain JavaScript object. While solely using React, you may access props in a class component by calling 'this.props'. In a functional component, you may pass props as the functional component's argument and call on props whenever necessary.

Yet, within my implementation of Redux, I began to use the functions "mapStateToProps()" and "mapDispatchToProps()" to access props.

mapStateToProps() takes in the entire, current state of the store as an argument and selects a part of that state from the Redux store to be returned as a plain JS object. The syntax of a JS object, specifically the key-value pairs, sets each value as a prop for the App component. So:

//App.js
const mapStateToProps = (currentState) => {
  return {
    bookmarks: currentState.bookmarks.bookmarks
  }
}
Enter fullscreen mode Exit fullscreen mode

"bookmarks" represents the key and "currentState.bookmarks.bookmarks" is the value of that key: it is what is in the Redux store. I can now call on "bookmarks" to encompass all of the store's state.

mapDispatchToProps() takes an argument of "dispatch". Dispatch is a function of the Redux store and it is the only way to trigger a state change. mapDispatchToProps() returns a plain JS object. In these key-value pairs, I set a key to the name of an action object, "createBookmark". For its value, I use an arrow function syntax to pass a parameter to be dispatched with the action object. This action object we declared is the key that tells the reducer what to do with the action. Why did I do this? So each declared key-value pair becomes a prop in my App component to be used within my other components.

//App.js
const mapDispatchToProps = (dispatch) => {
  return {
    createBookmark: (bookmark) => dispatch(createBookmark(bookmark))
  }
}

Enter fullscreen mode Exit fullscreen mode

Conclusion

This project was the most intense and complex thus far. It definitely tied in elements from each project leading up. Content-wise, module 5 and its project surpassed my expectations. I touched on some key features and concepts that I thought really set the foundation of my project, but there are many more. Check out my github repo here to see all concepts implemented.

I cannot wait to continue to work and expand on this project.

☁️Thank you for reading☁️
☁️Feel free to ask questions + comment below☁️

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .