Reprint big front-end technology road welcome attention

The React Way – Software design, architecture, and best practices

I’ve been using React since 2016, but haven’t been able to come up with a best practice for application architecture and design.

While there are some best practices at a low level, when it comes to architecture, most teams build their own “stuff.”

Of course, there are no best practices that are universal across all areas of the business. But there are some rules that can help you build an efficient base of code.

Software architecture is designed to be efficient and flexible. Developers can develop efficiently and make changes without having to rewrite their core.

This article has put together some principles and rules that have worked for me and the teams I’ve worked with.

I outlined good practices for components, program structure, testing, styling, state management, and data capture. Some examples may be oversimplified, so we can focus on principles rather than implementation.

Take everything here as one point of view, not the only one. There is more than one way to build software.

directory

  • component
    • Priority function component
    • Write components that are consistent in style
    • Naming components
    • Managing Helper functions
    • Don’t hardcode
    • Component length
    • Write comments in JSX
    • Using the Error Boundaries
    • Deconstruction Props
    • The number of Props
    • Pass objects instead of primitive types
    • Adjust the rendering
    • Avoid nested ternary operations
    • Extract the list
    • Give props the default value when deconstructing
    • Avoid nested render
  • State management
    • The use of Reducers
    • Use Hooks first
    • Use the data capture library
    • Use the state management library
  • Component concept model
    • Display type and container type
    • Stateless and stateful
  • The application structure
    • According to the Route/Module group
    • Creating a Common module
    • Use absolute paths
    • Wrapping external components
    • Put the components in a folder
  • performance
    • Don’t optimize too early
    • Pay attention to Bundle size
    • Rerender -Callbacks, arrays and objects
  • test
    • Do not rely on snapshot tests
    • Test render correctness
    • Validate status and events
    • Boundary condition testing
    • Integration testing
  • style
    • Using CSS – in – JS
    • Styled components together
  • Data acquisition
    • Use the data capture library

component

Priority function component

Priority function components – they have simpler syntax. There are no lifecycle methods, constructors, or boilerplate code. You can express the same logic in less code without losing readability.

Unless you’re going to use the wrong boundary, this is your best bet. The model you need to keep in your head becomes much smaller.

// 👎 Class components are verbose
class Counter extends React.Component {
  state = {
    counter: 0,}constructor(props) {
    super(props)
    this.handleClick = this.handleClick.bind(this)}handleClick() {
    this.setState({ counter: this.state.counter + 1})}render() {
    return (
      <div>
        <p>counter: {this.state.counter}</p>
        <button onClick={this.handleClick}>Increment</button>
      </div>)}}// 👍 Functional components are easier to read and maintain
function Counter() {
  const [counter, setCounter] = useState(0)

  handleClick = () = > setCounter(counter + 1)

  return (
    <div>
      <p>counter: {counter}</p>
      <button onClick={handleClick}>Increment</button>
    </div>)}Copy the code

Write components that are consistent in style

Use the same style for components. Put the helper functions in the same place, export them the same way, and follow the same naming pattern.

No one method is really better than another.

Whether it’s exported at the bottom of the file or directly in the component’s definition, just pick one and stick with it.

Naming components

Always name the component. This is useful for reading error stack information and using the React Dev tool.

It also makes it easier to locate components in files during development.

/ / 👎 get this
export default() = ><form>.</form>

// 👍 Name your functions
export default function Form() {
  return <form>.</form>
}
Copy the code

Managing Helper functions

Utility class methods that do not depend on component closure information should be moved outside. A good place to start is before the component definition, so that the code file can be read up and down as it is read.

This reduces the noise in the components, leaving only those parts that are necessary.

// 👎 Avoid nesting functions which don't need to hold a closure.
function Component({ date }) {
  function parseDate(rawDate) {... }return <div>Date is {parseDate(date)}</div>
}

// 👍 Place the helper functions before the component
function parseDate(date) {... }function Component({ date }) {
  return <div>Date is {parseDate(date)}</div>
}
Copy the code

You should minimize the number of utility methods in components. As much as possible, move these methods out and take as arguments the internal information of the component on which the method depends.

This makes it easy to track bugs and extensions by relying on a combination of pure functions.

