machinelearningmastery.ru

Машинное обучение, нейронные сети, искусственный интеллект
Header decor

Home

Как использовать Bootstrap с React

Дата публикации Oct 3, 2019

Подпишитесь на мой список рассылки сейчас наhttp://jauyeung.net/subscribe/,

Следуйте за мной в Твиттере наhttps://twitter.com/AuMayeung

React - это простая библиотека для создания интерактивных интерфейсных веб-приложений. Его набор функций является базовым. Он предоставляет вам компонентную архитектуру для создания веб-приложений. Каждый компонент выполняет небольшую работу в приложении, и они могут быть вложены друг в друга или помещены рядом. Из-за этого легко добавлять сторонние библиотеки. Bootstrap - это хорошо известный пользовательский интерфейс, созданный Twitter. Это был адаптер для React путем создания компонентов React с Bootstrap, чтобы его можно было использовать с React.

Для проверки формы вам необходимо использовать стороннюю библиотеку. Формик и Юп, расположенный по адресуhttps://github.com/jaredpalmer/formikа такжеhttps://github.com/jquense/yupработать вместе, чтобы мы могли позаботиться о большинстве потребностей в проверке формы. Formik позволяет нам создавать формы и отображать ошибки, а также обрабатывать изменения значений форм, что является другой вещью, которую мы должны сделать всей своей рукой в ​​противном случае. Да, давайте напишем схему для проверки наших полей формы. Он может проверять почти все, с общим кодом проверки, таким как электронная почта, и обязательными полями, доступными как встроенные функции. Он также может проверять поля, которые зависят от других полей, например формат почтового индекса в зависимости от страны. Формы начальной загрузки могут использоваться без проблем с Formik и Yup.

В этой статье мы создадим приложение адресной книги, в котором будут использованы эти библиотеки, а также React Bootstrap, который отлично интегрируется с указанными выше библиотеками для создания форм. Для начала нам нужно запустить Create React App, чтобы создать приложение. Мы бегаемnpx create-react-app address-bookсоздать папку проекта приложения с исходными файлами. Приложение будет иметь домашнюю страницу для отображения контактов, и мы откроем модальное окно для добавления контакта. Будет таблица, в которой отображаются все контакты и кнопки «Редактировать» и «Удалить» в каждой строке для редактирования или удаления каждого контакта. Контакты будут храниться в центральном магазине Redux для хранения контактов в центральном месте, что облегчает доступ к ним. React Router будет использоваться для маршрутизации. Контакты будут сохранены в бэкэнде, созданном с использованием пакета JSON-сервера, расположенного по адресуhttps://github.com/typicode/json-server,

Как только это будет сделано, мы должны установить несколько библиотек. Чтобы установить библиотеки, которые мы упоминали выше, мы запускаемnpm i axios bootstrap formik react-bootstrap react-redux react-router-dom yup, Axios - это HTTP-клиент, который мы используем для выполнения HTTP-запросов к серверу.react-router-domимя пакета для последней версии React Router.

Теперь, когда у нас установлены все библиотеки, мы можем приступить к созданию приложения. Все файлы будут вsrcпапка, если не указано иное. Сначала мы работаем в магазине Redux. Мы создаем файл с именемactionCreator.jsвsrcпапку и добавьте следующее:

import { SET_CONTACTS } from './actions';const setContacts = (contacts) => {
return {
type: SET_CONTACTS,
payload: contacts
}
};export { setContacts };

Это создатель действия для создания действия по хранению контактов в магазине.

Мы создаем еще один файл с именемactions.jsи добавить:

const SET_CONTACTS = 'SET_CONTACTS';export { SET_CONTACTS };

Это просто есть константа типа для отправки действия.

ВApp.jsмы заменим то, что существует, следующим:

