How I usually write my React components.
Tags: javascript, react, redux, typescript
Published: 2019-10-14 03:30:14


Let's say that somewhere in our application we fetch a list of users and the following interface describes the user object.

```typescript
interface User {
  address: string;
  createdAt: string;
  email: string;
  firstName: string;
  id: number;
  lastName: string;
  phone: string;
  updatedAt: string;
}
```

Then we have a function component that renders each user's first and last name next to a checkbox.

```tsx
import React from 'react';
import { useSelector } from 'react-redux';

import { usersSelector } from './selectors';

function UserList(): JSX.Element {
  const users = useSelector(usersSelector);

  return (
    <ul>
      {users.map(({ firstName, id, lastName }, index) => (
        <li key={index}>
          <input type="checkbox" value={id} />
          {firstName} {lastName}
        </li>
      ))}
    </ul>
  )
}
```

Each time a checkbox is clicked we need to send a request to an endpoint with the following payload.

```typescript
interface Payload {
  selectedUsers: number[];
}
```

The only thing that changes is the payload, so let's keep it in a state.

```tsx
import React, { useState } from 'react';
import { useSelector } from 'react-redux';

import { usersSelector } from './selectors';

function UserList(): JSX.Element {
  const users = useSelector(usersSelector);
  const [selectedUsers, setSelectedUsers] = useState<number[]>([]);

  return (
    <ul>
      {users.map(({ firstName, id, lastName }, index) => (
        <li key={index}>
          <input
            checked={selectedUsers.includes(id)}
            type="checkbox"
            value={id}
          />
          {firstName} {lastName}
        </li>
      ))}
    </ul>
  )
}
```

Now let's add a handler to update our payload on each checkbox click.
 
```tsx
import xor from 'lodash/xor';
import React, { useState } from 'react';
import { useSelector } from 'react-redux';

import { usersSelector } from './selectors';

function UserList(): JSX.Element {
  const users = useSelector(usersSelector);
  const [selectedUsers, setSelectedUsers] = useState<number[]>([]);

  const handleChange =
    (event: React.ChangeEvent<HTMLInputElement>): void => {
      const target = event.target;
      const value = target.value;
    
      setSelectedUsers(prev => xor(prev, [value]));
    }

  return (
    <ul>
      {users.map(({ firstName, id, lastName }, index) => (
        <li key={index}>
          <input
            checked={selectedUsers.includes(id)}
            onChange={handleChange}
            type="checkbox"
            value={id}
          />
          {firstName} {lastName}
        </li>
      ))}
    </ul>
  )
}
```

Let's run an effect on every UI update to make that request.

```tsx
import xor from 'lodash/xor';
import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';

import { selectUsers } from './actions'; 
import { usersSelector } from './selectors';

function UserList(): JSX.Element {
  const dispatch = useDispatch();
  const users = useSelector(usersSelector);
  const [selectedUsers, setSelectedUsers] = useState<number[]>([]);

  useEffect(() => {
    dispatch(selectUsers(selectedUsers));
  }, [dispatch, selectedUsers]);

  const handleChange =
    (event: React.ChangeEvent<HTMLInputElement>): void => {
      const target = event.target;
      const value = target.value;
    
      setSelectedUsers(prev => xor(prev, [value]));
    }

  return (
    <ul>
      {users.map(({ firstName, id, lastName }, index) => (
        <li key={index}>
          <input
            checked={selectedUsers.includes(id)}
            onChange={handleChange}
            type="checkbox"
            value={id}
          />
          {firstName} {lastName}
        </li>
      ))}
    </ul>
  )
}
```

Usually, we'd get a response back with the `selectedUsers` so as last step we would like to indicate the selected users in our UI as well.

```tsx
//
function UserList(): JSX.Element {
  const users = useSelector(usersSelector);
  const selectedUsersFromState = useSelector(
    selectedUsersFromStateSelector
  );
  const [selectedUsers, setSelectedUsers] = useState<number[]>(
    selectedUsersFromState
  );
//
```