// 👎 Helper functions shouldn't read from the component's state
export default function Component() {
  const [value, setValue] = useState(' ')

  function isValid() {
    // ...
  }

  return (
    <>
      <input
        value={value}
        onChange={e= > setValue(e.target.value)}
        onBlur={validateInput}
      />
      <button
        onClick={()= > {
          if (isValid) {
            // ...
          }
        }}
      >
        Submit
      </button>
    </>)}// 👍 Extract them and pass only the values they need
function isValid(value) {
  // ...
}

export default function Component() {
  const [value, setValue] = useState(' ')

  return (
    <>
      <input
        value={value}
        onChange={e= > setValue(e.target.value)}
        onBlur={validateInput}
      />
      <button
        onClick={()= > {
          if (isValid(value)) {
            // ...
          }
        }}
      >
        Submit
      </button>
    </>)}Copy the code

Do not hardcode labels

Do not hardcode labels for navigation, filters, or lists. Take a configuration object and loop through the configuration.

Then you only need to modify the tag and item content in the first place.

// 👎 Hardcoded markup is harder to manage.
function Filters({ onFilterClick }) {
  return (
    <>
      <p>Book Genres</p>
      <ul>
        <li>
          <div onClick={()= > onFilterClick('fiction')}>Fiction</div>
        </li>
        <li>
          <div onClick={()= > onFilterClick('classics')}>
            Classics
          </div>
        </li>
        <li>
          <div onClick={()= > onFilterClick('fantasy')}>Fantasy</div>
        </li>
        <li>
          <div onClick={()= > onFilterClick('romance')}>Romance</div>
        </li>
      </ul>
    </>)}// 👍 Use loops and configuration objects
const GENRES = [
  {
    identifier: 'fiction'.name: Fiction,
  },
  {
    identifier: 'classics'.name: Classics,
  },
  {
    identifier: 'fantasy'.name: Fantasy,
  },
  {
    identifier: 'romance'.name: Romance,
  },
]

function Filters({ onFilterClick }) {
  return (
    <>
      <p>Book Genres</p>
      <ul>
        {GENRES.map(genre => (
          <li>
            <div onClick={()= > onFilterClick(genre.identifier)}>
              {genre.name}
            </div>
          </li>
        ))}
      </ul>
    </>)}Copy the code

The length of the component

The React component is just a method to say props and return an HTML tag. They follow the same design principles as normal distributions.

If one method is doing too many things, extract some logic to other methods. The same goes for components. If a component has too many features, it is broken up into more smaller components.

If a section is complex and requires logical judgment and looping, extract it.

Rely on props and callbacks to communicate and get data. Lines of code is not an objective measure. Think about responsibility and abstraction.

Write comments in JSX

When more clarity is needed, it’s time to open up the code block and provide additional information (write comments). HTML tags are also part of the business logic, so you should actively provide comments as well.

function Component(props) {
  return (
    <>
      {/* If the user is subscribed we don't want to show them any ads */}
      {user.subscribed ? null : <SubscriptionPlans />}
    </>)}Copy the code

Using the Error Boundaries

A bug in one component should not crash the entire UI. Only in rare cases, if a serious error occurs, do we remove the entire page or redirect it. Most of the time, this works if we just hide a specific element on the screen.

There may be more than one try/catch in a method that processes data. So error boundaries are not just the top layer of the component. Use Error Boundaries to wrap individual component libraries to avoid cascading failures.

function Component() {
  return (
    <Layout>
      <ErrorBoundary>
        <CardWidget />
      </ErrorBoundary>

      <ErrorBoundary>
        <FiltersWidget />
      </ErrorBoundary>

      <div>
        <ErrorBoundary>
          <ProductList />
        </ErrorBoundary>
      </div>
    </Layout>)}Copy the code

Deconstruction Props

Most React components are functions. They type props to return the HTML tag. In a normal function you would use the parameters passed in directly, so this principle also applies. There is no need to repeat the props field around.

One reason not to deconstruct props is to distinguish between props and internal state. But in a normal method there is no difference between parameters and variables. Don’t create unnecessary rules.

// 👎 Don't repeat props everywhere in your component
function Input(props) {
  return <input value={props.value} onChange={props.onChange} />
}

// 👍 Destructure and use the values directly
function Component({ value, onChange }) {
  const [state, setState] = useState(' ')

  return <div>.</div>
}
Copy the code

The number of Props

How many props a component should accept is a subjective question. The number of props a component has is related to what it does. The more functions you pass to it, the more functions it undertakes.

Too many props is a sign that a component is doing too many things.

