Создайте приложение CRUD в реакции с крючками

В React была введена новая концепция — Hooks . Крючки являются альтернативой занятиям. Если вы раньше использовали React, вы будете знакомы с простыми (функциональными) компонентами и компонентами классов.

Простой компонент

const Example = () => {

  return <div>I’m a simple component</div>

}

Компонент класса

class Example extends Component {

  render() {

    return <div>I’m a class component</div>

  }

}

Многие функции, доступные для классов, такие как методы и состояние жизненного цикла, до сих пор не были доступны для простых компонентов. Новое предложение Hooks добавляет всю эту функциональность и многое другое.

Я хотел попробовать Hooks и посмотреть, как приложение может выглядеть без каких-либо классов, но я пока не видел примеров, поэтому решил создать его самостоятельно. Было создано простое приложение CRUD (создание, чтение, обновление, удаление), которое использует Hooks и не содержит классов, и я создал этот учебник для всех, кто хочет научиться их использовать.

Если вы не знаете, как создать простое приложение CRUD в React, независимо от того, используете ли вы классы или хуки, эта статья также подойдет вам.

Предпосылки

Чтобы следовать этому руководству, вам понадобятся базовые знания HTML, CSS и JavaScript / ES6. Вы также должны знать основы React, которые вы можете узнать, прочитав «Начало работы с React» .

Цели

В этом уроке мы сделаем простое приложение CRUD. У него будут пользователи, и вы сможете добавлять, обновлять или удалять пользователей. Мы не будем использовать какие-либо классы React, а вместо этого мы будем использовать State Hooks и Effect Hooks на функциональных компонентах. Если вы заблудились по пути, не забудьте проверить источник завершенного проекта.

Создать приложение React

Мы начнем с установки проекта с помощью create-реагировать на приложение (CRA).

npx create-react-app react-hooks

Тогда беги npm i.

Теперь все готово к Реакту.

Начальная настройка

Давайте начнем с очистки всех файлов из шаблона, которые нам не нужны. Удалить все из /src папки, за исключением App.js, index.js и index.css.

Для index.css, я просто скопировать и вставить CSS из Примитивный, простой CSS шаблонного я сделал, как точка этого приложения является работа по React и не заботиться о дизайне. Этот шаблон CSS просто добавляет некоторые разумные значения по умолчанию и простую сетку, чтобы мы могли начать создание прототипа.

В index.js, мы упростим это, удалив ссылки на Service Workers.

index.js

import React from ‘react’

import ReactDOM from ‘react-dom’

import ‘./index.css’

import App from ‘./App’

ReactDOM.render(<App />, document.getElementById(‘root’))

И App.js я сделаю простой, функциональный компонент App вместо класса.

App.js

import React from ‘react’

const App = () => {

  return (

    <div className=»container»>

      <h1>CRUD App with Hooks</h1>

      <div className=»flex-row»>

        <div className=»flex-large»>

          <h2>Add user</h2>

        </div>

        <div className=»flex-large»>

          <h2>View users</h2>

        </div>

      </div>

    </div>

  )

}

export default App

Теперь у нас есть начальная настройка и скелет для приложения.

Стэйт против стэйта Крючка

Если мы посмотрим на очень простой пример компонента класса с состоянием и функционального компонента с состоянием Хука, мы увидим сходства и различия. С состоянием класса вы получаете один основной объект состояния, и вы обновляете, используя методы класса и setState().

Я сделаю небольшой пример кода, как если бы это была библиотека, и у вас есть книги, которые имеют состояние.

Пример состояния компонента класса

class App extends Component {

  initialState = {

    title: »,

    available: false,

  }

  state = initialState

  updateBook = book => {

    this.setState({ title: book.title, available: book.available })

  }

}

С состоянием Hook, для каждого типа состояния есть геттер и сеттер (вы можете иметь столько, сколько хотите), и мы, очевидно, создаем функции вместо методов.

Пример состояния крючка

const App = () => {

  const initialBookState = {

    title: »,

    available: false,

  }

  const [book, setBook] = useState(initialBookState)

  const updateBook = book => {

    setBook({ title: book.title, available: book.available })

  }

}

