Carl Rippon

Building SPAs

Carl Rippon
BlogBooks / CoursesAbout
This site uses cookies. Click here to find out more

Building a simple component in React.js v2

August 05, 2017
react

I’ve been using React again recently. It’s come along way in the last couple of years since I last blogged on building a simple search component. In this post I’ll build this component again using TypeScript with modern React constructs …

This is a link to the old code and below is what it looked like:

search results components v2

Getting started

To scaffold the app I used TypeScript-React-Starter.

The key steps are installing create-react-app …

npm install -g create-react-app

… and then running it, referencing some TypeScript scripts …

create-react-app my-app --scripts-version=react-scripts-ts

You can then use the following command to start the app …

npm start

Basic component structure

The app is going to consist of the following components as we had in v1:

  • Criteria - responsible for the display and entry of the search criteria
  • ResultsList - responsible for the display of the search results
  • Search - this wraps the Criteria and ResultsList components and performs the actual search
  • App - the top level component for our app (essentially just wrapping the Search component)

Criteria component

Let’s start with our Criteria component …

Interface CriteriaProps {
  criteria: string;
  onCriteriaChange: (criteria: string) => void;
  onSubmit: () => void;
}

const Criteria = (props: CriteriaProps) => {

  const handleCriteriaChange = (e: React.FormEvent<HTMLInputElement>) => {
    props.onCriteriaChange(e.currentTarget.value);
  }

  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    props.onSubmit();
  };

  return (
    <form onSubmit={handleSubmit} >
      <input
          type="text"
          placeholder="Enter search criteria ..."
          value={props.criteria}
          onChange={handleCriteriaChange}
      />
      <input type="submit" value="Submit" />
    </form>
  );
}

This is a functional component with no state.

It takes the current criteria value in as a property which is then bound to the criteria input value.

It also takes in a delegate function in as a property which is bound to the form’s submit event via our handleSubmit() function.

Finally it takes in a delegate function in as a property which is bound to the criteria input change event via our handleCriteriaChange() function.

We are also using the arrow function notation to avoid the “this” scoping problem.

ResultsList component

Let’s move on to our ResultsList component …

interface ResultsListProps {
  customers: Customer[];
}

const ResultsList = (props: ResultsListProps) => {
  const listStyle = {
    listStyleType: "none"
  };

  const listItems = props.customers.map(customer => (
    <li key={customer.id}>{customer.name}</li>
  ));

  return <ul style={listStyle}>{listItems}</ul>;
};

This is also a functional component with no state that takes in the customers to display as a property value.

We make use of Array.prototype.map() to project the customers into a html list.

We also need to remember to include the key attribute so that React can properly manage changes to the html list.

The interface for Customer is:

interface Customer {
  id: number;
  name: string;
}

Search component

Now on to our Search component …

interface SearchState {
  allCustomers: Customer[];
  filteredCustomers: Customer[];
  criteria: string;
}
class Search extends React.Component<{}, SearchState> {
  constructor() {
    super();

    const allCustomers: Customer[] = [
      {
        id: 1,
        name: "Alfreds Futterkiste"
      },
      {
        id: 2,
        name: "Berglunds snabbköp"
      },
      {
        id: 3,
        name: "Cactus Comidas para llevar"
      },
      {
        id: 4,
        name: "Chop-suey Chinese"
      },
      {
        id: 5,
        name: "Du monde entier"
      },
      {
        id: 6,
        name: "Ernst Handel"
      },
      {
        id: 7,
        name: "France restauration"
      },
      {
        id: 8,
        name: "Island Trading"
      },
      {
        id: 9,
        name: "Let's Stop N Shop"
      },
      {
        id: 10,
        name: "Maison Dewey"
      },
      {
        id: 11,
        name: "Paris spécialités"
      },
      {
        id: 12,
        name: "Que Delícia"
      },
      {
        id: 13,
        name: "Rancho grande"
      }
    ];
    this.state = {
      allCustomers: allCustomers,
      filteredCustomers: allCustomers,
      criteria: ""
    };
  }
  handleCriteriaChange = (criteria: string) => {
    this.setState({ criteria: criteria });
  };
  doSearch = () => {
    this.setState({
      filteredCustomers: this.state.allCustomers.filter(
        customer =>
          customer.name.toLowerCase().indexOf(this.state.criteria) === 0
      )
    });
  };
  render() {
    return (
      <div>
        <Criteria
          criteria={this.state.criteria}
          onCriteriaChange={this.handleCriteriaChange}
          onSubmit={this.doSearch}
        />
        <ResultsList customers={this.state.filteredCustomers} />
      </div>
    );
  }
}

This is our first component which has state which is why we are using a class component. Our state consists of:

  • allCustomers - a hardcoded array of all our customers
  • filteredCustomers - the current array of filtered customers
  • criteria - the current criteria

The state is intialised in the constructor with the filtered customers set to all the customers and the criteria set to an empty string.

The handleCriteriaChange() function catches changes to the criteria that are bubbled up from the Criteria component and sets the criteria bit of the state.

The doSearch() function catches the submit event from the Criteria component. It then does the appropriate filter on the array of customers using Array.prototype.filter(). Lastly it sets the filteredCustomers bit of the state.

The markup simply references the Criteria and ResultsList components, passing the appropriate properties.

App

Lastly, here’s our App functional component which simply wraps the Search component …

const App = () => {
  return <Search />;
};

If you to learn about using TypeScript with React, you may find my course useful:

Using TypeScript with React

Using TypeScript with React
Find out more

Want more content like this?

Subscribe to receive notifications on new blog posts and courses

Required
© Carl Rippon
Privacy Policy