back to top
July 13, 2023

An evaluation of GitHub Copilot as a tool for React Developers

mobile: An evaluation of GitHub Copilot as a tool for React Developers's image

Our team at Ballast Lane has been digging our teeth into the world of AI, evaluating different tools and deciding as a team how we plan to use them now and in the future.  As a frontend developer, my analysis is of Github Copilot as a tool to help with React Development, specifically with Declarative State Management.  I will provide some examples of its use below, as well as draw some conclusions. The summary is that it should be embraced, with caution, just like any open-source, generative tool.

What is Github Copilot and how is it used in React?

Github Copilot is a new tool, powered by OpenAI Codex, that can generate code from natural language descriptions, helping engineers write better code faster and easier. It does this by suggesting code snippets, completing functions, writing tests, and even generating entire files based on the developer's input. In separate articles my coworkers have evaluated Copilot for Node.js and Flutter.  Here, I will focus on React, a JavaScript library for building user interfaces.

Some of the benefits include:

  • Increased productivity, by suggesting code for common React tasks such as setting up components, connecting to databases, or handling user inputs.  It essentially reduces the amount of boilerplate, repetitive code you need to write.
  • Improving code quality, by helping you write more concise, idiomatic code.  Of course there are some caveats with this.
  • Ensuring that best practices are followed. Copilot is trained on a massive dataset of open-source code that experienced React engineers have written, helping you to learn.
  • Consistency and Collaboration by sticking to the React Style guide.

How can Github Copilot help with Declarative State Management in React?

In web development, state management, the process of tracking and updating the state of a program, is often used to track the state of a user interface, such as the number of items in a shopping cart or the current page that the user is viewing.

Declarative state management is a way of managing the state of an application by describing the desired state rather than specifying how to change it, making it easier to reason about the state of the application and prevent bugs. Here are some of the benefits of using React and Github Copilot together for Declarative State Management:

1. Suggesting code for tasks

To use Copilot for state management in React, engineers simply need to write a comment describing the desired state or behavior of a component, and Copilot will suggest a possible implementation using hooks, custom hooks, or context.

Example:Example: Let us say we need to create a counter page that contains a counter, which we can increment, decrement and reset. We can use Github Copilot to help us with this task.

Github Copilot can read prompts inside commented code, this means that we can simply describe what we want the counter page to do, and Copilot will suggest code that implements it. Here's an example of using Github Copilot with React Hooks:

tasks

The code uses the useReducer hook to subscribe to changes in the state of the application and render the user interface accordingly. The reducer function defines the logic for how the state of the application is updated. Take into account that the more detailed the initial prompt is or the more data you provide like components or functions that you want to be used, the better the suggestions will be that you get.

Conclusion: Copilot was able to implement the reducer function and the counter page component based on the prompt I provided. The code that Copilot suggested was concise, idiomatic, and error-free.

2. Suggesting code for libraries

Managing state in React applications can be challenging, especially as the complexity and scale of the application grows. There are many libraries that aim to simplify state management, such as Redux, MobX, Recoil, and others. However, these solutions often require a lot of boilerplate code, introduce new concepts and abstractions, and increase the cognitive load for developers.

Example:Example: In this example, we are going to use Redux Toolkit, which is a toolset for efficient Redux development, to create a counter slice with three actions, increment, decrement and reset the counter as it is stated in the prompt.

suggesting
// Using  Github  Copilot  with  Redux  Toolkit
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { increment, decrement, reset } from '../GlobalRedux/Features/counter/counterSlice'
 
export default function CounterPage() {
  // Implement a counter component using redux
  const dispatch = useDispatch()
  const count = useSelector((state) => state.counter.value)
  return (
    <div>
      <h1>Counter</h1>
      <div>
        <button onClick={() => dispatch(increment())}>+</button>
        <span>{count}</span>
        <button onClick={() => dispatch(decrement())}>-</button>
      </div>
      <button onClick={() => dispatch(reset())}>Reset</button>
    </div>
  )
}

Github Copilot was able to use the Redux syntax to create a counter slice including the increment, decrement and reset actions that I requested. As a result, we get a refactored version of the CounterGithub Copilot was able to use the Redux syntax to create a counter slice including the increment, decrement and reset actions that I requested. As a result, we get a refactored version of the Counter component from the previous example to use this reducer in it.

