React – Lazy Loading и компоненты высшего порядка (HOC)

Внимание

Данный материал является частью цикла статей «Создаем приложение на React с использованием Redux». Не забудьте посмотреть другие статьи по этой теме :-)

  1. React - Настройка проекта и подготовка компонентов
  2. React - навигация и маршрутизация
  3. React - использование Redux в приложении React
  4. React – Lazy Loading и компоненты высшего порядка (HOC)
  5. React - обработка исключений
  6. React - создание динамических форм и модальных компонентов
  7. React - валидация формы и обработка POST запроса
  8. React - обработка запроса PUT для редактирования данных
  9. React - обработка запроса на удаление данных

Создание компонента высшего порядка (HOC) и маршруты компонента OwnerList

Давайте создадим следующую структуру папок и файл OwnerList.js внутри папки containers:

Прежде чем изменять компонент OwnerList, давайте создадим один компонент более высшего порядка (HOC). Мы собираемся использовать его в качестве помощника для обертывания нашего контента внутри блока return() в компоненте. Как вы уже знаете (из предыдущих сообщений), для блока return() требуется один корневой тег и все содержимое внутри него. Если мы не хотим использовать теги div или p, которые могут испортить наши стили, мы можем создать этот вспомогательный компонент. Он собирается создать оболочку, которая будет возвращать все дочернее содержимое внутри нее, не нарушая стили проекта.

Итак, внутри папки src создадим новую папку и назовем ее hoc. В этой папке мы создадим новую папку и назовем ее Auxiliary. Наконец, давайте создадим новый файл Auxiliary.js:

Теперь нам нужно изменить файл Auxiliary.js:

const auxiliary = (props) => props.children;

export default auxiliary;

Все, что мы здесь делаем, это просто возвращаем дочерний контент, который будет помещен между вспомогательным тегом open и close.

Теперь мы можем изменить файл OwnerList.js:

import React, { Component } from 'react';
import { Table, Col, Row } from 'react-bootstrap';
import { Link } from 'react-router-dom';
import Aux from '../../../hoc/Auxiliary/Auxiliary';

class OwnerList extends Component {
    render() {
        let owners = [];
        return (
            <Aux>
                <Row>
                    <Col mdOffset={10} md={2}>
                        <Link to='/createOwner' >Create Owner</Link>
                    </Col>
                </Row>
                <br />
                <Row>
                    <Col md={12}>
                        <Table responsive striped>
                            <thead>
                                <tr>
                                    <th>Name</th>
                                    <th>Date of birth</th>
                                    <th>Address</th>
                                    <th>Details</th>
                                    <th>Update</th>
                                    <th>Delete</th>
                                </tr>
                            </thead>
                            <tbody>
                                {owners}
                            </tbody>
                        </Table>
                    </Col>
                </Row>
            </Aux>
        )
    }
}

export default OwnerList;

Описание компонента OwnerList

Логика этого компонента довольно проста.

Мы создаем пустой массив owners, который будет содержать всех владельцев базы данных. Над таблицей есть ссылка на Create Component. Мы используем компонент Link библиотеки react-router-dom. Затем нам нужно создать таблицу, в которой будут отображаться все данные из массива, хотя в настоящее время это просто пустой массив.

Давайте продолжим, изменив файл App.js, чтобы добавить маршрут к этому компоненту:

import OwnerList from './Owner/OwnerList/OwnerList';
<Route path="/" exact component={Home} />
<Route path="/owner-list" component={OwnerList} />
<Route path="*" component={NotFound} />

Если мы запустим наше приложение с помощью команды npm start, в результате мы сможем перейти к компоненту OwnerList, щелкнув меню Действия владельца:

Реализация Redux

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

Во-первых, мы должны подключить наш компонент к нашему редьюсеру. Редьюсер настроит состояние и передаст данные этому компоненту как свойство внутри объекта props.

Для этого импортируем connect из библиотеки react-redux и все действия из файла repositoryAction.js:

import { connect } from 'react-redux';
import * as repositoryActions from '../../../store/actions/repositoryActions';

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

const mapStateToProps = (state) => {     
    return {  
       data: state.data     
    } 
}
const mapDispatchToProps = (dispatch) => {
     return {
         onGetData: (url, props) => dispatch(repositoryActions.getData(url, props))     
    } 
} 
export default connect(mapStateToProps, mapDispatchToProps)(OwnerList);

Функция mapStateToProps отображает свойство data из объекта initialState в repositoryReducer со свойством data внутри компонента OwnerList. Чтобы получить доступ к этому свойству data, нам просто нужно назвать его так: this.props.data.

Функция mapDispatchToProps создает дополнительное свойство onGetData. Мы можем вызвать его с помощью оператора: this.props.onGetData. Затем он отправит действие внутри файла repositoryActions.js, который будет получать данные с сервера.

Обзор Redux

Прямо сейчас, если мы посмотрим на диаграмму из предыдущего поста, то все встанет на свои места. Из этого компонента мы вызываем действие внутри файла repositoryActions.js. Это действие извлекает данные с сервера и запускает редьюсер. Наш редьюсер обновляет состояние, изменяя свойство данных внутри объекта initialState. Наконец, Central Store отображает это свойство данных с этим компонентом с помощью функции mapStateToProps.

Создание компонента-владельца

Поскольку мы хотим отображать несколько владельцев внутри компонента OwnerList, давайте создадим компонент Owner и импортируем его в компонент OwnerList.