import React from 'react';
import { Router, Route, Link } from "react-router-dom";
import HomePage from './HomePage';
import { createBrowserHistory as createHistory } from 'history'
import Navbar from 'react-bootstrap/Navbar';
import Nav from 'react-bootstrap/Nav';
import './App.css';
const history = createHistory();function App() {
return (
<div className="App">
<Router history={history}>
<Navbar bg="primary" expand="lg" variant="dark" >
<Navbar.Brand href="#home">Address Book App</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="mr-auto">
<Nav.Link href="/">Home</Nav.Link>
</Nav>
</Navbar.Collapse>
</Navbar>
<Route path="/" exact component={HomePage} />
</Router>
</div>
);
}export default App;

Здесь мы добавляем панель навигации и показываем наши маршруты, маршрутизируемые маршрутизатором React. ВApp.cssзаменяем существующий код на:

.App {
text-align: center;
}

центрировать некоторый текст.

Далее мы строим нашу контактную форму. Это самая логичная часть нашего приложения. Мы создаем файл с именемContactForm.jsи добавить:

import React from 'react';
import { Formik } from 'formik';
import Form from 'react-bootstrap/Form';
import Col from 'react-bootstrap/Col';
import InputGroup from 'react-bootstrap/InputGroup';
import Button from 'react-bootstrap/Button';
import * as yup from 'yup';
import { COUNTRIES } from './exports';
import PropTypes from 'prop-types';
import { addContact, editContact, getContacts } from './requests';
import { connect } from 'react-redux';
import { setContacts } from './actionCreators';const schema = yup.object({
firstName: yup.string().required('First name is required'),
lastName: yup.string().required('Last name is required'),
address: yup.string().required('Address is required'),
city: yup.string().required('City is required'),
region: yup.string().required('Region is required'),
country: yup.string().required('Country is required').default('Afghanistan'),
postalCode: yup
.string()
.when('country', {
is: 'United States',
then: yup.string().matches(/^[0-9]{5}(?:-[0-9]{4})?$/, 'Invalid postal code'),
})
.when('country', {
is: 'Canada',
then: yup.string().matches(/^[A-Za-z]\d[A-Za-z][ -]?\d[A-Za-z]\d$/, 'Invalid postal code'),
})
.required(),
phone: yup
.string()
.when('country', {
is: country => ["United States", "Canada"].includes(country),
then: yup.string().matches(/^[2-9]\d{2}[2-9]\d{2}\d{4}$/, 'Invalid phone nunber')
})
.required(),
email: yup.string().email('Invalid email').required('Email is required'),
age: yup.number()
.required('Age is required')
.min(0, 'Minimum age is 0')
.max(200, 'Maximum age is 200'),
});function ContactForm({
edit,
onSave,
setContacts,
contact,
onCancelAdd,
onCancelEdit,
}) {
const handleSubmit = async (evt) => {
const isValid = await schema.validate(evt);
if (!isValid) {
return;
}
if (!edit) {
await addContact(evt);
}
else {
await editContact(evt);
}
const response = await getContacts();
setContacts(response.data);
onSave();
}return (
<div className="form">
<Formik
validationSchema={schema}
onSubmit={handleSubmit}
initialValues={contact || {}}
>
{({
handleSubmit,
handleChange,
handleBlur,
values,
touched,
isInvalid,
errors,
}) => (
<Form noValidate onSubmit={handleSubmit}>
<Form.Row>
<Form.Group as={Col} md="12" controlId="firstName">
<Form.Label>First name</Form.Label>
<Form.Control
type="text"
name="firstName"
placeholder="First Name"
value={values.firstName || ''}
onChange={handleChange}
isInvalid={touched.firstName && errors.firstName}
/>
<Form.Control.Feedback type="invalid">
{errors.firstName}
</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} md="12" controlId="lastName">
<Form.Label>Last name</Form.Label>
<Form.Control
type="text"
name="lastName"
placeholder="Last Name"
value={values.lastName || ''}
onChange={handleChange}
isInvalid={touched.firstName && errors.lastName}
/><Form.Control.Feedback type="invalid">
{errors.lastName}
</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} md="12" controlId="address">
<Form.Label>Address</Form.Label>
<InputGroup>
<Form.Control
type="text"
placeholder="Address"
aria-describedby="inputGroupPrepend"
name="address"
value={values.address || ''}
onChange={handleChange}
isInvalid={touched.address && errors.address}
/>
<Form.Control.Feedback type="invalid">
{errors.address}
</Form.Control.Feedback>
</InputGroup>
</Form.Group>
</Form.Row>
<Form.Row>
<Form.Group as={Col} md="12" controlId="city">
<Form.Label>City</Form.Label>
<Form.Control
type="text"
placeholder="City"
name="city"
value={values.city || ''}
onChange={handleChange}
isInvalid={touched.city && errors.city}
/><Form.Control.Feedback type="invalid">
{errors.city}
</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} md="12" controlId="region">
<Form.Label>Region</Form.Label>
<Form.Control
type="text"
placeholder="Region"
name="region"
value={values.region || ''}
onChange={handleChange}
isInvalid={touched.region && errors.region}
/>
<Form.Control.Feedback type="invalid">
{errors.region}
</Form.Control.Feedback>
</Form.Group><Form.Group as={Col} md="12" controlId="country">
<Form.Label>Country</Form.Label>
<Form.Control
as="select"
placeholder="Country"
name="country"
onChange={handleChange}
value={values.country || ''}
isInvalid={touched.region && errors.country}>
{COUNTRIES.map(c => <option key={c} value={c}>{c}</option>)}
</Form.Control>
<Form.Control.Feedback type="invalid">
{errors.country}
</Form.Control.Feedback>
</Form.Group><Form.Group as={Col} md="12" controlId="postalCode">
<Form.Label>Postal Code</Form.Label>
<Form.Control
type="text"
placeholder="Postal Code"
name="postalCode"
value={values.postalCode || ''}
onChange={handleChange}
isInvalid={touched.postalCode && errors.postalCode}
/><Form.Control.Feedback type="invalid">
{errors.postalCode}
</Form.Control.Feedback>
</Form.Group><Form.Group as={Col} md="12" controlId="phone">
<Form.Label>Phone</Form.Label>
<Form.Control
type="text"
placeholder="Phone"
name="phone"
value={values.phone || ''}
onChange={handleChange}
isInvalid={touched.phone && errors.phone}
/><Form.Control.Feedback type="invalid">
{errors.phone}
</Form.Control.Feedback>
</Form.Group><Form.Group as={Col} md="12" controlId="email">
<Form.Label>Email</Form.Label>
<Form.Control
type="text"
placeholder="Email"
name="email"
value={values.email || ''}
onChange={handleChange}
isInvalid={touched.email && errors.email}
/><Form.Control.Feedback type="invalid">
{errors.email}
</Form.Control.Feedback>
</Form.Group><Form.Group as={Col} md="12" controlId="age">
<Form.Label>Age</Form.Label>
<Form.Control
type="text"
placeholder="Age"
name="age"
value={values.age || ''}
onChange={handleChange}
isInvalid={touched.age && errors.age}
/><Form.Control.Feedback type="invalid">
{errors.age}
</Form.Control.Feedback>
</Form.Group>
</Form.Row>
<Button type="submit" style={{ 'marginRight': '10px' }}>Save</Button>
<Button type="button" onClick={edit ? onCancelEdit : onCancelAdd}>Cancel</Button>
</Form>
)}
</Formik>
</div>
);
}ContactForm.propTypes = {
edit: PropTypes.bool,
onSave: PropTypes.func,
onCancelAdd: PropTypes.func,
onCancelEdit: PropTypes.func,
contact: PropTypes.object
}const mapStateToProps = state => {
return {
contacts: state.contacts,
}
}const mapDispatchToProps = dispatch => ({
setContacts: contacts => dispatch(setContacts(contacts))
})export default connect(
mapStateToProps,
mapDispatchToProps
)(ContactForm);

