反応アプリにGoogleマップを加えてください


今すぐ私の電子メールリストを購読http://jauyeung.net/subscribe/
に私について来てください
反応は、インタラクティブなフロントエンドのWebアプリを作成するためのシンプルなライブラリです.その機能セットは基本的です.それはWebアプリケーションを構築するためのコンポーネントベースのアーキテクチャを提供します.各コンポーネントは、アプリケーション内の小さなものをし、彼らはお互いに入れ子にすることができますまたは並んで置くことができます.このため、サードパーティライブラリを追加するのは簡単です.Googleマップは、開発者がそれのためのコンポーネントを書いているので、反応アプリに組み込むことができる人気のある地図システムです.
この物語では、我々は、これらのライブラリを使用してアドレス帳のアプリをビルドし、プラスのブートストラップは、フォームを作成する上でこれらのライブラリとの偉大な統合をしています.我々はアプリケーションを足場に作成する反応アプリを実行する必要が起動します.我々は走るnpx create-react-app address-book 最初のファイルでアプリケーションプロジェクトフォルダーを作成するには.アプリは、連絡先を表示し、連絡先を追加するには、モーダルを開くことができますホームページがあります.
すべての連絡先を表示し、各行に編集して削除ボタンを編集したり、各連絡先を削除するテーブルがあります.連絡先は、中央の場所に連絡先を格納するには、中央reduxストアに格納されます.反応ルータはルーティングに使用されます.
コンタクトは、JSONサーバパッケージを使用して生成されたバックエンドに保存されますhttps://github.com/typicode/json-server . これは、Googleのマップの連絡先の場所を表示する必要があります.ユーザーが各列でマップボタンをクリックすると、ユーザーはモーダルで地図を表示します.
フォームの検証のために、サードパーティライブラリを使用する必要があります.フォルククとユウhttps://github.com/jaredpalmer/formik and https://github.com/jquense/yup 私たちはほとんどのフォームの検証ニーズの世話をするために一緒に偉大な作品.FormIkフォームを構築し、エラーを表示し、フォームの値の変更を処理します.yup私たちのフォームフィールドを検証するためのスキーマを書きましょう.これは、ほとんどの何かをチェックすることができます電子メールのような一般的な検証コードや必要なフィールドを組み込み機能としてご利用いただけます.また、国によって郵便番号形式のような他のフィールドに依存するフィールドをチェックすることもできます.ブートストラップフォームはFormickとYUPとシームレスに使用できます.
使用するreact-google-maps 当社の反応アプリにGoogleマップを組み込むにはhttps://www.npmjs.com/package/react-google-maps . それは人気があり、それが反応の最新バージョンで動作するように積極的に維持されます.また使いやすい.
それが完了したら、いくつかのライブラリをインストールする必要があります.上記のライブラリをインストールするにはnpm i axios bootstrap formik react-bootstrap react-redux react-router-dom yup react-google-maps . AxiosはHTTPリクエストをバックエンドにするためのHTTPクライアントです.react-router-dom は、最新のバージョンの反応ルータのパッケージ名です.
我々はすべてのライブラリをインストールしたので、我々はアプリを構築を開始することができます.すべてのファイルはsrc さもなければ言及する以外のフォルダ.最初に、我々はRedux店で働きます.ファイルを作成しますactionCreator.jssrc フォルダを追加し、次のように追加します.
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;
ここでは、ナビゲーションバーを追加し、我々のルートは、反応ルータによってルーティング表示されます.イン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);
私たちは私たちのブートストラップでここで私たちの連絡先を構築を容易にするFormekを使用しますForm 入れ子になったコンポーネントFormik フォルムの使用できるコンポーネントhandleChange , handleSubmit , values , touched and errors パラメータ.handleChange は、自分自身でコードを書くことなく入力からデータフィールドデータを更新させる関数です.handleSubmit 関数は、onSubmit ハンドラFormik コンポーネント.関数のパラメータは、フィールド名をキーとして入力したデータですname 各フィールドの属性と各フィールドの値を、それらのキーの値として指定します.注意してくださいvalue プロップ,我々は||'' それで、我々は未定義の値を得ないで、制御されないフォーム警告が引き起こされるのを防ぎます.
フォームの検証メッセージを表示するには、isInvalid 各プロップForm.Control コンポーネント.The schema オブジェクトは何ですかFormik フォームの検証をチェックします.の議論required 関数は検証エラーメッセージです.The second argument of the matches , min and max 関数も妥当性検査メッセージです.
パラメータContactForm 関数は小道具であり、我々はHomePage 後でビルドするコンポーネント.The handleSubmit 関数は、データが有効かどうかをチェックし、もしそれがあれば、それが追加されているか、連絡先を編集しているかどうかに従って保存されます.保存が成功したら、私たちは店と電話で連絡先を設定しますonSave PROPは、モードを閉じる機能です.モーダルは、ホームページで定義されます.mapStateToProps 関数名が示すように、我々のコンポーネントの小道具に直接状態をマップできるように、Retrixによって提供される関数です.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"];
これらは国の国の形でフィールドです.
次に、Googleマップのコンポーネントを使用して、連絡先の場所を表示します.ファイルを作るMapComponent.js を追加します.
import React from "react";  
import { compose, withProps } from "recompose";  
import { GOOGLE_API_KEY } from "./requests";import {  
  withGoogleMap,  
  GoogleMap,  
  Marker,  
  withScriptjs,  
} from "react-google-maps";const MapComponent = compose(  
  withProps({  
    googleMapURL: `[https://maps.googleapis.com/maps/api/js?key=${GOOGLE_API_KEY}&v=3.exp&libraries=geometry,drawing,places`](https://maps.googleapis.com/maps/api/js?key=${GOOGLE_API_KEY}&v=3.exp&libraries=geometry,drawing,places`),  
    loadingElement: <div style={{ height: `100%` }} />,  
    containerElement: <div style={{ height: `400px` }} />,  
    mapElement: <div style={{ height: `100%` }} />,  
  }),  
  withScriptjs,  
  withGoogleMap  
)(({ lat, lng }) => (  
  <GoogleMap defaultZoom={8} defaultCenter={{ lat, lng }}>  
    <Marker position={{ lat, lng }} />  
  </GoogleMap>  
));

