Панель поиска в React с использованием Typescript и Redux



Да, это еще одно приложение для задач. В последнее время я использовал Typescript и Redux и подумал, что было бы забавно преобразовать этот учебник для использования Typescript и Redux.

Вместо того, чтобы настраивать проект с помощью Parcel, я все равно буду использовать старый добрый веб-пакет в качестве сборщика.

В этом руководстве сначала будет рассмотрено преобразование того же проекта в Typescript, а затем добавление Redux в микс.

Часть 1. Преобразование в машинописный текст

  • Шаг 1. Отобразите статический список задач
  • Шаг 2. Динамическое добавление в список задач
  • Шаг 3. Динамическое удаление элементов из списка задач
  • Шаг 4: Разбейте список на отдельный компонент
  • Шаг 5: Создайте панель поиска

Полный репозиторий см. на странице → https://gitlab.com/rozajay/search-bar.

Часть 1 — это часть реализации с помощью Typescript. Часть 2 будет посвящена интеграции Redux и стилей.

Установите приложение create-реагировать

npm install -g create-react-app

Создать новый проект

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

Запуск проекта

npm run start

Хорошо! Теперь мы готовы идти.

Шаг 1. Отображение статического списка задач

Внутри App.tsx в папке src у нас есть приведенный ниже код.

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;

Мы видим, что содержимое довольно похоже на чистый jsx. Прежде всего, нам нужно добавить наш конструктор и состояние. Мы создадим массив «список» в состоянии. Однако прежде чем мы это сделаем, нам нужно определить интерфейсы для реквизита и состояния.

Интерфейс — это синтаксический контракт, которому должен соответствовать объект. Другими словами, интерфейс определяет синтаксис, которого должна придерживаться любая сущность. Смотри исходник..

Причина, по которой используются интерфейсы, заключается в том, что они обеспечивают отличный способ обеспечения согласованности между объектами. Они также позволяют нам быть более уверенными в том, что правильные данные передаются функциям и конструкторам.

Внутри App.tsx добавьте следующие строки после импорта библиотеки.

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

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

export interface State {
  list: string[]
}

Эти два «синтаксических контракта» должны быть переданы в основной компонент приложения.

Typescript позволяет нам передавать реквизиты и интерфейсы состояний. Из приведенного ниже кода видно, что они передаются в качестве аргументов.

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"
      ]
    }
  }
  ...
}

Остальной код такой же, как и в исходном руководстве. Это окончательная версия файла App.tsx.

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;

Запуск npm start даст вам представление ниже.

Шаг 2. Динамическое добавление в список задач

Сначала добавьте в форму ввод и кнопку.

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;

Затем добавьте функцию добавления элемента в div onClick кнопки. Вы можете видеть, что если вы просто скопируете и вставите функцию addItem из исходного руководства, вы увидите красные линии под newItem.value и получите ошибку после компиляции. Ошибка будет Object is possibly 'null' . Это связано с тем, что текущий newItem рассматривается как HTMLElement , по умолчанию ввод для HTMLElement не имеет объектного ключа value . Поэтому для переменных newItem и 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");
    }
  }

Теперь после компиляции не должно быть ошибок, и теперь вы сможете добавлять элементы в список. Привязки в конструкторе можно избежать, используя функцию стрелки. Функция анонимной стрелки автоматически привязывает это.

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

Шаг 3. Динамическое удаление элементов из списка задач

Теперь для удаления элементов его выполнение аналогично добавлению элемента.

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

Фактическая функция removeItem

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

Только изменение добавляется и в другом случае. В противном случае вы получите ошибку Typescript. Теперь вы должны иметь возможность добавлять и удалять элементы! Продолжайте следовать исходному руководству, чтобы разбить список на новый компонент.

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;

Опять же, нам нужно убедиться, что мы определили соответствующий интерфейс свойства.

Шаг 5: Создание панели поиска

Остальное довольно прямолинейно, вот несколько вещей, на которые следует обратить внимание.

Внутри компонента List необходимо определить тип nextProps. Это тот же тип, что и реквизит.

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

См. полное репо по адресу:

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