Мы используем Formik, чтобы облегчить построение нашей контактной формы здесь, с нашим BoostrapFormкомпонент, вложенный вFormikкомпонент, так что мы можем использовать Formik'shandleChange,handleSubmit,values,touchedа такжеerrorsпараметры.handleChangeэто функция, которая позволяет нам обновлять данные поля формы из входных данных без написания кода самостоятельно.handleSubmitэто функция, которую мы передали вonSubmitобработчикFormikсоставная часть. Параметром в функции являются данные, которые мы ввели, с именем поля в качестве ключа, как определеноnameатрибут каждого поля и значение каждого поля в качестве значения этих ключей. Обратите внимание, что в каждомvalueопора, у нас есть||''поэтому мы не получаем неопределенные значения и не допускаем срабатывания неконтролируемых предупреждений форм.

Чтобы отобразить сообщения проверки формы, мы должны передатьisInvalidопора каждомуForm.Controlсоставная часть.schemaобъект это то, чтоFormikпроверим для проверки формы. Аргумент вrequiredфункция - это сообщение об ошибке валидации. Второй аргументmatches,minа такжеmaxФункции также являются сообщениями проверки.

ПараметрContactFormФункция это реквизит, который мы передадим изHomePageкомпонент, который мы будем строить позже.handleSubmitфункция проверяет, являются ли данные действительными, а если это так, то она приступает к сохранению в зависимости от того, добавляет ли он или редактирует контакт. Затем, когда сохранение прошло успешно, мы устанавливаем контакты в магазине и звонимonSaveprop, которая является функцией для закрытия модальной формы, в которой находится форма. Модальная часть будет определена на домашней странице.