export default MapComponent;
引数withProps ファンクションコールはGoogleMap コンポーネントwithScriptjs GoogleマップからJavaScriptファイルを読み込みます.lat and lng 我々は、緯度と経度でそれぞれの場所を表示するコンポーネントに渡す小道具です.
イン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 MapComponent from "./MapComponent";  
import { connect } from "react-redux";  
import { getContacts, deleteContact, getLatLng } from "./requests";function HomePage() {  
  const [openAddModal, setOpenAddModal] = useState(false);  
  const [openEditModal, setOpenEditModal] = useState(false);  
  const [openMapModal, setOpenMapModal] = useState(false);  
  const [initialized, setInitialized] = useState(false);  
  const [loc, setLoc] = useState({  
    lat: 0,  
    lng: 0,  
  });  
  const [selectedContact, setSelectedContact] = useState({});  
  const [contacts, setContacts] = useState([]); 
  const openModal = () => {  
    setOpenAddModal(true);  
  }; 

  const closeModal = () => {  
    setOpenAddModal(false);  
    setOpenEditModal(false);  
    setOpenMapModal(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();  
  }; 

  const openMap = async contact => {  
    try {  
      const address = `${contact.addressLineOne}, ${contact.addressLineTwo}, ${contact.city}, ${contact.country}`;  
      const response = await getLatLng(address);  
      const loc = response.data.results[0].geometry.location;  
      setLoc(loc);  
      setOpenMapModal(true);  
    } catch (ex) {  
      console.log(ex);  
    }  
  }; 

  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> <Modal show={openMapModal} onHide={closeModal}>  
        <Modal.Header closeButton>  
          <Modal.Title>Map</Modal.Title>  
        </Modal.Header>  
        <Modal.Body>  
          <MapComponent  
            lat={loc.lat}  
            lng={loc.lng}  
          />  
        </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>Map</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={openMap.bind(this, c)}  
                >  
                  Map  
                </Button>  
              </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);
これは、連絡先やボタンを追加するには、編集、および削除連絡先を表示するテーブルがあります.これは、1回目の負荷でデータを取得するgetData 関数呼び出しuseEffect ‘sコールバック関数.useEffect ‘s callbackはすべてのレンダリング業者に対して呼び出されますので、initialized フラグを指定した場合にのみロードされ、true .
このコンポーネントのすべての小道具をContactForm コンポーネント.議論をするonClick ハンドラ関数は、コールする必要がありますbind 関数の引数と2番目の引数としての関数の引数bind . 例えば、このファイルではeditContact.bind(this, c) , どこc はコンタクトオブジェクトです.The editContact 関数は以下のように定義される.
const editContact = (contact) => {  
  setSelectedContact(contact);  
  setOpenEditModal(true);  
}
ccontact パラメータを渡します.
次に、ファイルを作成します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](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 MAPURL = "https://maps.googleapis.com/maps/api/geocode/json?address=";  
const axios = require("axios");  
export const GOOGLE_API_KEY = "your API key";

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}`);

export const getLatLng = address => {  
  return axios.get(  
    `${MAPURL}${encodeURIComponent(address)}&key=${GOOGLE_API_KEY}`  
  );  
};
これらの関数は、私たちの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/](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>
タイトルを変更し、ブートストラップスタイルシートを追加します.
今我々は実行してアプリケーションを実行することができますset PORT=3001 && react-scripts start WindowsまたはPORT=3006 react-scripts start Linuxで.
endを開始するには、まずjson-server 走らせるnpm i json-server . プロジェクトフォルダに移動して実行します.
json-server --watch db.json
インdb.json , テキストを次のように変更します.
{  
  "contacts": [  
  ]  
}
だから我々はcontacts で定義されたエンドポイントrequests.js 利用可能です.