Я не собираюсь углубляться в обоснование хуков по сравнению с компонентами классов, так как вы можете прочитать все об этом в предисловии к React’s Hooks . Я просто покажу вам, как с ними работать, чтобы создать функциональное приложение.

Настройка вида

Первое, что мы сделаем, это создадим пример данных и таблицу для их отображения для представления. Создайте новый каталог с именем tables в src и файл внутри с именем UserTable.js. Мы сделаем скелет для стола.

Столы / UserTable.js

import React from ‘react’

const UserTable = () => (

  <table>

    <thead>

      <tr>

        <th>Name</th>

        <th>Username</th>

        <th>Actions</th>

      </tr>

    </thead>

    <tbody>

      <tr>

        <td>Name data</td>

        <td>Username data</td>

        <td>

          <button className=»button muted-button»>Edit</button>

          <button className=»button muted-button»>Delete</button>

        </td>

      </tr>

    </tbody>

  </table>

)

export default UserTable

Теперь просто импортируйте файл и добавьте новый компонент.

App.js

import React from ‘react’

import UserTable from ‘./tables/UserTable’

const App = () => {

  return (

    <div className=»container»>

      <h1>CRUD App with Hooks</h1>

      <div className=»flex-row»>

        <div className=»flex-large»>

          <h2>Add user</h2>

        </div>

        <div className=»flex-large»>

          <h2>View users</h2>

          <UserTable />

        </div>

      </div>

    </div>

  )

}

export default App

Давайте введем несколько случайных фиктивных данных и useState импорт из React.

App.js

import React, { useState } from ‘react’

import UserTable from ‘./tables/UserTable’

const App = () => {

  const usersData = [

    { id: 1, name: ‘Tania’, username: ‘floppydiskette’ },

    { id: 2, name: ‘Craig’, username: ‘siliconeidolon’ },

    { id: 3, name: ‘Ben’, username: ‘benisphere’ },

  ]

  const [users, setUsers] = useState(usersData)

  return (

    <div className=»container»>

      <h1>CRUD App with Hooks</h1>

      <div className=»flex-row»>

        <div className=»flex-large»>

          <h2>Add user</h2>

        </div>

        <div className=»flex-large»>

          <h2>View users</h2>

          <UserTable users={users} />

        </div>

      </div>

    </div>

  )

}

export default App

Реквизит работает так же, как и раньше. Мы отобразим пользовательские данные, которые мы отправили, и отобразим свойства для каждого пользователя, или отобразим сообщение, если пользователей нет. Кнопки редактирования и удаления еще ни к чему не подключены, поэтому они ничего не будут делать.

UserTable.js

import React from ‘react’

const UserTable = props => (

  <table>

    <thead>

      <tr>

        <th>Name</th>

        <th>Username</th>

        <th>Actions</th>

      </tr>

    </thead>

    <tbody>

      {props.users.length > 0 ? (

        props.users.map(user => (

          <tr key={user.id}>

            <td>{user.name}</td>

            <td>{user.username}</td>

            <td>

              <button className=»button muted-button»>Edit</button>

              <button className=»button muted-button»>Delete</button>

            </td>

          </tr>

        ))

      ) : (

        <tr>

          <td colSpan={3}>No users</td>

        </tr>

      )}

    </tbody>

  </table>

)

export default UserTable

Мы сразу перейдем к кнопкам редактирования и удаления. Теперь, когда базовое представление настроено, давайте добавим функционал добавления.

Добавление нового пользователя

Мы собираемся настроить форму для добавления нового пользователя.

Самое первое, что мы можем сделать, — это создать реальную функцию, которая добавит нового пользователя в состояние. У нас есть setUsers функция автоматически useState, поэтому мы будем использовать ее для обновления состояния пользователя.

Поскольку мы не используем реальный API и базу данных, которая, вероятно, будет иметь идентификатор с автоматическим увеличением, я собираюсь увеличить идентификатор нового пользователя вручную. Эта функция будет принимать user объект в качестве параметра и добавлять их в users массив объектов. В …users коде гарантирует, что все предыдущие пользователи остаются в массиве.

