Skip to content

Dev Log

Sarah Jiang edited this page Jan 15, 2020 · 11 revisions

1/14


Small decisions -> costly outcomes

  • Learned a lesson today about how early decisions around configuration and following convention impact/compound as time goes. I realized today that I should have been returning API payloads in the camelCase variable style that is convention in Javascript. I made the decision to alter the rails configuration file environment.rb and just hope for the best in terms of my features not breaking. To compound that, I made several other changes such as reseeding my database, all without testing incrementally. So, naturally when everything broke, it was very unclear what was the root cause of each individual breakage. This became a fairly costly mistake in terms of time.

  • Outcome: Spent about 1.5hrs today fixing basic features which broke after I changed the jBuilder configuration to automatically convert payload keys from Ruby conventional snake_case to camelCase.

An interesting design feature: Referents

  • My SongPage renders 2 components SongPageLyrics and SongPageAnnotations. SongPageLyrics needs to render lines of a song's lyrics based on whether or not the lyric is included within a "referent".
  • A referent is a fragment of a song's lyrics which can span +1 lines, to which users of the site attach "annotations". In the backend I have referents and annotations set up as separate tables in a has_many, and belongs_to relationship, respectively. My backend returns both referents and annotations that are tied to an individual song.
  • All referents will be highlighted in a gray text, and are interactive; i.e. clicking on the referent triggers an annotation to appear in the SongPageAnnotation component. Therefore we need to keep track of an "active" referent that needs to be stored in the state.
Questions to answer:
  • Where does the "active" referent state live? In the SongPage component or the SongPageLyric component?
  • How to set up conditional front-end rendering logic for:
    • Lines which belong to referents?
    • Lines which do not belong to referents?
  • How to modify the state of the sibling component SongPageAnnotation when a user clicks a referent?

Outcome:

  • SongPageLyrics stores an activeReferentId in local state, which is updated when a user clicks on a referent.
  • The same function, setCurrReferent which updates the local state, also is responsible for evoking setCurrAnnotation, a function defined inside the SongPage and threaded into SongPageLyrics as props.
  • SongPage in turn stores in local state the activeAnnotationId, which is updated by setCurrAnnotation(). When this state is updated, the props passed down to the SongPageAnnotation component also update, triggering a re-render of the SongPageAnnotation component. Since that component renders EITHER a "currentAnnotation" OR a general About page, it updates accordingly.

1/12


CSS Woes Part I: Setting background image using an ActiveStorage Url + React Inline styles

  • React Inline styles are pretty funky. Pass the style attribute an object with k/v pairs corresponding to the property you want to style. React doesn't accept common CSS or SCSS syntax for styles. So background-image has it's corresponding React key value backgroundImage, for example. This is what worked for me in the end:

    <div style={ { backgroundImage: "url(" + props.song.image_url + ")" } }

  • Helpful StackOverflow post here!

1/11


Chrome Dev Tools Magic: Repainting frames view

  • Was able to use the paint view in Chrome dev tools in order to see which components are 'repainting' live. I was trying to to resolve a sneaky bug where the ChartIndex parent component slice of state for currentSelectedGenre was updating but the child node ChartIndexFilter I was expecting to re-render wasn't doing as I expected, even though the change in state affected their state as well (the filter is passed down currentSelectedGenre as props).
  • Resolution: Realized that in the updateGenre function which sets the updated state in the parent, I was correctly updating the state, and correctly passing the updated props into the child component. However, inside the ChildIndexFilter, I was rendering this.state.currentSelectedGenre not this.props.currentSelectedGenre. The reason why the currentGenre was not updating was because I was setting the child component's state via the constructor function once
  • Learning: The constructor function runs one time, it doesn't update the state automatically if props change. What does update AND trigger re-renders are the props passed into child components. So your child components should be rendering this.props.someFeature.
    • Also, at architectural level, I realized it was unnecessary to use the props to set the same state in my child component as in my parent component. Since my parent component was already keeping track of the state one level higher, it was unnecessary to keep track at the level of the child component. I could just use prop threading to have the parent trigger changes in the children. This was a refresher on one of the key design principles of React, single source of truth for state!

