Джентльменский набор React компонентов FullStack разработчика для управления потоком данных

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

Существует токсичный стереотип, что FullStack разработчики не могут ни в фронт, ни в бек. Как минимум, так как объем работ большой, часто, программирование фронта на React превращается в формошлепство с сомнительным качеством кода. Код копируется без создания компонентов, нет глобального состояния приложения.

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

Лучшее состояние UI для FullStack - отсутствие состояния

Так как состояние фронта дублирует структуры данных, объявленные на стороне backend, причиной появления плохого кода скорее всего будет желание сэкономить на их фронтовых описаниях. Как вариант, рискну предложить возродить использование stateless подхода для простых форм React приложений.

Функция createProvider

Позволяет вынести какой-либо объект в React контекст. Вместе с Mobx может быть использована для создания общего controller-а для вложенных view-шек. В методах анонимного класса getPosts и getPostById используется функция fetchApi- алиас для fetch(...).then((data) => data.json())

import { createProvider, fetchApi } from 'react-declarative';

const [BlogApiProvider, useBlogApi] = createProvider(
  () => new class {

    getPosts = () =>
      fetchApi("https://jsonplaceholder.typicode.com/posts")

    getPostById = (id) =>
      fetchApi(`https://jsonplaceholder.typicode.com/posts/${id}`)

  }
);

export { BlogApiProvider, useBlogApi };

Компонент FetchView

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

import { FetchView } from 'react-declarative'

const PostList = () => {

  const { getPosts } = useBlogApi();

  const state = [
    getPosts,
  ];

  return (
    <FetchView state={state} animation="fadeIn">
      {(posts) => (
        <div>
          {posts.map((post, idx) => (
            <p key={idx}>
              <b>{post.title}</b>
              {post.body}
            </p>
          ))}
        </div>
      )}
    </FetchView>
  );
};

Компонент Async

В отличие от FetchView умеет показывать спиннер (см CircularProgress), сигнализирующий пользователю о активной загрузке данных. Если id изменится, то запрос выполнится повторно задействуя пропсу payload

import { Async } from 'react-declarative'

import { CircularProgress } from '@mui/material'

const PostItem = ({
  id,
}) => {

  const { getPostById } = useBlogApi();

  return (
    <Async payload={id} Loader={CircularProgress}>
      {async (id) => {
        const { title, body } = await getPostById(id);
        return (
          <div>
            <p>{title}</p>
            <p>{body}</p>
          </div>
        );
      }}
    </Async>
  );
};

Компонент Switch

Роутер, с использованием которого легко настроить кеширование и инвалидацию кеша. Для примера, реализуем ролевую модель.

import { createProvider, fetchApi, singleshot } from 'react-declarative';

import sleep from '../utils/sleep';

const roleApiManager = new class {

  getRoles = singleshot(async () => {
    await sleep(5_000);
    return [
      'admin',
      ...
    ]
  });

  hasRole = async (role) => {
    const roles = await this.getRoles();
 		return roles.includes(role);
  };

	unload = () => {
    this.getRoles.clear();
  };

};

const [RoleApiProvider, useRoleApi] = createProvider(
	() => roleApiManager
);

export { roleApiManager, RoleApiProvider, useRoleApi };

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

import { Switch } from 'react-declarative';

...

const routes = [
  {
    path: '/sample-page',
    unload: roleApiManager.unload,
  },
];

...

const App = () => (
  <Switch history={history} items={routes} />
);

Компонент If

Этот компонент осуществит ветвление представления исходя из истинности промиса в condition. Он так же умеет перезапрашивать данные при изменении payload

import { If } from 'react-declarative'

const ProfilePage = () => {
  const { hasRole } = useRoleApi();
  return (
    <If condition={() => hasRole("admin")}>
      <button>Кнопка только для админа</button>
    </If>
  );
};

Краткость сестра таланта

Если данные компоненты вызовут интерес среди сообщества, я напишу цикл статей, посвященной упрощению рутины на стороне фронтенда.

Источник: https://habr.com/ru/post/676612/


Интересные статьи

Интересные статьи

Эта статья про опыт кэширования приложения eCommerce, написанного на ASP.NET Core, и которое работает в Azure Cloud. Общеизвестно, что кэширование может значительно улучш...
В данной статье я хочу описать известные мне способы встраивания mapbox-gl в React приложение, на примере создания простого веб приложения содержащего карту на Next.js с ...
Слышали про такой инструмент, как Airtable, но не знали, с чего начать? Тогда приглашаем в мир визуального программирования построения БД! Этим постом мы начинаем цикл обучающих статей, ...
Облака подобны магической шкатулке — задаешь, что тебе нужно, и ресурсы просто появляются из ниоткуда. Виртуальные машины, базы данных, сеть — все это принадлежит только тебе. Существуют и другие...
Тема статьи навеяна результатами наблюдений за методикой создания шаблонов различными разработчиками, чьи проекты попадали мне на поддержку. Порой разобраться в, казалось бы, такой простой сущности ка...