App.js

const addUser = user => {

  user.id = users.length + 1

  setUsers([…users, user])

}

Мы собираемся создать для этого компонент, поэтому я просто добавлю ссылку на компонент вверху и вставлю компонент под заголовком «Добавить пользователя». Мы можем пройти addUser()через как опора. Не включайте скобки, когда мы передаем их в качестве ссылки, — <AddUserForm addUser={addUser} />нет <AddUserForm addUser={addUser()} />.

App.js

import React, { useState } from ‘react’

import UserTable from ‘./tables/UserTable’

import AddUserForm from ‘./forms/AddUserForm’

const App = () => {

  const usersData = [

    { id: 1, name: ‘Tania’, username: ‘floppydiskette’ },

    { id: 2, name: ‘Craig’, username: ‘siliconeidolon’ },

    { id: 3, name: ‘Ben’, username: ‘benisphere’ },

  ]

  const [users, setUsers] = useState(usersData)

  const addUser = user => {

    user.id = users.length + 1

    setUsers([…users, user])

  }

  return (

    <div className=»container»>

      <h1>CRUD App with Hooks</h1>

      <div className=»flex-row»>

        <div className=»flex-large»>

          <h2>Add user</h2>

          <AddUserForm addUser={addUser} />

        </div>

        <div className=»flex-large»>

          <h2>View users</h2>

          <UserTable users={users} />

        </div>

      </div>

    </div>

  )

}

export default App

Теперь нам нужно создать форму, которую вы можете использовать для добавления нового пользователя. Давайте создадим forms подкаталог с файлом внутри, который называется AddUserForm.js.

AddUserForm.js

import React, { useState } from ‘react’

const AddUserForm = props => {

  return (

    <form>

      <label>Name</label>

      <input type=»text» name=»name» value=»» />

      <label>Username</label>

      <input type=»text» name=»username» value=»» />

      <button>Add new user</button>

    </form>

  )

}

export default AddUserForm

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

Как и раньше, мы хотим создать какое-то состояние, за исключением того, что это состояние будет временным для отслеживания того, что в данный момент находится в форме добавления пользователя.

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

AddUserForm.js

const initialFormState = { id: null, name: », username: » }

const [user, setUser] = useState(initialFormState)

Теперь мы создадим функцию для обновления состояния в форме. Event всегда передается любому on событию в DOM, так что вы увидите это как параметр функции. Деструктуризация объекта позволит нам легко получить name (ключ) и value из формы. Наконец, мы установим пользователя так же, как и для App компонента, за исключением того, что на этот раз мы используем вычисляемые имена свойств для динамической установки имени (использования [name]) и значения.

const handleInputChange = event => {

  const { name, value } = event.target

  setUser({ …user, [name]: value })

}

Если вы не понимаете, что происходит, попробуйте поиграться с console.log(event)функцией обработки ввода.

Теперь мы извлекаем значения из объекта состояния и ссылаемся на нашу функцию в onChange событии.

<form>

  <label>Name</label>

  <input type=»text» name=»name» value={user.name} onChange={handleInputChange} />

  <label>Username</label>

  <input type=»text» name=»username» value={user.username} onChange={handleInputChange} />

  <button>Add new user</button>

</form>

Последнее, о чем нужно позаботиться, это отправка формы обратно App компоненту. Когда мы передали функцию props, мы будем использовать реквизит для доступа к функции. Я собираюсь написать onSubmit функцию, и мы предотвратим отправку формы по умолчанию. Я добавил немного проверки, чтобы убедиться, что пустые значения не могут быть отправлены, и отправил пользователя в функцию добавления. Наконец, я использую метод установки для сброса формы к ее начальному значению после успешной отправки.

<form

  onSubmit={event => {

    event.preventDefault()

    if (!user.name || !user.username) return

    props.addUser(user)

    setUser(initialFormState)

  }}

К счастью, этот код довольно прост, так как нам не нужно беспокоиться об асинхронных вызовах API.