If a component has more than five props, consider whether to split it. In some cases, it may just require a lot of data. An input box, for example, might have many props. In other cases, it’s a signal that needs to be extracted further.

Note: The more props a component uses, the more reasons it has to re-render.

Pass objects instead of primitive types

One way to reduce the number of props is to pass in objects instead of primitive types. Pass the username, email, and Settings one by one, rather than putting them all together. In this way, even if the user adds a field, there is no need to change it.

Using TypeScript makes it even easier.

// 👎 Don't pass values on by one if they're related
<UserProfile
  bio={user.bio}
  name={user.name}
  email={user.email}
  subscription={user.subscription}
/>

// 👍 Use an object that holds all of them instead
<UserProfile user={user} />
Copy the code

Conditions apply colours to a drawing

In some cases, conditional rendering using short-circuit operators can backfire, and an unwanted 0 May appear in the interface. To avoid this default, use the ternary operator. The only thing to notice is that they are more verbose.

Short-circuit operators reduce the amount of code, which is good. Ternary operators are longer, but error-free. Also, it’s easier to add judgment conditions.

// 👎 Try to avoid short-circuit operators
function Component() {
  const count = 0

  return <div>{count && <h1>Messages: {count}</h1>}</div>
}

// 👍 Use a ternary instead
function Component() {
  const count = 0

  return <div>{count ? <h1>Messages: {count}</h1> : null}</div>
}
Copy the code

Avoid nested ternary operators

Ternary operators become unintelligible beyond one level. Although they look more concise, it is more important to ensure readability.

// 👎 Nested ternaries are hard to read in JSX
isSubscribed ? (
  <ArticleRecommendations />
) : isRegistered ? (
  <SubscribeCallToAction />
) : (
  <RegisterCallToAction />
)

// 👍 Place them inside a component on their own
function CallToActionWidget({ subscribed, registered }) {
  if (subscribed) {
    return <ArticleRecommendations />
  }

  if (registered) {
    return <SubscribeCallToAction />
  }

  return <RegisterCallToAction />
}

function Component() {
  return (
    <CallToActionWidget
      subscribed={subscribed}
      registered={registered}
    />)}Copy the code

Extract the list

It is common to traverse an array using methods such as map. But in a component with many HTML tags, this extra indentation and map syntax reduces readability. When you need to map some elements, put them in a separate component, even if it has few HTML tags. This way the parent component doesn’t need to care about the details and just needs to show the list. Keep loop operations in components only if the component’s primary responsibility is to display lists. Try to keep only one map per component, but extract the list if the HTML tags are long or complex.

// 👎 Don't write loops together with the rest of the markup
function Component({ topic, page, articles, onNextPage }) {
  return (
    <div>
      <h1>{topic}</h1>
      {articles.map(article => (
        <div>
          <h3>{article.title}</h3>
          <p>{article.teaser}</p>
          <img src={article.image} />
        </div>
      ))}
      <div>You are on page {page}</div>
      <button onClick={onNextPage}>Next</button>
    </div>)}// 👍 Extract the list in its own component
function Component({ topic, page, articles, onNextPage }) {
  return (
    <div>
      <h1>{topic}</h1>
      <ArticlesList articles={articles} />
      <div>You are on page {page}</div>
      <button onClick={onNextPage}>Next</button>
    </div>)}Copy the code

Give default values when deconstructing props

One way to specify default property values is to add a defaultProps property to the component. This means that the values of components and their parameters are not placed together.

It is best to give the default value when deconstructing props. This makes it easier to read the code from top to bottom, with definitions and values together, without jumping.

// 👎 Don't define the default props outside of the function
function Component({ title, tags, subscribed }) {
  return <div>.</div>
}

Component.defaultProps = {
  title: ' '.tags: [].subscribed: false,}// 👍 Place them in the arguments list
function Component({ title = ' ', tags = [], subscribed = false }) {
  return <div>.</div>
}
Copy the code

Avoid nested render methods

When you need to extract HTML tags from components or logic, do not put them in the function body of the same component. A component is just a function. This is defined as nested in its parent.

This means that it will have access to all of its parent’s state and data. It makes code more unreadable – what does this function do between all the components?

Move it to its own component, relying only on its own props instead of the closure.

// 👎 Don't write nested render functions
function Component() {
  function renderHeader() {
    return <header>.</header>
  }
  return <div>{renderHeader()}</div>
}

