Проект React реализует глобальную загрузку и сообщения об ошибках

React.js

предисловие

  • использовать в проектеloading, обычно в компоненте с переменной (например,isLoading), чтобы сохранить данные запросаloadingстатус, запросapiбывший генералisLoadingустановлено значениеtrue,проситьapiпозжеisLoadingустановлено значениеfalse, так что реализацияloadingГосударственный контроль, такой как следующий код:
import { Spin, message } from 'antd';
import { Bind } from 'lodash-decorators';
import * as React from 'react';
import * as api from '../../services/api';

class HomePage extends React.Component {
  state = {
    isLoading: false,
    homePageData: {},
  };
  
  async componentDidMount () {
    try {
      this.setState({ isLoading: true }, async () => {
        await this.loadDate();
      });
    } catch (e) {
      message.error(`获取数据失败`);
    }
  }
  
  @Bind()
  async loadDate () {
    const homePageData = await api.getHomeData();
    this.setState({
      homePageData,
      isLoading: false,
    });
  }
  
  render () {
    const { isLoading } = this.state;
    return (
      <Spin spinning={isLoading}>
        <div>hello world</div>
      </Spin>
    );
  }
}

export default HomePage;
  • Однако для большого проекта, если каждый запросapiНаписание кода, подобного приведенному выше, очевидно, приведет к тому, что в проекте будет слишком много повторяющегося кода, что не способствует сопровождению проекта. Поэтому глобальное хранилище описано нижеloadingгосударственное решение.

идеи

  • упаковкаfetchЗапрос (портал 👉:Процесс настройки проекта react + typescript) и соответствующие запросы данных, связанные сapi
  • использоватьmobxделать государственное управление
  • Используйте декоратор@initLoadingреализоватьloadingИзменение и сохранение состояния

Запас знаний

  • Введение в этот раздел и последующие подразделыКодНекоторые сопутствующие базовые знания, если вы их освоили, можете сразу пропустить🚶🚶🚶.

@Decorator

  • Основная функция декоратора заключается в расширении некоторых новых поведений к существующему методу или классу вместо непосредственного изменения самого метода или класса, что можно просто понимать как ненавязчивую модификацию поведения.
  • Декоратор может не только украшать класс, но и украшать свойства класса (идея этой статьи). Как и в приведенном ниже коде, декораторreadonlyдля украшенияnameметод.
class Person {
  @readonly
  name() { return `${this.first} ${this.last}` }
}
  • функция декоратораreadonlyВсего можно принять три параметра:
    • первый параметрtargetявляется прототипом объекта класса, который в данном случаеPerson.prototype, первоначальное намерение декоратора состоит в том, чтобы «украсить» экземпляр класса, но экземпляр еще не был сгенерирован в это время, поэтому он может украсить только прототип (это отличается от украшения класса, в этом случаеtargetпараметр относится к самому классу)
    • второй параметрnameимя свойства, которое нужно украсить
    • третий параметрdescriptorэто объект описания для этого свойства
function readonly(target, name, descriptor){
  // descriptor对象原来的值如下
  // {
  //   value: specifiedFunction,
  //   enumerable: false,
  //   configurable: true,
  //   writable: true
  // };
  descriptor.writable = false;
  return descriptor;
}

readonly(Person.prototype, 'name', descriptor);
// 类似于
Object.defineProperty(Person.prototype, 'name', descriptor);
  • Приведенный выше код показывает, что функция декоратораreadonlyизменит объект описания атрибута (descriptor), а измененный объект описания затем используется для определения свойств.
  • следующее@logДекоратор, который может играть роль вывода логов:
class Math {
  @log
  add(a, b) {
    return a + b;
  }
}

function log(target, name, descriptor) {
  var oldValue = descriptor.value;

  descriptor.value = function() {
    console.log(`Calling ${name} with`, arguments);
    return oldValue.apply(this, arguments);
  };

  return descriptor;
}

const math = new Math();

// passed parameters should get logged now
math.add(2, 4);
  • Приведенное выше описание кода, декоратор@logРоль заключается в выполнении один раз перед выполнением исходной операции.console.log, чтобы достичь цели вывода журнала.

mobx

  • Управление состоянием в проектах не используетсяreduxвместо этого используйтеmobx, Причина в том, чтоreduxОчень сложно написать:

    • Если вы хотите написать асинхронный метод и обработать егоside-effects, использоватьredux-sagaилиredux-thunkДля выполнения асинхронной обработки бизнес-логики
    • Если для повышения производительности необходимо ввестиimmutableСоответствующие гарантии библиотекиstoreпроизводительность, сreselectсделать механизм кэширования
  • reduxАльтернативаmobx, в официальной документации рекомендуется использоватьRootStoreсвязать всеStore, который разрешает крестStoreПроблема вызова и может кэшировать источники данных нескольких модулей одновременно.

  • в проектеstoresхранится в каталогеindex.tsкод показывает, как показано ниже:

import MemberStore from './member';
import ProjectStore from './project';
import RouterStore from './router';
import UserStore from './user';

class RootStore {
  Router: RouterStore;
  User: UserStore;
  Project: ProjectStore;
  Member: MemberStore;

  constructor () {
    this.Router = new RouterStore(this);
    this.User = new UserStore(this);
    this.Project = new ProjectStore(this, 'project_cache');
    this.Member = new MemberStore(this);
  }
}

export default RootStore;