Вот наш полный AddUserForm компонент.

AddUserForm.js

import React, { useState } from ‘react’

const AddUserForm = props => {

  const initialFormState = { id: null, name: », username: » }

  const [user, setUser] = useState(initialFormState)

  const handleInputChange = event => {

    const { name, value } = event.target

    setUser({ …user, [name]: value })

  }

  return (

    <form

      onSubmit={event => {

        event.preventDefault()

        if (!user.name || !user.username) return

        props.addUser(user)

        setUser(initialFormState)

      }}

    >

      <label>Name</label>

      <input type=»text» name=»name» value={user.name} onChange={handleInputChange} />

      <label>Username</label>

      <input type=»text» name=»username» value={user.username} onChange={handleInputChange} />

      <button>Add new user</button>

    </form>

  )

}

export default AddUserForm

Здорово.

Удаление пользователя

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

Ниже addUser в примере App.js мы создадим deleteUser, который будет принимать идентификатор пользователя и отфильтровать их из массива пользователя.

const deleteUser = id => {

  setUsers(users.filter(user => user.id !== id))

}

Мы передаем эту функцию через реквизит UserTable.

<UserTable users={users} deleteUser={deleteUser} />

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

<button onClick={() => props.deleteUser(user.id)} className=»button muted-button»>

  Delete

</button>

Теперь вы можете удалить некоторых или всех пользователей.

Обновление пользователя

Последняя часть головоломки представляет возможность обновления существующих пользователей. Это будет похоже на добавление пользователя, за исключением того, что мы сможем определить, какой пользователь редактируется. В компонентах класса мы бы использовали componentDidUpdate метод жизненного цикла для достижения этой цели, но теперь мы будем использовать Effect HookКрюк Эффект подобен componentDidMount и в componentDidUpdate сочетании.

То, как мы собираемся структурировать это, когда действие Edit для пользователя выбрано, форма «Add user» станет формой «Edit user», и она будет предварительно заполнена данными от выбранного пользователя. Вы можете отменить режим редактирования или отправить изменение, которое обновит выбранного пользователя и выйдет из режима редактирования.

Давайте начнем. Во-первых,  App.js первое, что мы хотим сделать, это сделать состояние для того, включен ли режим редактирования. Это начнется как ложное.

App.js

const [editing, setEditing] = useState(false)

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

const initialFormState = { id: null, name: », username: » }

Нам нужен способ увидеть и обновить, кто является редактируемым текущим пользователем, поэтому мы применим этого пустого пользователя к currentUser состоянию.

const [currentUser, setCurrentUser] = useState(initialFormState)

Когда для пользователя выбран параметр «Редактировать», он должен включить режим редактирования и установить текущего пользователя, что мы и сделаем в этой editRow функции.

const editRow = user => {

  setEditing(true)

  setCurrentUser({ id: user.id, name: user.name, username: user.username })

}

Теперь просто передайте эту функцию так, UserTable как мы сделали deleteUser.

<UserTable users={users} editRow={editRow} deleteUser={deleteUser} />

Над в UserTable.js, мы посылаем user объект над.

UserTable.js

<button

  onClick={() => {

    props.editRow(user)

  }}

  className=»button muted-button»

  Edit

</button>

Теперь у нас есть все настройки — есть переключатель режима редактирования и кнопка, которая переводит текущего пользователя в состояние при переключении переключателя режима редактирования.

Давайте создадим реальную функцию, которая будет вызываться при отправке формы редактирования. В отличие от delete (которая фильтрует пользователя по ID) или add (которая добавляет пользователя в массив), функция обновления должна отображать массив и обновлять пользователя, который соответствует переданному идентификатору.

Это означает, что мы будем принимать два параметра — обновленный пользовательский объект и идентификатор — и будем использовать троичную операцию, чтобы отобразить пользователей и найти тот, который мы хотим обновить.

App.js

const updateUser = (id, updatedUser) => {

  setEditing(false)

  setUsers(users.map(user => (user.id === id ? updatedUser : user)))

}

