Harleigh Abel

Harleigh Abel

Harleigh Abel

Harleigh-Abel-profile-pic
SnowflakeBaby Screenshot
Software Development

Snowflake Baby - Developing a Membership Application in React/Redux

June 2 2021
7 min read
#javascript #rails #react #redux #api #webdev

The Project


Since early in my coding career as a passionate entrepreneur I’ve wanted to construct a web application in the form of a basic membership site, an application that solves a matching problem, ie sellers to buyers, owners to guests, passengers to drivers. When I discovered the concept for SnowflakeBaby I felt inspired to build this application that solves the issue of matching embryo donors with recipients through a basic membership site model. In order not to bury myself in complicated features I decided that the MVP would focus on a three specific things, primarily the ability to sign up and create a donor or recipient profile, the ability to search and filter for matches, and basic messaging capability. The application would be include a React/Redux front end and a rails api backend. Here are some of the challenges and lesson I encountered while constructing this application.


Idiosyncrasies of JSX


When working with the React framework, one of the first skills to master is writing components in JSX. JSX is like a Javascript/HTML hybrid that is converted by Babel transpilers into conventional HTML and JS and bundled by Webpack before it is parsed by the browser. JSX has a few idiosyncrasies to remember along the way, since many of the Vanilla JS features we are used to maybe slightly modified or not available at all.


is quick review of JSX deviations and syntactic requirements to remember:

  • • All JSX expressions must have only ONE outermost element.
  • • Multi-line JSX expressions must be wrapped in parentheses
  • • Curly Braces are used to embed javascript expressions in JSX expressions
  • • JSX uses the keyword className as opposed to class in JS
  • • JSX does not support if/else statements
  • • Conditional are expressed in one of three ways: using ternary operators, an if statement outside of the JSX expression, of the && operator to create a boolean expression that will execute or return noting at all.
  • • Event listener names are always camelCased
  • • Event listener values are a function that can be declared inline or as variable with an optional event parameter.
  • • Key attributes are used to uniquely identify individual elements, ie. as in lists of similar elements that need to be identified by the DOM for updating

Class Based Components vs Function Based Components


Javascript and web development in general is a rapidly evolving landscape and many of the common practices and design patterns have changed as new features have been introduced, React is no different. Initially the only way option for creating React components was to build a class based component with methods and constructor functions, but in React 16.8 new features and design patterns were introduced including the concept of function based components. While I was originally taught to use the class based design pattern, function components have become the preferred convention for react developers with many advantages to the original format.

A function component is regular JS function that acts as a React component. There are many advantages to using function components over the former format. Function components are lightweight, simpler, easier to read, they follow best practices of separation of concerns, they use less memory, less system resources. Function components are written in regular javascript and supported across all browsers. The React team has also indicated that there will be future enhanced performance benefits to using this component format.


Advantage of Class Based Components


Although function components are light weight, simpler and require less boilerplate code, we do loose the advantages of having access the to React Component library that class based components inherit from. Additionally React references aka 'refs' can not be accessed from function components, and the timing of a React hooks execution maybe slightly different than what we expecting from a lifecycle methods. And finally, it's important to note that class based components use 'strict-mode' which may help us identify silent errors.


Converting a Class Based Component to Function Component


I believe one of the best ways to visualize and understand the differences between class-based components and function components is convert one to the other. Let's walk through the steps required to do that conversion.


To convert a class based component to a function component:

  1. 1. Change the class declaration to a function declaration
  2. 2. Remove the render keyword from the final code block, keep the return statement and ensure that it is the last method
  3. 3. Change all methods to arrow functions
  4. 4. Delete all instances of the THIS keyword
  5. 5. Remove the constructor method and replace it with a useState() hook
  6. 6. Remove any event handler bindings
  7. 7. Replace lifecycle methods with useEffect() hooks

Once you have completed the steps above your component will have transformed from this class based component:

class based component code sample


To this function component:

function component code sample


LifeCycle Methods vs React Hooks