// 👍 Extract it in its own component
import Header from '@modules/common/components/Header'

function Component() {
  return (
    <div>
      <Header />
    </div>)}Copy the code

State management

State management

The use of reducer

Sometimes, you need a more powerful way to manage state. Try useReducer before you are ready to use an external library. This is a great mechanism for complex state management without relying on third-party libraries.

Combining the React context with TypeScript, useReducer can be very powerful. However, it is not yet widely used. People will still go to third-party libraries.

If you need more than one state, move them to a Reducer.

👎 Don't use too many separate pieces of state
const TYPES = {
  SMALL: 'small'.MEDIUM: 'medium'.LARGE: 'large'
}

function Component() {
  const [isOpen, setIsOpen] = useState(false)
  const [type, setType] = useState(TYPES.LARGE)
  const [phone, setPhone] = useState(' ')
  const [email, setEmail] = useState(' ')
  const [error, setError] = useSatte(null)

  return(...). }👍 Unify them in a reducer instead
const TYPES = {
  SMALL: 'small'.MEDIUM: 'medium'.LARGE: 'large'
}

const initialState = {
  isOpen: false.type: TYPES.LARGE,
  phone: ' '.email: ' '.error: null
}

const reducer = (state, action) = > {
  switch (action.type) {
    ...
    default:
      return state
  }
}

function Component() {
  const [state, dispatch] = useReducer(reducer, initialState)

  return(...). }Copy the code

Prioritize Hooks over HOC and Render Props

In some cases, we need to enhance components or enable them to access external state. There are generally three ways to do this — HOC, Render Props, and Hooks.

Hooks have proven to be the most effective way to achieve this effect. Metaphysically, a component is a function that uses other functions. Hooks allow you to access multiple external sources without conflicting. No matter how many Hooks there are, you know where each value is coming from.

With HOC, you can get data from props. It is not clear whether it comes from the parent or the wrapped component. In addition, linking multiple props together leads to errors.

Render Props result in high indentation and poor readability. Having multiple components with Render Props nested in the same tree looks even worse. And it only exposes values in HTML tags, so you have to write logic in there or pass it along.

With Hooks, you can use simple values that are easy to trace, and do not interfere with JSX.

// 👎 Avoid using render props
function Component() {
  return (
    <>
      <Header />
      <Form>
        {({ values, setValue }) => (
          <input
            value={values.name}
            onChange={e= > setValue('name', e.target.value)}
          />
          <input
            value={values.password}
            onChange={e= > setValue('password', e.target.value)}
          />
        )}
      </Form>
      <Footer />
    </>)}// 👍 Favor hooks for their simplicity and readability
function Component() {
  const [values, setValue] = useForm()

  return (
    <>
      <Header />
      <input
        value={values.name}
        onChange={e= > setValue('name', e.target.value)}
      />
      <input
        value={values.password}
        onChange={e= > setValue('password', e.target.value)}
      />
    )}
      <Footer />
    </>)}Copy the code

Use the data capture library

Usually the data we want to manage in state is retrieved from the API. We need to keep this data in memory, update it and be able to access it from multiple places.

Modern data acquisition libraries like React Query provide sufficient mechanisms to manage external data. We can cache it, expiration it, and retrieve it. It can also be used to send data, triggering it to refresh another piece of data.

If you use the GraphQL Client library like Apollo, it’s even easier. It has the concept of client state built in.

State management library

In most cases, you don’t need a state management library. They should be used in large applications that need to manage complex state. There are plenty of guides on the subject, so I just want to mention the two libraries I would pick — Recoil and Redux.

Component concept model

Display type and container type

The main idea is to divide components into two groups – presentation components and container components. Also known as smart and stupid.

The idea is that some components don’t have any functionality or state. They are simply called by the parent component through some props. Container components contain business logic, data acquisition, and state management.

This model is the MVC model of the back-end application. It’s versatile enough to work anywhere, and it’s okay to use it.

But, in modern UI applications, this pattern is not sufficient. Putting all the logic in a few components leads to bloat. They end up taking on too much responsibility and becoming unmanageable. As applications evolve, concentrating complexity in a few places is bad for maintainability.

Stateless and stateful

Treat components as stateful and stateless. The conceptual model mentioned above implies that a subset of components should manage most of the complexity. Instead, it should be scattered throughout the application.

The data should be adjacent to where it is used. When using the GraphQL client, you should get the data in the component that displays it. Even if it’s not top notch. Don’t think about containers, think about responsibilities. Consider what is the most logical component to store a state.