mapStateToPropsэто функция, предоставляемая React Redux, так что мы можем отобразить состояние непосредственно на реквизиты нашего компонента, как предполагает название функции mapDispatchToPropsпозволяет нам вызывать функцию в подпорках компонента под названиемsetContactsотправить действие, как мы определили вactionCreators.js

Далее мы создаем файл с именемexports.js, и положи:

export const COUNTRIES = ["Afghanistan", "Albania", "Algeria", "Andorra", "Angola", "Anguilla", "Antigua &amp; Barbuda", "Argentina", "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan", "Bahamas"
, "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia", "Bosnia &amp; Herzegovina", "Botswana", "Brazil", "British Virgin Islands"
, "Brunei", "Bulgaria", "Burkina Faso", "Burundi", "Cambodia", "Cameroon", "Canada", "Cape Verde", "Cayman Islands", "Chad", "Chile", "China", "Colombia", "Congo", "Cook Islands", "Costa Rica"
, "Cote D Ivoire", "Croatia", "Cruise Ship", "Cuba", "Cyprus", "Czech Republic", "Denmark", "Djibouti", "Dominica", "Dominican Republic", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea"
, "Estonia", "Ethiopia", "Falkland Islands", "Faroe Islands", "Fiji", "Finland", "France", "French Polynesia", "French West Indies", "Gabon", "Gambia", "Georgia", "Germany", "Ghana"
, "Gibraltar", "Greece", "Greenland", "Grenada", "Guam", "Guatemala", "Guernsey", "Guinea", "Guinea Bissau", "Guyana", "Haiti", "Honduras", "Hong Kong", "Hungary", "Iceland", "India"
, "Indonesia", "Iran", "Iraq", "Ireland", "Isle of Man", "Israel", "Italy", "Jamaica", "Japan", "Jersey", "Jordan", "Kazakhstan", "Kenya", "Kuwait", "Kyrgyz Republic", "Laos", "Latvia"
, "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg", "Macau", "Macedonia", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Mauritania"
, "Mauritius", "Mexico", "Moldova", "Monaco", "Mongolia", "Montenegro", "Montserrat", "Morocco", "Mozambique", "Namibia", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia"
, "New Zealand", "Nicaragua", "Niger", "Nigeria", "Norway", "Oman", "Pakistan", "Palestine", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines", "Poland", "Portugal"
, "Puerto Rico", "Qatar", "Reunion", "Romania", "Russia", "Rwanda", "Saint Pierre &amp; Miquelon", "Samoa", "San Marino", "Satellite", "Saudi Arabia", "Senegal", "Serbia", "Seychelles"
, "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "South Africa", "South Korea", "Spain", "Sri Lanka", "St Kitts &amp; Nevis", "St Lucia", "St Vincent", "St. Lucia", "Sudan"
, "Suriname", "Swaziland", "Sweden", "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "Timor L'Este", "Togo", "Tonga", "Trinidad &amp; Tobago", "Tunisia"
, "Turkey", "Turkmenistan", "Turks &amp; Caicos", "Uganda", "Ukraine", "United Arab Emirates", "United Kingdom", "United States", "United States Minor Outlying Islands", "Uruguay"
, "Uzbekistan", "Venezuela", "Vietnam", "Virgin Islands (US)", "Yemen", "Zambia", "Zimbabwe"];