When function components were originally introduced they did not work with lifecycle methods such as componentDidMount(), or componentDidUpdate() and there was no way to hook our component functions into the lifecycle of a component. React hooks were introduced in React 16.8 to give developers the same functionality and ease of that class-based components provide.


What are React Hooks?


As state above and in the React Documentation: "hooks are functions that let you hook into React and lifecycle features from a function component". Because class-based components are dependant on life cycle methods with stateful logic they can be hard to break up into separation of concerns. Now with React hooks we have the ability to create smaller stateful components broken down by separation of concerns. The two most essential and commonly used hooks are useState and useEffect.


useState()


The useState function is how we add state to our function components. To use this function we import it from the React library without import statement. Then a state variable and set and initial value. When we call the useState function it will return the current state value and the function to update it.


useEffect()


The useEffect function was created to allow sideEffects within a component. Prior to the creation of useEffect, React developers relied on lifecycle methods like componentDidMount, componentDidUpdate, and componentWillUnmount(). componentDidMount() was called as soon as the component was mounted and this is where React developers would place initiate their API calls. componentDidUpdate() was called when any state changes where made and used to update the DOM to reflect these changes. componentWillUnmount(), another commonly used lifecycle method in class based components is called right before a component is destroyed and the DOM is cleared or reset, it is used to remove any event listeners, cancel subscriptions and clean up any tasks that may need to be resolved before a component is destroyed. Now lets see how we accomplish the same results by using useEffect().

To mimick the effects of the lifecycle method componentDidMount() in our function component and execute useEffect() only after our component has mounted, we make our useEffect() function call with an empty array for the second argument. To use useEffect() similarly to componentDidUpdate() we chain a useEffect() call with an array, and a second useEffect() call with the dependency you want to use as a field hook. To make use of useEffect() as componentWillUnmount(), return a useEffect() with an empty dependency array. Don’t forget, when using useEffect() to add an empty function call for cleanup. This will prevent setting state on unmounted components, and clear stale state values when the user changes.


Differences Between useEffect() and Lifecycle Methods


It is important to understand when transitioning from lifecycle methods to the useEffect hook that the conversion is not absolute. There are small variances to be aware of when using one to replace the other. Unlike componentDidMount(), useEffect() executes after the DOM has been rendered which could lead to visible content flicker if you need to read from the DOM and synchronously set state to create the new UI. The work around in this case is to use the layoutEffect() function which is executes with the same timing and the previous lifecycle method componentDidMount().

Another potential oversight, to be aware of, is the fact that useEffect() will 'capture' values in the effect function through the use of closure, while the ComponentDidMount does not create closure and will just read the current value. If your effect depends on a value, make sure that it is included in your useEffect() dependency array to keep these captured values current and accurate. Don't forget to include a clean up function to prevent stale values, or setting set on unmounted components.


Debugging Nested JSON objects


One of the most frustrating challenges I encountered was an issue with the JSON objects I was returning from my API. No matter what I did I could not seem to access nested object values. It was pretty clear to me what syntax combination of array indexes and object keys should have returned that values I was trying to retrieve but I still wasn't able to access them. After a few frustrating days of returning to the problem I discovered that the issue was I had double encoded nested objects within my api JSON objects, but using a combination of the to_json method in my controllers and active_model serializers. I was able to resolve this by removing some unnecessary serializers, and parsing nested objects a second time.

I found very little documentation regarding active model serializers, and observed Fast JSON seems to have become a popular replacement.


Application Architecture


Application architecture, and design patterns is another area I would like to explore further in React/Redux. While I'm very comfortable with MVC architecture for the back end api, and I used traditional React design patterns such and parent container components passing props to child presentational components, I feel like the end result was a multitude of components that could possible be reduced to simplify and condense the finished product.


As with the construction of every new application I build from scratch there were to many lessons to include here but I’ve included some of the notable concepts.

The repo for the React.js frontend can be found here!
The repo for the Rails API can be found here!

atxrenegade/Harleigh Abel