Код

  • вышеупомянутая параloadingКод, связанный с контролем состояния, не имеет ничего общего с логикой взаимодействия самого компонента, если есть еще однотипные операции, требующие добавления дублирующего кода, то это явно неэффективно, а затраты на обслуживание слишком высоки.
  • Таким образом, эта статья создастinitLoadingдекоратор для упаковкиloadingМетоды класса для сохранения и изменения состояния.
  • Основная идея заключается в использованииstoreконтроль и хранениеloadingСтатус, в частности:
    • установить одинBasicStoreкласс, пиши внутриinitLoadingдекоратор
    • необходимо использовать глобальныеloadingсостояние различных модулейStoreнужно унаследоватьBasicStoreклассы, реализующие различныеStoreмеждуloading"Карантинная" обработка состояний
    • использовать@initLoadingОбертка декоратора должна бытьloadingРазличные модули, в которых состояние сохраняется и изменяетсяStoreметод в
    • Приобретение компонентовStoreсохраненный глобальныйloadingгосударство
  • Советы: Конкретный процесс 👆 лучше понять код 👇.

Реализация декоратора @initLoading

  • в проектеstoresновый каталогbasic.tsфайл со следующим содержимым:
import { action, observable } from 'mobx';

export interface IInitLoadingPropertyDescriptor extends PropertyDescriptor {
  changeLoadingStatus: (loadingType: string, type: boolean) => void;
}

export default class BasicStore {
  @observable storeLoading: any = observable.map({});

  @action
  changeLoadingStatus (loadingType: string, type: boolean): void {
    this.storeLoading.set(loadingType, type);
  }
}

// 暴露 initLoading 方法
export function initLoading (): any {
  return function (
    target: any,
    propertyKey: string,
    descriptor: IInitLoadingPropertyDescriptor,
  ): any {
    const oldValue = descriptor.value;

    descriptor.value = async function (...args: any[]): Promise<any> {
      let res: any;
      this.changeLoadingStatus(propertyKey, true); // 请求前设置loading为true
      try {
        res = await oldValue.apply(this, args);
      } catch (error) {
        // 做一些错误上报之类的处理 
        throw error;
      } finally {
        this.changeLoadingStatus(propertyKey, false); // 请求完成后设置loading为false
      }

      return res;
    };

    return descriptor;
  };
}
  • Как видно из приведенного выше кода,@initLoadingРоль декоратора заключается в том, чтобы обернуть имя свойства метода.propertyKeyхранятся в отслеживаемых данныхstoreLoading, установите метод-оболочку обернутого метода перед запросомloadingзаtrue, который устанавливает метод-оболочку для обернутого метода, когда запрос выполнен успешно/ошибкаloadingзаfalse.

Магазин наследует от BasicStore

  • отProjectStoreНапример, если модуль имеетloadProjectListметод используется для извлечения данных списка элементов, и этот метод должен использоватьloading, то проектstoresв каталогеproject.tsСодержимое файла следующее:
import { action, observable } from 'mobx';
import * as api from '../services/api';
import BasicStore, { initLoading } from './basic';

export default class ProjectStore extends BasicStore {
  @observable projectList: string[] = [];

  @initLoading()
  @action
  async loadProjectList () {
    const res = await api.searchProjectList(); // 拉取 projectList 的 api
    runInAction(() => {
      this.projectList = res.data;
    });
  }
}

используемые компоненты

  • Предположим, правильноHomePageКомпонент добавляет данные при загрузкеloadingОтображение состояния:
import { Spin } from 'antd';
import { inject, observer } from 'mobx-react';
import * as React from 'react';
import * as api from '../../services/api';

@inject('store')
@observer
class HomePage extends React.Component {
  render () {
    const { projectList, storeLoading } = this.props.store.ProjectStore;
    return (
      <Spin spinning={storeLoading.get('loadProjectList')}>
        {projectList.length && 
          projectList.map((item: string) => {
            <div key={item}>
              {item}
            </div>;
          })}
      </Spin>
    );
  }
}

export default HomePage;
  • Приведенный выше код используетсяmobx-reactиз@injectи@observerдекоратор для упаковкиHomePageкомпоненты, их роль заключается вHomePageПреобразованы в отзывчивые компоненты и введеныProvider(в файле ввода) предоставленоstoreк этому компонентуprops, так что можно пройтиthis.props.storeстать другимStoreданные модуля.
    • @observerФункция/декоратор может быть использована для добавленияReactКомпоненты превратились в реактивные компоненты
    • @injectДекоратор эквивалентенProviderКомпонент более высокого порядка, который можно использовать изReactизcontextвыбирать средиstoreв видеpropsпередается целевому компоненту
  • наконец прошлоthis.props.store.ProjectStore.storeLoading.get('loadProjectList')получитьProjectStoreГлобальные переменные хранятся в модуляхloadingгосударство.

Суммировать

  • Решение, описанное в этой статье, дает два преимущества, которые можно получить во время запроса.loadingОтображение состояния; при возникновении ошибки ее можно обработать глобально (отчет об ошибках и т. д.).
  • Рациональное использование декораторов может значительно повысить эффективность разработки, а инкапсуляция и уточнение некоторых нелогически связанных кодов может помочь нам быстро выполнять повторяющиеся задачи и экономить время.

использованная литература

  1. Начало работы с ECMAScript 6 | Декораторы
  2. Волшебное использование декораторов Javascript
  3. typescript | decorators