Это страны для поля страны в форме.

ВHomePage.jsмы ставим:

import React from 'react';
import { useState, useEffect } from 'react';
import Table from 'react-bootstrap/Table'
import ButtonToolbar from 'react-bootstrap/ButtonToolbar';
import Button from 'react-bootstrap/Button';
import Modal from 'react-bootstrap/Modal'
import ContactForm from './ContactForm';
import './HomePage.css';
import { connect } from 'react-redux';
import { getContacts, deleteContact } from './requests';function HomePage() {
const [openAddModal, setOpenAddModal] = useState(false);
const [openEditModal, setOpenEditModal] = useState(false);
const [initialized, setInitialized] = useState(false);
const [selectedId, setSelectedId] = useState(0);
const [selectedContact, setSelectedContact] = useState({});
const [contacts, setContacts] = useState([]);const openModal = () => {
setOpenAddModal(true);
}const closeModal = () => {
setOpenAddModal(false);
setOpenEditModal(false);
getData();
}const cancelAddModal = () => {
setOpenAddModal(false);
}const editContact = (contact) => {
setSelectedContact(contact);
setOpenEditModal(true);
}const cancelEditModal = () => {
setOpenEditModal(false);
}const getData = async () => {
const response = await getContacts();
setContacts(response.data);
setInitialized(true);
}const deleteSelectedContact = async (id) => {
await deleteContact(id);
getData();
}useEffect(() => {
if (!initialized) {
getData();
}
})return (
<div className="home-page">
<h1>Contacts</h1>
<Modal show={openAddModal} onHide={closeModal} >
<Modal.Header closeButton>
<Modal.Title>Add Contact</Modal.Title>
</Modal.Header>
<Modal.Body>
<ContactForm edit={false} onSave={closeModal.bind(this)} onCancelAdd={cancelAddModal} />
</Modal.Body>
</Modal><Modal show={openEditModal} onHide={closeModal}>
<Modal.Header closeButton>
<Modal.Title>Edit Contact</Modal.Title>
</Modal.Header>
<Modal.Body>
<ContactForm edit={true} onSave={closeModal.bind(this)} contact={selectedContact} onCancelEdit={cancelEditModal} />
</Modal.Body>
</Modal>
<ButtonToolbar onClick={openModal}>
<Button variant="outline-primary">Add Contact</Button>
</ButtonToolbar>
<br />
<Table striped bordered hover>
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Address</th>
<th>City</th>
<th>Country</th>
<th>Postal Code</th>
<th>Phone</th>
<th>Email</th>
<th>Age</th>
<th>Edit</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{contacts.map(c => (
<tr key={c.id}>
<td>{c.firstName}</td>
<td>{c.lastName}</td>
<td>{c.address}</td>
<td>{c.city}</td>
<td>{c.country}</td>
<td>{c.postalCode}</td>
<td>{c.phone}</td>
<td>{c.email}</td>
<td>{c.age}</td>
<td>
<Button variant="outline-primary" onClick={editContact.bind(this, c)}>Edit</Button>
</td>
<td>
<Button variant="outline-primary" onClick={deleteSelectedContact.bind(this, c.id)}>Delete</Button>
</td>
</tr>
))}
</tbody>
</Table>
</div>
);
}const mapStateToProps = state => {
return {
contacts: state.contacts,
}
}export default connect(
mapStateToProps,
null
)(HomePage);