For example, a

Who validates in the form? Is it input’s responsibility? This means that the component will understand the business logic of the application. How will it notify the Form of an error? How will this error be flushed? Will form know? What happens if an error exists but you try to commit anyway?

When you are faced with a problem like this, you should realize that responsibility is being confused. In this case, it is best to leave the input stateless and receive error messages from the form.

The application structure

According to the Route/Module group

Grouping by container and component makes the application hard to find. To understand what components belong where, you need to have a very high level of familiarity with the project.

Not all components are created equal — some are universal, and some are designed for specific parts of an application. This structure (container and component grouping) is only suitable for a small number of projects. A few more components can become unmanageable.

// 👎 Don't group by technical details├ ─ ─ containers | ├ ─ ─ Dashboard. The JSX | ├ ─ ─ the JSX ├ ─ ─ components | ├ ─ ─ Table. The JSX | ├ ─ ─ Form. The JSX | ├ ─ ─ Button. The JSX | ├ ─ ─ Input. JSX | ├ ─ ─ Sidebar. JSX | ├ ─ ─ ItemCard. JSX// 👍 Group by module/domain├ ─ ─ modules | ├ ─ ─ common | | ├ ─ ─ components | | | ├ ─ ─ Button. The JSX | | | ├ ─ ─ Input. The JSX | ├ ─ ─ dashboard | | ├ ─ ─ the components | | | ├ ─ ─ Table. JSX | | | ├ ─ ─ Sidebar. JSX | ├ ─ ─ the details | | ├ ─ ─ components | | | ├ ─ ─ Form. The JSX | | | ├ ─ ─ ItemCard. JSXCopy the code

Group by route/ Module from the start. This structure supports change and growth. The key is not to let the application grow so quickly that its architecture fails. This would be true if it were grouping based on components and containers.

A module-based structure is easy to extend. You just add modules on top of it without adding complexity.

There’s nothing wrong with the container/component architecture, but it’s too generic. It doesn’t tell the reader anything about the project, except that he used React.

Creating a Common module

Components like buttons, input boxes, and tabs are used everywhere. Even if you don’t intend to use a module-based structure, you should extract this generic content.

Even if you’re not using Storybook, you can see what components you have. It helps avoid repetition. You don’t want everyone on your team making their own version of Button. Unfortunately, this often happens because of the poor structure of the project.

Use absolute paths

Making things easier to change is a fundamental purpose of the project structure. Absolute paths mean that fewer changes must be made if components need to be moved. Plus, it makes it easier to figure out where everything is coming from.

// 👎 Don't use relative paths
import Input from '.. /.. /.. /modules/common/components/Input'

// 👍 Absolute ones don't change
import Input from '@modules/common/components/Input'
Copy the code

I use the @ prefix to indicate that it’s an internal module, but I’ve seen it with ~.

Wrapping external components

Try not to import too many third-party components directly. By creating an adapter, we can change the API if necessary. Also, we can change third-party libraries in one place.

This also applies to component libraries, such as Semantic UI and Utility components. The easiest thing to do is to re-export them from a public module so they can be pulled out of the same place.

The component does not need to know what library we are using as the date picker.

// 👎 Don't import directly
import { Button } from 'semantic-ui-react'
import DatePicker from 'react-datepicker'

// 👍 Export the component and use it to manage your internal module
import { Button, DatePicker } from '@modules/common/components'
Copy the code

Put the components in a folder

I created a Components directory for each module. When I need to create a component, I create it there first. If it needs additional files like styles or tests, I create its own folder and put them there.

As a general practice, it is a good idea to export the React component from an index.js file. So from don’t need to like the import Form from ‘components/UserForm/UserForm import path that need to be repeated. Still, keep the name of the component file so you don’t get confused when you open multiple component files.

// 👎 Don't keep all component files together├ ─ ─ components ├ ─ ─ the Header. The JSX ├ ─ ─ the Header. The SCSS ├ ─ ─ the Header. The test. The JSX ├ ─ ─ Footer. The JSX ├ ─ ─ Footer. The SCSS ├ ─ ─ Footer. Test. The JSX// 👍 Move them in their own folder├ ─ ─ components ├ ─ ─ the Header ├ ─ ─ index. The js ├ ─ ─ the Header. The JSX ├ ─ ─ the Header. The SCSS ├ ─ ─ the Header. The test. The JSX ├ ─ ─ Footer ├ ─ ─ index. The js ├ ─ ─ Footer.jsx ├── Footer.scSS ├─ Footer.test.jSxCopy the code