Using the repainting view

  • Command + Opt + i to open Chrome Dev Tools
  • ⋮ --> More tools --> Rendering --> Select "Paint Flashing"
  • Note that the paint view doesn't distinguish renders of components from renders that result from hover effects or animations, so you'll want to temporarily disable any :hover effects you have going in order just to be viewing the component re-render.

1/10


Changing a components children's state from the parent using createRef()

  • ChartIndex has a child component called the ChartIndexFilter which consists of a dropdown menu. The ChartIndexFilter stores a bit of state to mark whether the dropdown is active or not.
  • Found out that we can use the React create.Ref to access functions from the createFilter component to change it's state from inactive to active when the user clicks away from the element.

1/9


Using react-router's props.history methods

  • Ran into an issue today trying to implement a small UI feature: allowing the user to click anywhere outside of the sign in / sign up modal in order to dismiss the modal. Previously I had implemented a tiny function in the SessionForm component which was called after the user demo logs in. The function looked like this.
closeModal() {
    <Redirect exact to="/"></Redirect>
  }
  • Problem #1: Thought it would be straightforward enough to reuse this function as a callback function onClick for the session-form-overlay div I had been using. However, in this case I was not able to render the Redirect.

    • Workaround #1: Instead of using this function, I wrote a dismissModal function, which compared the event target against the event currentTarget to determine whether the user was clicking on the form or somewhere else on the page. If clicking elsewhere on the page, I ran: this.props.history.goBack(). However this presented another problem.
  • Problem #2: The design of my SessionForm component was such that a user first encounters the /signin route, and clicks a link to signup if they want to make a new account. So .goBack() from the Sign Up page actually just sends user back to the Sign In page.

    • Workaround #2: Used this.props.history.push(<path_name>) to push another path to the history stack instead!

Now I'm left with a bit of code redundancy, as I have a dismissModal function as well as a closeModal function which essentially accomplish the same thing, but this can be dried up pretty quickly. Still a bit of a mystery as to why having a Redirect didn't work in this implementation. Noting for later: readup on React-Router

1/8


Storing Objects in ActiveRecord + Rails strong params

  • Learned today that if you want to store an object with Active Record, you cannot just specify the type as "array" or "hash" in your migration. So the following does NOT work:
class CreateSongs < ActiveRecord::Migration[5.2]
  def change
    create_table :songs do |t|
      t.array :my_column, null:false
      t.timestamps
    end
  end
end

Instead you need to store it as something like .string or .text, and inside of your model, use the serialize function. Like so:

serialize :body, Array
  • Also, if using strong params to permit params, make sure to specify the scalar value of the data you are permitting. Found this out the hard way today and consulted the very useful documentation on Git. This worked for me:

params.permit(:my_column => [])

Build and test and build and test again?

  • Bootstrapped various functions to the window in order to complete testing of the backend route handling for my second set of components (ChartIndex, ChartIndexItems, Song, SongDetail etc). Did this by assigning those functions to the window in my apps "entry file":

window.myFunkyFunction = myfunkyFunction

  • Makes me wonder about how Test-Driven-Development cycle can realistically be accomplished by an individual working on a solo project, learning a new set of tools, or even just generally operating under a time crunch. Given that about 75% of my time currently consists of figuring out "what to do".

1/7


CSS Woes p.1 - styling forms

  • Center an absolutely positioned modal to be responsive to 1) modal width and viewport width using left: calc(50% - (put_width_of_modal)px
  • A modal should not be nested inside the dark grey overlay container, as it will take the opacity value you give the overlay container. Spent 25min struggling with this until restructuring the containers in the SessionForm component solved the problem. Helpful site: 4 Reasons Your Z-Index Isn't Working

1/6


Single Session Form vs. Multiple Forms

  • Made design decision to implement single SessionForm preso component connected to 2 containers SigninContainer and SignupContainer.
  • Pro: By using a single presentational component, I can reduce duplicative code to create + export form component + render form contents.
  • Con: Signin form differs slightly from Signup, as it accepting either username or email address in combo with password as credentials. So some custom logic is needed to handle User sign in inputs. HandleSubmit form and update need to account for either user inputting email OR username into the same field
  • Workarounds:
    • Went with presentational component + custom logic to handle differing inputs
    • Added email_or_user key to local state which is rendered by the form.
    • Added some custom logic to update function allowing both email_or_user keys, plus either the relevant username or email keys to update if the user inputs an email. Used regex to match email.