Он имеет таблицу для отображения контактов и кнопки для добавления, редактирования и удаления контактов. Он получает данные один раз при первой загрузке сgetDataвызов функции вuseEffectCall функция обратного вызова.useEffectОбратный вызов вызывается при каждом рендере, поэтому мы хотим установитьinitializedустановите флажок и убедитесь, что он загружается, только еслиtrue,

Обратите внимание, что мы передаем все реквизиты в этом компонентеContactFormсоставная часть. Чтобы передать аргументonClickфункция-обработчик, мы должны вызватьbindна функцию и передать аргумент для функции в качестве второго аргументаbind, Например, в этом файле мы имеемeditContact.bind(this, c), гдеcэто контактный объект.editContactФункция определяется следующим образом:

const editContact = (contact) => {
setSelectedContact(contact);
setOpenEditModal(true);
}

cэтоcontactпараметр, который мы передаем.

Далее мы создаем файл с именемHomePage.cssи положи:

.home-page {
padding: 20px;
}

добавить немного отступов.

Вindex.jsзаменяем существующий код на:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { contactsReducer } from './reducers';
import { Provider } from 'react-redux'
import { createStore, combineReducers } from 'redux'const addressBookApp = combineReducers({
contacts: contactsReducer,
})const store = createStore(addressBookApp)ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
, document.getElementById('root'));// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

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

Затем мы создаем файл с именемreducers.js, и добавить:

import { SET_CONTACTS } from './actions';function contactsReducer(state = {}, action) {
switch (action.type) {
case SET_CONTACTS:
state = JSON.parse(JSON.stringify(action.payload));
return state;
default:
return state
}
}export { contactsReducer };

Это редуктор, в котором мы храним контакты, которые отправляем, вызывая опору, предоставленнуюmapDispatchToPropsфункция в наших компонентах.

Затем мы создаем файл с именемrequests.js, и добавить:

const APIURL = 'http://localhost:3000';
const axios = require('axios');
export const getContacts = () => axios.get(`${APIURL}/contacts`);
export const addContact = (data) => axios.post(`${APIURL}/contacts`, data);
export const editContact = (data) => axios.put(`${APIURL}/contacts/${data.id}`, data);
export const deleteContact = (id) => axios.delete(`${APIURL}/contacts/${id}`);

Эти функции делают наши HTTP-запросы на сервер для сохранения и удаления контактов.

Наконец, вpublic/index.htmlзаменяем существующий код на:

<!DOCTYPE html>
<html lang="en"><head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<link rel="apple-touch-icon" href="logo192.png" />
<link rel="manifest" crossorigin="use-credentials" href="%PUBLIC_URL%/manifest.json" /><!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React Address Book App</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous" />
</head><body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body></html>

изменить заголовок и добавить таблицу стилей Bootstrap.

Теперь мы можем запустить приложение, запустивset PORT=3001 && react-scripts startв Windows илиPORT=3006 react-scripts startв линуксе

Чтобы начать обратно, мы сначала устанавливаемjson-serverпакет, запустивnpm i json-server, Перейдите в папку нашего проекта и запустите:

json-server --watch db.json

Вdb.jsonизмените текст на:

{
"contacts": [
]
}

так что у нас естьcontactsконечные точки определены вrequests.jsимеется в наличии.

В конце мы имеем следующее:

Оригинальная статья

Footer decor

© machinelearningmastery.ru | Ссылки на оригиналы и авторов сохранены. | map