DEV Community

James Shipman
James Shipman

Posted on

Final Project - User Profile

Summary

So, yeah. This took a lot longer than I anticipated it would because I over estimated how much I understood and remembered about basic CRUD functionality in rails and how react ties into it all. I am not going to belabor the point by talking each line of code here but I will post it all, along with the notes I took to keep track of what it was all doing.

Rails model, controller and route

There are so many ways one can build a user profile. Make it part of the user model, keep it separate (I did the separate model), will the profile have unique information that only associates to a user or will it also contain information from elsewhere in the app/database/3 rd party data pulled in ....? I choose to make it a 1:1 association that does not contain any non user information. Basically I could have put all the same columns into the user model. Keeping them separate but associated made it easier for me (or I thought it was going to) to build the routes and needed CRUD functionality.

The first issue I came across with the profile was that before a user created a profile there was nothing for the profile component to display and that gave me errors. Ultimately it was decided (myself and instructors) that when a new user was registered that the controller would also make an associated profile record with default data ("first name", "last name" that kinda stuff), in order for the profile page to have something to render. While this solved many problems what it caused was a rabbit hole of issues getting an Update route to work in a way that I understood and could replicate for future models/features in the app.

Here is what I finally got to work. For any one reading this more experienced than myself (that would be all of you FYI), please feel free to blast my potential "code smell", and non DRY-ness .... I am a dev noob, here me type way too much ;)

profiles_controller.rb

class ProfilesController < ApplicationController
  include CurrentUserConcern

  def show
    profile = Profile.find_by(id: params[:id])
    render json: profile
  end

  def update
    # grab current_user id to build a profile onto
    # try is built in
    # {profile: {firstname, lastname, ....}}
    profile = Profile.find_by(id: params[:id])
    profile.update(
      firstname: params["profile"]["firstname"],
      lastname: params["profile"]["lastname"],
      bio: params["profile"]["bio"],
      birthday: params["profile"]["birthday"],
      # user_id: @current_user.id,
    )

    # conditional to render json object of a status notification, a boolean for logged in, and the user model data
    if profile
      session[:user_id] = @current_user.id
      render json: {
        status: :updated,
        logged_in: true,
        user: @current_user,
        profile: @current_profile,
      }
    else
      render json: { status: 401 }
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

not sure if the show method needs to be there, but it's staying in because everything in my app to this point is working. I took a lot of similar code from the registrations controller as that controller creates a user, so the I figured they would have similar structure and such.

Next is the routes for the profile.

routes.rb

Rails.application.routes.draw do
  resources :sessions, only: [:create, :update]
  delete :logout, to: "sessions#logout"
  get :logged_in, to: "sessions#logged_in"
  # update :update, to: "sessions#update"

  resources :registrations, only: [:create]

  resources :profiles, only: [:show, :update]
  # resources :users, only: [:show] do
  # end

  root to: "static#home"
end
Enter fullscreen mode Exit fullscreen mode

I ultimately decided on not nesting the profile update route in the user route because the user routes aren't needed; the user "stuff" is handled in sessions and registrations.

Over in React land, the component I created for updating the profile (UpdateProfile, I know ... surprise naming convention). The part that gave me the most challenge was the part where I had to actually update the backend from the frontend. Here is the function in its working form that patches the profile record correctly.

UpdateProfile.js

...
handleSubmit(event) {
    // console.log(e.target.value);
    // const { firstname, lastname, bio, birthday } = this.setState;
    // const uId = this.props.user.id;
    const pId = this.props.profile.id;
    // console.log(this.state);

    axios
      .patch(
        // "http://localhost:3001/profiles",
        `http://localhost:3001/profiles/${pId}`,
        {
          profile: {
            firstname: this.state.firstname,
            lastname: this.state.lastname,
            bio: this.state.bio,
            birthday: this.state.birthday,
            // user_id: this.props.user.id,
          },
        },
        { withCredentials: true }
      )
      .then((resp) => {
        // console.log(this.state);
        if (resp.data.status === "updated") {
          console.log("profile has been updated", resp.data.profile);
          this.props.handleShowUpdateProfile();
          // this.props.handleSuccessfullAuth(resp.data);
        }
      })
      .catch((error) => {
        console.log("update error", error);
        // alert("update error", error);
      });

    // event.preventDefault();
  }
...
Enter fullscreen mode Exit fullscreen mode

Getting the correct match up of data structure between the frontend and the backend was tricky as I seem to mis bad spelling and transposed characters really, really easily. This gets the job done just fine though.

The function this.props.handleShowUpdateProfile() is passed down from the parent component that holds both UpdateProfile and Profile components. I have UpdateProfile rendering in a conditional statement based on a boolean state. This ensures that the UpdateProfile component - which is just a form - is hidden upon correct saving of data back to the controller - that's the if (resp.data.status ......) portion of the promise. I really like how placing a conditional based on a status that is sent from the backend you can kind of force the an order of operation to what is actually async behavior.

I take no credit for this, the video series I watched that helped me build the user authentication system used this, so I stole it like a good programmer

Note taking and Keeping Track of What the Flip is Going on in my App

To say I got lost in the logic and data flow of my the little app that I have is an understatement. Just the user authentication was a knot I had to untie each time I sat down to work. It got to the point that I had to find a way to write down how it worked. Not the code itself, that's took hairy to look at and I'm a visual thinker so words are hard. I found myself tell VSCode many times that I wish it would draw me a map of what functions live in what component, where the states are being passed down from, which component is nested where ..... so I did that. I have easily spent half as much time taking note of all of this as I have actually writing code and troubleshooting code.

Here is what I came up with, and for anyone else that is a visual thinker like me I hope you find this helpful. I used mind mapping to build out the flow of the data, how the components connect and the logic of different aspects of the app. It doesn't matter the specific app I used as I'm not here to promote one useful app over another.

Component Tree
Component Tree

Frontend Logic
Frontend Logic

Here is a link to the full mind map, the above are just screen grabs of specific portions of the larger map.
Mind map

Thank you for reading this post, it was a longer one. Future posts on this project will hopefully be shorter.

Stay healthy :)

Top comments (2)

Collapse
 
anamsoomro profile image
Anam Soomro

I love how you used the mind map to organize your logic. Much more organized than the scribbles in my notebook haha

Collapse
 
jbshipman profile image
James Shipman

Thank you. It's super fast way of taking note too; especially the app that I use. I found many years ago that I remember things as shapes first so the overall shape of a mind map becomes what I remember and then I can drill mentally into the words.