В папке компонентов создайте следующую структуру:

Для работы с датами необходимо установить еще одну стороннюю библиотеку react-moment:

npm install --save react-moment

Нам также необходимо установить библиотеку моментов:

npm install --save moment

Теперь мы можем изменить файл Owner.js:

import React from 'react';
import Aux from '../../../hoc/Auxiliary/Auxiliary';
import Moment from 'react-moment';
import { Button } from 'react-bootstrap';

const redirectToOwnerDetails = (id, history) => {
    history.push('/ownerDetails/' + id);
}

const redirectToUpdateOwner = (id, history) => {
    history.push('/updateOwner/' + id);
}

const rediterctToDeleteOwner = (id, history) => {
    history.push('/deleteOwner/' + id);
}

const owner = (props) => {
    return (
        <Aux>
            <tr>
                <td>{props.owner.name}</td>
                <td><Moment format="DD/MM/YYYY">{props.owner.dateOfBirth}</Moment></td>
                <td>{props.owner.address}</td>
                <td>
                    <Button onClick={() => redirectToOwnerDetails(props.owner.id, props.history)}>Details</Button>
                </td>
                <td>
                    <Button bsStyle="success" onClick={() => redirectToUpdateOwner(props.owner.id, props.history)}>Update</Button>
                </td>
                <td>
                    <Button bsStyle="danger" onClick={() => rediterctToDeleteOwner(props.owner.id, props.history)}>Delete</Button>
                </td>
            </tr>
        </Aux>
    )
}

export default owner;

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

Важно отметить, что объект props имеет свойство history, которое позволяет нам перемещаться программно. Кроме того, мы используем формат даты: «DD/MM/YYYY», чтобы показать, насколько легко работать с форматами с помощью библиотеки Moment. Для компонентов создания и редактирования мы будем использовать формат MM/DD/YYYY. Все, что осталось сделать, - это импортировать этот компонент в компонент OwnerList и отобразить всех владельцев на экране.

Компонент для вывода Owner

Давайте импортируем компонент Owner внутри компонента OwnerList:

import Owner from '../../../components/OwnerComponents/Owner/Owner';

Затем над частью render() мы собираемся создать новую функцию. С помощью этой функции мы собираемся вызвать свойство OnGetData, чтобы получить данные с сервера:

componentDidMount = () => {
    let url = '/api/owner';
    this.props.onGetData(url, { ...this.props });
}

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

Наконец, внутри функции рендеринга добавьте код для заполнения нашего массива owners:

let owners = [];
if (this.props.data && this.props.data.length > 0) {
    owners = this.props.data.map((owner) => {
        return (
            <Owner key={owner.id} owner={owner} {...this.props} />
        )
    })
}

В этой функции мы проверяем, заполнено ли свойство data (поскольку http-запросы являются асинхронными) и является ли это массивом. Затем для каждого элемента массива мы заполняем данными компонент Owner. Обратите внимание на свойство key, которое является обязательным при создании дочерних компонентов из массива объектов. Более того, мы передаем объект props, чтобы получить доступ к свойству history этого объекта. Таким же образом мы передаем единственный объект-владелец дочернему компоненту, потому что мы хотим, чтобы он мог использовать синтаксис props.owner.

Теперь, если мы перейдем в меню «Действия владельца», мы увидим результат со всеми владельцами:

Ленивая загрузка содержимого

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

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

Давайте создадим внутри папки hoc новую папку AsyncComponent, а внутри - новый файл AsyncComponent.js и изменим его:

import React, {Component} from 'react';

const asyncComponent = (importComponent) => {
    return class extends Component{
        state = {
            component: null
        }

        componentDidMount(){
            importComponent()
            .then(cmp => {
                this.setState({component: cmp.default});
            });
        }
        render(){
            const C = this.state.component;

            return C ? <C {...this.props} /> : null;
        }
    }
}

export default asyncComponent;

С помощью этого компонента мы собираемся загружать наш компонент асинхронно.

Для выполнения этого действия нам нужно изменить файл App.js, чтобы загрузить наш компонент OwnerList в асинхронном режиме:

//import OwnerList from './Owner/OwnerList/OwnerList';
import asyncComponent from '../hoc/AsyncComponent/AsyncComponent';
const AsyncOwnerList = asyncComponent(() => {
  return import('./Owner/OwnerList/OwnerList');
});

class App extends Component {
  render() {
    return (
      <BrowserRouter>
        <Layout>
          <Switch>
            <Route path="/" exact component={Home} />
            <Route path="/owner-list" component={AsyncOwnerList} />
            <Route pat="*" component={NotFound} />
          </Switch>
        </Layout>
      </BrowserRouter>
    );
  }
}

export default App;

Теперь мы импортируем наш компонент Async и внутри него импортируем компонент OwnerList. Наконец, мы больше не загружаем OwnerList внутри компонента Route, а загружаем AsyncOwnerList.

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

Заключение

Прочитав этот пост, вы узнали:

  • Способ создания компонентов HOC
  • Как реализовать Redux для получения данных с сервера
  • Как использовать библиотеку Moment для форматирования дат
  • Способ загрузки компонентов в асинхронном режиме с помощью функции отложенной загрузки.

Спасибо, что прочитали статью, и я надеюсь, что вы нашли в ней что-то полезное.

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