Pik react utils
Todo
Entities should work with Proxies to get thier fields automaticlyNormalization proccess with _uid and _typeTypescriptAdd API and store functions for delete/update/create entitiesEntities typingsCss modules/Styled componentsTestsSentry implementationOauthError handling- Write doc about necessary settings to use that package
Dev guide
Quiсk start
npm i pik-react-utils
Settings
Table of contents
Utils
Basic
- pikUtilsConfig - function to add core utils configuration (sentry, api and authService configuration)
Regular
- factoryEntityGetter — function to create MobX computed entity getters
- factoryEntitiesGetter — function to create MobX computed entities getters
- bind, bindArgs - cache result of global.bind function
oAuth utils
- authService - implements specific
IAuthService
interface methodsauthorize(), handleAuthError(), prepareApiRequest()
, store@computed isAuthenticated: bool
Components
- PikContainer — it's necessary to use this container as container of the app
- Spinner — loading indicator component (deprecated)
- PikAuthorization -- wrapper to route containers, should be used when we need to enable oAuth onto specific route
Stuff
- BaseEntity, BaseValue, List — general types
- api — configurable api
- stores — stores
Tips
- All configs should be in entry point file of your application
API
Основные идеи
-
Стандартное API покрывает только стандартные кейсы. В случае кастомных запросов (например, для отправки данных сторонним сервисам, или нашим endpoint-ам с особой логикой, загрузки файлов и т.п.) нужно реализовывать это в проекте, а если это становится стандартным запросом, то выносить в новые методы для работы с API.
-
По договоренности с бэкэндом мы всегда знаем куда и как нужно посылать стандартные запросы. Например на GET, POST, PATCH какой-то сущности.
-
Не делать повторные запросы, если мы ожидаем ответа сервера по такому же запросу.
Работа с entitiesStore
Основные идеи
-
Разработчик может абстрагироваться от получения и хранения данных, апи-запросов, асинхронности, нормализации и прочих вспомогательных операций.
-
Хранилище само получает данные от сервера при необходимости.
-
Хранилище не делает лишних и повторных запросов.
-
Можно использовать методы получения данных в рендере.
-
Нужно кэшировать результаты запросов.
-
Данные полученные от сервера нормализуются и хранятся в единственном экземпляре.
-
Изменения в хранилище приведут к автоматической перерисове компонентов.
Параметры методов
-
getPromise: boolean
— получить промис как результат работы метода, на его reslove или reject можно завязаться, если нужно сделать какие-то операции после завершения запроса. -
sync: boolean
— отправить результат на сервер -
api: boolean
- отправить результат на сервер (нужно избавиться от параметра sync или api). Сейчас используется в createEntity -
after: boolean
- все действия в сторе предшествуют отправке соответствующих апи-запросов, after меняет порядок на обратный — сначала запрос, и после reslove произойдут изменения в хранилище. -
log: boolean
(deprecated) - логировать изменения сущности, тогда только изменненные поля будут отправлены на сервер. Есть в старой версии утилит.
Принципы работы
-
Похож на принцип работы createFetcher из выступления Дэна Абрамова.
-
Когда используется метод getEntity/getList мы получаем либо то что хранится в хранилище, либо undefined.
-
Если мы получили undefined, то хранилище автоматически сделает запрос, чтобы получить необходимые данные от API.
-
Полученные данные нормализуются, чтобы храниться в единственном экземпляре.
-
В хранилище можно положить любые нормализуемые данные, если использовать функцию normalizeResult. Таким образом можно отделить логику запросов (особенно для нестандартных запросов) от хранения данных.
-
Когда данные в хранилище появятся/обновятся/удалятся, то использующие эти данные getEntity/getList в рендерах, заставят нужный компонент перерисоваться с актуальными данными.
Z. Бонус. Когда пользователь запрашивает свойство сущности, которого еще нет в хранилище, но которое там потенциально может быть, будет сделан запрос на получение полной сущности.
How not to use
-
Это не хелпер по созданию апи-запросов.
-
Если нужно сдедать запрос на удаление, создание или изменение того что не лежит в хранилище используйте другие утилиты.
-
Централизованная обработка ошибок не входит в текущую версию entitiesStore
How to use
- Приложение должно обрабатывать ситуации, когда данных нет в хранилише, и когда они есть. Приложение подписывается на обновления данных в хранилище и реагирует на изменения. Это достигает использованием двух методов entitiesStore.getEntity и entitiesStore.getList и декоратором observer из mobx-react.
const EmployeeList = const Employee =
Здесь мы говорим, что хотим использовать данные списка сотрудников для отображения компонента. Если данных нет, выводим спиннер. Если они есть выводим данные. Если данных, сначала не было а потом они появились, то стриггерится ререндер и мы выведем список. Если изменятся данные о каком-то сотруднике, то мы это отобразим. Если вдруг данные о сотруднике или весь список инвалидируется компонент это обработает.
- Когда на основании данных нам нужно сделать вычисления для вывода, то мы делаем это в
@computed
и используем результат в рендере. В рендере вычисления мы не делаем, чтобы не повторять вычисления при перерисовках, и чтобы не страдала читабельность.
@@observer @computed { const ENTIRIES_STORE: store = thisprops return store } @computed { if !thisemployeeList return const companies = thisemployeeListentities return companies } { return <> thisemployers </> }))
- Создать сущность на сервере и положить результат в хранилище, обработать результат валидации на сервере:
@@observer @observable formData = first_name: "" last_name: "" @observable errors = first_name: last_name: @actionbound { Object } @actionbound { Object } { const ENTIRIES_STORE: store = thisprops const promise = api promise
Работа с oAuth
Основные идеи
- Быстрая настройка, легкое использование
- Абстракция от имплементации необходимого API для авторизации/аутентефикации пользователя
- authService имплементирует механизмы продления/получения bearer токена
- Собственная имплементация (при необходимости) IAuthService для изменения механизма авторизации
Принцип работы
configuration
- Разработчик передает
authUrl: string
иclientId: number
вapi.config({...})
api.config()
валидирует полученные аргументы, в случае еслиauthService: IAuthService === undefined
используется стандартный сервис авторизации
api Usage
- При каждом вызове
request()
получаем токен изsessionManager.sessionData
, используем полученный токен для добавления заголовка{ authorization: `Bearer pik ${token}` }
- Обрабатываем каждый ответ, в случае если
responseCode === 403
, вызываем методthis.authService.authorize()
application Usage
- Применяя механизм авторизации для уникального роута разработчику необходимо передать контейнер в функцию
PikAuthorization()
и вернуть результат вызова вRoute.component
соответствующий уникальному роуту - При переходе на уникальный роут
PikAuthorization()
валидируетapi.authService.isAuthenticated
, если возвращается true, то запускается механизм отрисовки компонента соответствующего уникальному роуту, в противном случае вызываетсяapi.authService.authorize()
How to use
- Import and configure api
const appParams = authUrl: `http://yourathrerviceuri.com` clientId: 3939323 const axiosConfig = withCredentials: false // with default authServiceapi // with custom authServiceconst myAwesomeAuthService: IAuthService = api
- Import PikAuthorization and enable oAuth token validation to specific route
{ return <Router> <Switch> /*your code here*/ <Route exact=true path=`` component= /> /*your code here*/ </Switch> </Router> }