Search Bar in React featuring Typescript and Redux

Roza J
6 min readApr 19, 2019

Yupe, this is yet another task app. Have been using Typescript and Redux lately and thought it would be a fun practice to convert this tutorial to use Typescript and Redux.

Instead of setting the project using Parcel, I’ll still be using the good ol’ webpack as bundler.

This tutorial will first go through converting the same project into Typescript then how to add Redux into the mix.

Part 1: Convert into Typescript

  • Step 1: Display a static list of todos
  • Step 2: Dynamically add to the list of todos
  • Step 3: Dynamically delete items from the list of todos
  • Step 4: Break List into its own component
  • Step 5: Create a search bar

See complete Repo at → https://gitlab.com/rozajay/search-bar

Part 1 is the implementation part with Typescript. Part 2 will be on integrating Redux and styling.

Install create-react-app

npm install -g create-react-app

Create out new project

create-react-app search-bar — scripts-version=react-scripts-ts

Running the project

npm run start

Okay! Now we are ready to go.

Step 1: Displaying a static list of Todos

Inside App.tsx in the src folder we have the below code.

import * as React from 'react';
import './App.css';

import logo from './logo.svg';

class App extends React.Component {
public render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.tsx</code> and save to reload.
</p>
</div>
);
}
}

export default App;

We can see the content is fairly similar from pure jsx. First thing is we need to add in our constructor and state. We will create a ‘list’ array in state. However before we do that we need to define the interfaces for props and state.

An interface is a syntactical contract that an entity should conform to. In other words, an interface defines the syntax that any entity must adhere to. See source..

The reason interfaces are used is because they provide a great way to enforce consistency across objects. They also allow us to be more sure that proper data is passed to functions and constructors.

Inside App.tsx, add in the below lines after library imports.

import * as React from 'react';
import './App.css';

import logo from './logo.svg';
export interface Props {
list?: string[]
}

export interface State {
list: string[]
}

These two “syntactical contracts” must be passed down into the main App Component.

Typescript allows us to pass down Props and State Interfaces. We can see from the below code that they are passed as arguments.

class App extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
list: [
"go to the store",
"wash the dishes",
"Learn some code"
]
}
}
...
}

The rest of the code is the same as the original tutorial. This is the final version of App.tsx file.

import * as React from 'react';
import './App.css';

import logo from './logo.svg';

export interface Props {
list?: string[]
}

export interface State {
list: string[]
}
class App extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
list: [
"go to the store",
"wash the dishes",
"Learn some code"
]
}
}
public render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.tsx</code> and save to reload.
</p>
<div className="content">
<div className="container">
<section className="section">
<ul>
{this.state.list.map((item: string) => (
<li key={item}>{item}</li>
))}
</ul>
</section>
</div>
</div>
</div>
);
}
}

export default App;

Run npm start will give you the below view.

See the list of todos in bullet points

Step 2: Dynamically add to the list of todos

First add in the form input and button.

import * as React from 'react';
import './App.css';
import logo from './logo.svg';export interface Props {
list?: string[]
}
export interface State {
list: string[]
}
class App extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
list: [
"go to the store",
"wash the dishes",
"Learn some code"
]
}
}
public render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.tsx</code> and save to reload.
</p>
<div className="content">
<div className="container">
<section className="section">
<ul>
{this.state.list.map((item: string) => (
<li key={item}>{item}</li>
))}
</ul>
</section>
<hr />
<section className="section">
<form className="form" id="addItemForm">
<input type="text" className="input" id="addInput" placeholder="Something that needs to be done..." />
<button className="button is-info">
Add Item
</button>
</form>
</section>
</div>
</div>
</div>
);
}
}
export default App;

Then add an add item function to the onClick of button div. You can see if you just copy paste the addItem function from the original tutorial you will see red lines under newItem.value and there will get an error after compilation. The error will be Object is possibly 'null' . This is because current newItem is seen as an HTMLElement , by default the typing for HTMLElement does not have object key of value . There fore specific typing need to be added for the variables newItem and form

addItem(e: any) {
// Prevent button click from submitting form
e.preventDefault();
// Create variables for our list, the item to add, and our form
let list = this.state.list;
const newItem = document.getElementById("addInput") as HTMLInputElement;
const form = document.getElementById("addItemForm") as HTMLFormElement;
// If our input has a value
if (newItem.value != "") {
// Add the new item to the end of our list array
list.push(newItem.value);
// Then we use that to set the state for list
this.setState({
list: list
});
// Finally, we need to reset the form
newItem.classList.remove("is-danger");
form.reset();
} else {
// If the input doesn't have a value, make the border red since it's required
newItem.classList.add("is-danger");
}
}

Now after compilation there should be no errors and you should now be able to add items to the list. The binding in constructor can be avoided by using an arrow function. The anonymous arrow function automatically bind this.

<button className="button is-info" onClick={(e) => this.addItem(e)}>

Step 3: Dynamically delete items from the list of todos

Now for the deletion of items, its execution is similar to adding item.

<ul>{this.state.list.map((item: string) => (<li key={item}>{item} &nbsp;<span className="delete" onClick={() => this.removeItem(item)} >x</span></li>))}</ul>

The actual removeItem function

removeItem(item: string) {// Put our list into an arrayconst list = this.state.list.slice();// Check to see if item passed in matches item in arraylist.some((value, index) => {if (value === item) {// If item matches, remove it from arraylist.splice(index, 1);return true;} else {return false;}});// Set state to listthis.setState({list: list});}

Only change is adding in an else case as well. Otherwise you will receive a Typescript error. Now you should be able to add and delete items! Continue to follow the original tutorial to break List into a new component.

import * as React from 'react';
import './App.css';
export interface ListProps {
items: string[],
delete(item: string): void
}
class List extends React.Component<ListProps> {
constructor(props: ListProps) {
super(props);
}
render() {
console.log(this.props.items)
return (
<div>
<ul>
{this.props.items.map((item: string) => (
<li key={item}>{item} &nbsp;
<span className="delete" onClick={() => this.props.delete(item)} >x</span>
</li>
))}
</ul>
</div>
)
}
}
export default List;

Again, we need to make sure we define the relevant prop interface.

Step 5: Building up the search bar

The rest is fairly straight forward, here are a few things to note.

Inside List component, the nextProps type need to be define. It is the same type as Props.

componentWillReceiveProps(nextProps: ListProps) {
this.setState({
filtered: nextProps.items
})
}

See complete repo at:

https://gitlab.com/rozajay/search-bar

--

--