performance

Don’t optimize too early

Before making any kind of optimizations, make sure they are for a reason. Blindly following best practices is a waste of effort unless it actually affects the application in some way.

Yes, it’s important to be optimization-minded, but build readable and maintainable components first before achieving performance. Well-written code is easier to improve.

When a performance problem is found, determine and determine the cause of the problem. If the bundle is large, there is no point in trying to reduce rerender times.

Once you know where the performance problems are coming from, fix them on a scale of impact.

Pay attention to bundle size

The amount of JavaScript code required to run the first screen is the most important factor affecting application performance. Your application may be very fast, but if you need to load 4MB JS to run it, no one will probably notice.

Don’t export an entire bundle. Split applications even further at the routing level. Make sure to send as few JS as possible.

Load in the background or when the user needs it. If you press a button to trigger a PDF download, you can wait until the button is hovered to download the PDF library.

Rerender -Callbacks, arrays and objects

It’s good to try to reduce unnecessary rerender in your application. Note, however, that rerender times are rarely the ones that have the biggest impact on your application.

The most common advice is to avoid passing callback as props. This means that a new function is created each time, which triggers a rerender. I’ve never had a performance problem with callbacks; in fact, I’ve just been passing the callback as props.

If you do experience performance issues with closures, delete them. But don’t make your code less readable or unnecessarily verbose.

Passing arrays or objects directly is the same kind of problem. They do not pass the reference equality check, so rerender is triggered. If you need to pass an array, extract it as a constant before the component is defined to ensure that the same instance is passed each time.

test

Do not rely on snapshot tests

Since I started using React in 2016, I’ve only encountered one problem that a snapshot test would have found for me. A call to new Date() with no arguments, which always defaults to the current Date.

In addition, snapshot tests only fail when a component changes. The usual workflow is to change the component, see if the snapshot failed, update the snapshot, and continue.

Don’t get me wrong, they’re a good sanity check, but they’re not a good substitute for component-level testing. I don’t even create them snapshot test cases anymore.

Test render correctness

The main functional aspect of the test should be to check that the component works as expected. Make sure it uses the default props and passes the props properly.

Verify that the function returns the correct result (JSX) for a given input (props). Everything you need to detect is on the screen.

Validate status and events

A stateful component is likely to change in response to events. Simulate the event and ensure that the component responds correctly.

Verify that the event handler is called and that the parameters passed are correct. Verify that the internal state is set correctly.

Boundary condition testing

Boundary condition testing

When the test cases cover the base case, make sure you add use cases that deal with boundary conditions.

This means passing an empty array to ensure that no index is accessed unchecked. Throw an error in the API call to ensure that the component can handle it.

Writing integration tests

Integration testing means verifying the interface or larger component. Test whether they work well as part of the extraction. This gives us the most confidence that the application will work as expected.

Because the components themselves work well, their unit tests pass. Integration, however, could be problematic.

style

Using CSS – in – JS

This is a very controversial view and many people would disagree. I prefer to use a library like Styled Components or Emotion, as I can express everything about the component in JavaScript. One less file to maintain. You don’t need to worry about CSS conventions.

A LOGICAL unit in React is a component, so a component should have everything associated with it from a separation of concerns perspective.

Note: There is nothing wrong with choosing SCSS, CSS modules, libraries (such as Tailwind) for styling. But CSS-in-JS is the one I recommend.

Styled components together

It is normal to have multiple CSS-in-JS components in the same file. Ideally, we want to keep them in the same files as the normal components that use them.

However, if they become too long, as styles generally do, they are extracted into separate files and placed next to their users. I’ve seen this pattern in open source projects like Spectrum.

Data acquisition

Use the data capture library

React doesn’t have an explicit way to get or update data from the API. Each team creates its own implementation, usually consisting of a service that calls asynchronous functions that communicate with the API.

This route means that we manage load status and HTTP errors ourselves. This leads to long and boilerplate code.

Instead we can use a library like React Query or SWR. They communicate with the server using hooks and are naturally integrated into the component lifecycle.

They have built-in caching capabilities and manage load and error status. We just need to manipulate these libraries. Moreover, they eliminate the need to use state management libraries to process this data.