-
Notifications
You must be signed in to change notification settings - Fork 0
Dev Log
-
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
tocamelCase
.
- 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
andannotations
set up as separate tables in ahas_many
, andbelongs_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.
- 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?
- 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.
-
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 + ")" } }
- 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
notthis.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!
-
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.
- 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.
- 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 thesession-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 eventtarget
against the eventcurrentTarget
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.
- Workaround #1: Instead of using this function, I wrote a
-
Problem #2: The design of my SessionForm component was such that a user first encounters the
/signin
route, and clicks a link tosignup
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!
- Workaround #2: Used
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
- 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 => [])
- 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".
- 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
- 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
oremail address
in combo withpassword
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 bothemail_or_user
keys, plus either the relevantusername
oremail
keys to update if the user inputs an email. Used regex to match email.
This is the wiki for the genius app clone. Please use the bulleted legend to view the various design documents related to this project!