Posted by Dan Beaven on Jul 29, 2019

Starting as a React Developer at MyBuilder

I have been developing websites and JavaScript apps for over five years now and using ReactJS for over four but never inside of an organisation. I have rarely, if ever, had to read other people’s code and my experience collaborating on a codebase is limited to the few projects I have done with friends. I was curious before joining MyBuilder about this transition, what it would be like to get to grips with a large codebase and how the process of writing code would differ. This process is, I am sure, different for each company; nevertheless, if you find yourself looking for your first development role or even are considering joining MyBuilder hopefully I can shed some light on what your first days will look like. Today marks my third week at the company, and it’s been easy getting up to speed, adding value, and improving the codebase.

I was keen to start writing code as soon as possible; however, the first day was consumed by setting up the tooling and going over the application architecture. The first job that I was assigned was to move a library of shared JavaScript React components over to TypeScript. Since many of the components were written a long time ago when hooks weren’t a thing and best practices for React were less defined I also used it as an opportunity to refactor the class components to function components.

There are several reasons why you might want to move class components to function components. One big one is the readability and testability of the code. Class components take advantage of ES6 classes. You can define them extending React.Component and adding a render() method which returns react elements/jsx. Originally they were useful because you could save state within them and have them rerender when that state changed. The React.Component class also has a number of lifecycle methods that allow you perform network requests and change the component state at various stages of rendering such as before the component has mounted or after.

import React, { Component } from 'react'

export default class NewComponent {
  state = { counter: 0 }
  render() { 
    return (
      <div onClick={this.handleClick.bind(this)}>
        {this.state.counter}
      </div>
      )
  }
  handleClick() { 
    this.setState(s => ({ counter: s.counter + 1}))
  }
}

A so-called function component simply takes the component props as an argument and returns React elements/jsx.

import React from 'react'

export default ({ children }) => <div>new component, {children}</div>

Originally Class Components were used as containers and function components were used as purely presentational. However, React 16.8 introduced hooks a brand new concept to the React ecosystem which allows one to define state within a function component. Hooks can mimic all the same behaviour as component lifecycle methods but come with the advantage of reduced boilerplate and the ability to abstract out custom hooks so components can share stateful logic. We can rewrite the above class component taking advantage of the useState hook.

import React, { useState } from 'react'

export default ({ children }) => {
  const [count, setCount] = useState(0)
  const handleClick = () => setCount(count + 1)
  return (
      <div onClick={handleClick}>
       {this.state.counter}, { this.props.children } 
    </div>
  )
}

One of the components that I refactored and added type-safety to was this SelectField Component. The component acts as a controller mapping over its children replacing them with either a checkbox or radio component bound with change and focus events. By moving the component from a class component to a function component I was able to shave off over 60 lines of code.

<SelectField multiSelect={false} defaultValue=>
  <Option
    name="a"
    component={TextArea}
    note="This is the note for a"
    meta={ error: "This is the error for a" }>
    <OptionTextArea></OptionTextArea>
    Architectural services
  </Option>
  <Option
    name="b"
    note="This is the note for b">
    Boiler specialist
  </Option>
</SelectField>

Screenshot of SelectField Component

When adding type definitions to React components it is often helpful, particularly on larger projects, to export the types separately under some naming convention. This makes it easier for container components to extend the prop types of their children or in our case to ensure the children of SelectField have the correct type definitions. In this case, I use the convention of component name + ‘Props’.

export interface OptionProps {
  component?: React.ReactNode;
  componentAttributes?: object;
  name?: string;
  note?: string;
  placeholder?: string;
  children: string;
}

const Option: React.FC<OptionProps> = ({ children }) => <>{children}</>;

For the SelectField prop types, we want to ensure that the children are of the same type as the Option Component.

import Option, { OptionProps } from './Option';

export interface SelectFieldProps {
  // Children are of the same type as Option
  children: React.ReactElement<OptionProps>[] | React.ReactElement<OptionProps>;
  multiSelect?: boolean;
  onChange: (x?: any) => void;
  value: { [x: string]: boolean };
}

const SelectField: React.FC<SelectFieldProps> = ...

Now TypeScript will complain if we try to pass in a component that doesn’t accept the same prop types as the Option component. This is an example of one component that I helped to fix, and in my first two weeks at the company, I’ve already refactored 25 components, modified 200 files, and cleaned up over 2500 lines of code.

Familiarising oneself with an application as large as MyBuilder can quickly become overwhelming and I think everyone will have a way that is best for them. For myself that was to dive into the code early because seeing the interfaces on a component level helped to contextualise the architectural decisions. Fortunately, the team here understands this, they encourage questions that might lie outside my job role and accommodate different learning styles. I have only been here a couple of weeks now but I already feel confident traversing the codebase (at least on the frontend) and have been able to contribute a significant amount of work.

Jobs at MyBuilder and Instapro

We need experienced software engineers who love their craft and want to share their hard-earned knowledge.

View vacancies
comments powered by Disqus