Copilot can suggest code for Redux Toolkit tasks, such as configuring a store, creating reducers and actions, and connecting Redux to React components. It can also help avoiding common implementation errors, make code more concise and compelling, and even propose fixes for bugs. However, it is important to review the generated code carefully, as Copilot generated code may introduce some bugs. In this example, it missed some that were needed so it is crucial to double-check what we get.

Conclusion: Github Copilot and Redux are both powerful tools that can be used to write more efficient code. Nevertheless, Copilot's suggestions are not always perfect, so it is important to use them with caution. Before you accept a suggestion, make sure that it is correct and that it does not introduce any new errors.

3. Helping with Debugging

Common state management errors include race conditions, bugs caused by mutation or incorrect logic. Copilot can be used to debug these kinds of errors by checking code that we provide, returning suggestions and explanations about what was wrong with that piece of code. It can review code that uses react hooks, immutable data structures, and unit tests just to name a few examples.

Example: In this example, we have a counter that updates its value asynchronously, however when running the setTimeout function it is using the current value of counter at the moment counterUpdate is fired instead of the current value at the moment setCounter is fired. This represents a bug and we will use copilot to help us find out why:

debugging

Copilot was able to identify the issue and provide a concise explanation of the problem. It also provided the information to fix it. This solution ensures that we are always using the latest value of counter when updating the state.

4. Writing better React tests

As engineers it is important to write high quality tests to ensure that code is working as expected. Github Copilot is a powerful tool that can help us to write better tests. Copilot can generate code for a variety of React testing frameworks, including Jest, Enzyme, and React Testing Library.

Example: Let's create some unit testing for the component LazyCounter that we used before.

react test
import React from 'react'
import { render, fireEvent, waitFor } from '@testing-library/react'
import LazyCounter from './LazyCounter'
describe('LazyCounter', () => {
  it('renders the initial count of 0', () => {
    const { getByText } = render(<LazyCounter />)
    expect(getByText('You clicked 0 times')).toBeInTheDocument()
  })
 
  it('increments the count when the button is clicked', async () => {
    const { getByText } = render(<LazyCounter />)
    fireEvent.click(getByText('Increase Count'))
    await waitFor(() => expect(getByText('You clicked 1 times')).toBeInTheDocument())
  })
 
  it('increments the count after a delay', async () => {
    jest.useFakeTimers()
    const { getByText } = render(<LazyCounter />)
    fireEvent.click(getByText('Increase Count'))
    jest.advanceTimersByTime(1500)
    await waitFor(() => expect(getByText('You clicked 1 times')).toBeInTheDocument())
    fireEvent.click(getByText('Increase Count'))
    jest.advanceTimersByTime(1500)
    await waitFor(() => expect(getByText('You clicked 2 times')).toBeInTheDocument())
    jest.useRealTimers()
  })
})

Github Copilot analyzed the code for the LazyCounterGithub Copilot analyzed the code for the LazyCounter component and generated the resulting unit tests using React Testing Library. The tests verified that the elements are correctly displayed and that the increment counter functionality works as expected. Note that along with the piece of code Copilot provided, it also explained the response it created simplifying the comprehension of the code.

Conclusions

My overall conclusion is that Github Copilot is definitely worth adding to your toolset.  It enhances the coding process by offering intelligent code snippet suggestions, and it helps us to write better code efficiently by offering contextual snippets that align with the code being written and the existing codebase within the project.  As I demonstrated in the examples above, Copilot can be particularly helpful with state management, suggesting best practice solutions as well as for other complex solutions that require state management libraries like Redux Toolkit, which often needs a lot of boilerplate.  These benefits come with a health warning: you should use Copilot responsibly, and we must be aware of its limitations. Given that Copilot is still being developed, it may occasionally suggest code that is incorrect or inefficient and cannot replace a human.  You still need to check your work.  Knowing the current limitations, we as a company are encouraging our engineering team to incorporate this technology in our toolbox to keep improving the quality and efficiency of our code, in order to keep providing the best possible solutions to our clients.