Нам просто нужно сделать саму форму редактирования.

Создать forms/EditUserForm.js. Большая часть будет такой же, как форма добавления. Пока единственное отличие состоит в том, что мы будем устанавливать состояние напрямую с currentUser помощью реквизита. Также есть кнопка отмены, которая просто отключит режим редактирования.

EditUserForm.js

import React, { useState } from ‘react’

const EditUserForm = props => {

  const [user, setUser] = useState(props.currentUser)

  const handleInputChange = event => {

    const { name, value } = event.target

    setUser({ …user, [name]: value })

  }

  return (

    <form

      onSubmit={event => {

        event.preventDefault()

        props.updateUser(user.id, user)

      }}

    >

      <label>Name</label>

      <input type=»text» name=»name» value={user.name} onChange={handleInputChange} />

      <label>Username</label>

      <input type=»text» name=»username» value={user.username} onChange={handleInputChange} />

      <button>Update user</button>

      <button onClick={() => props.setEditing(false)} className=»button muted-button»>

        Cancel

      </button>

    </form>

  )

}

export default EditUserForm

Теперь нам нужно перенести форму редактирования App.js, а также создать переключатель для отображения формы добавления или редактирования.

Сначала внесите компонент.

App.js

import EditUserForm from ‘./forms/EditUserForm’

Затем создайте переключатель. Мы будем использовать троичную операцию, чтобы проверить, является ли editing состояние истинным или нет. Если это правда, показать форму редактирования. Если false, показать форму добавления. Обязательно передайте все созданные нами функции компоненту редактирования.

App.js

<div className=»flex-large»>

  {editing ? (

    <div>

      <h2>Edit user</h2>

      <EditUserForm

        editing={editing}

        setEditing={setEditing}

        currentUser={currentUser}

        updateUser={updateUser}

      />

    </div>

  ) : (

    <div>

      <h2>Add user</h2>

      <AddUserForm addUser={addUser} />

    </div>

  )}

</div>

Итак, в этот момент нажатие на кнопку «Изменить» должно переключить режим редактирования, и вы сможете обновить пользователя. Но мы закончили?

Использование Effect Hook

Если вы немного поиграете с этим, вы заметите проблему. Два, на самом деле. Если вы начнете редактировать одного пользователя, а затем попытаетесь переключиться на другого, ничего не произойдет. Почему? Ну, компонент уже открыт, и хотя состояние родительского элемента изменилось, он не зарегистрирован до реквизита.

Это где Effect Hook вступает в действие. Мы хотим, чтобы EditUserForm компонент знал, что реквизиты изменились, что мы сделали бы раньше componentDidUpdate.

Первый шаг — ввести useEffect.

EditUserForm.js

import React, { useState, useEffect } from ‘react’

EditUserForm.js

useEffect(() => {

  setUser(props.currentUser)

}, [props])

В Effect Hook мы создаем функцию обратного вызова, которая обновляет user состояние, добавляя новую пропу. Раньше нам нужно было сравнивать if (prevProps.currentUser !== this.state.currentUser), но с помощью Effect Hook мы можем просто пройти, [props] чтобы сообщить, что мы смотрим реквизит.

Использование [props]массива аналогично использованию componentDidUpdate. Если вы делаете одноразовое событие, подобное componentDidMount, вы можете [] вместо этого передать пустой array ( ).

Теперь, если вы попытаетесь изменить редактируемого пользователя, он будет работать правильно!

Я сказал, что здесь есть две проблемы, и другая проблема заключается в том, что вы можете удалить пользователя, пока он редактируется. Мы можем решить эту проблему, добавив setEditing(false) к deleteUser функции в App.js.

Вот и все. У нас есть полное CRUD-приложение, использующее React State и Effect hooks.

Вывод

Я не охватил все варианты использования Hooks или все функциональные возможности, но я попытался привести рабочий пример полной, хотя и простой, программы React. Для полного FAQ по всем вопросам, связанным с крючками, ознакомьтесь с FAQ по крюкам.

Если по пути вы заблудились, не забудьте проверить демо-версию и исходники.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.