Панель поиска в 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} <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} <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 }) }
См. полное репо по адресу: