pik-react-utils
TypeScript icon, indicating that this package has built-in type declarations

0.6.0 • Public • Published

Pik react utils

Todo

  • Entities should work with Proxies to get thier fields automaticly
  • Normalization proccess with _uid and _type
  • Typescript
  • Add API and store functions for delete/update/create entities
  • Entities typings
  • Css modules/Styled components
  • Tests
  • Sentry implementation
  • Oauth
  • Error 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 methods authorize(), 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

  1. All configs should be in entry point file of your application

API

Основные идеи

  1. Стандартное API покрывает только стандартные кейсы. В случае кастомных запросов (например, для отправки данных сторонним сервисам, или нашим endpoint-ам с особой логикой, загрузки файлов и т.п.) нужно реализовывать это в проекте, а если это становится стандартным запросом, то выносить в новые методы для работы с API.

  2. По договоренности с бэкэндом мы всегда знаем куда и как нужно посылать стандартные запросы. Например на GET, POST, PATCH какой-то сущности.

  3. Не делать повторные запросы, если мы ожидаем ответа сервера по такому же запросу.

Работа с entitiesStore

Основные идеи

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

  2. Хранилище само получает данные от сервера при необходимости.

  3. Хранилище не делает лишних и повторных запросов.

  4. Можно использовать методы получения данных в рендере.

  5. Нужно кэшировать результаты запросов.

  6. Данные полученные от сервера нормализуются и хранятся в единственном экземпляре.

  7. Изменения в хранилище приведут к автоматической перерисове компонентов.

Параметры методов

  1. getPromise: boolean — получить промис как результат работы метода, на его reslove или reject можно завязаться, если нужно сделать какие-то операции после завершения запроса.

  2. sync: boolean — отправить результат на сервер

  3. api: boolean - отправить результат на сервер (нужно избавиться от параметра sync или api). Сейчас используется в createEntity

  4. after: boolean - все действия в сторе предшествуют отправке соответствующих апи-запросов, after меняет порядок на обратный — сначала запрос, и после reslove произойдут изменения в хранилище.

  5. log: boolean (deprecated) - логировать изменения сущности, тогда только изменненные поля будут отправлены на сервер. Есть в старой версии утилит.

Принципы работы

  1. Похож на принцип работы createFetcher из выступления Дэна Абрамова.

  2. Когда используется метод getEntity/getList мы получаем либо то что хранится в хранилище, либо undefined.

  3. Если мы получили undefined, то хранилище автоматически сделает запрос, чтобы получить необходимые данные от API.

  4. Полученные данные нормализуются, чтобы храниться в единственном экземпляре.

  5. В хранилище можно положить любые нормализуемые данные, если использовать функцию normalizeResult. Таким образом можно отделить логику запросов (особенно для нестандартных запросов) от хранения данных.

  6. Когда данные в хранилище появятся/обновятся/удалятся, то использующие эти данные getEntity/getList в рендерах, заставят нужный компонент перерисоваться с актуальными данными.

Z. Бонус. Когда пользователь запрашивает свойство сущности, которого еще нет в хранилище, но которое там потенциально может быть, будет сделан запрос на получение полной сущности.

How not to use

  1. Это не хелпер по созданию апи-запросов.

  2. Если нужно сдедать запрос на удаление, создание или изменение того что не лежит в хранилище используйте другие утилиты.

  3. Централизованная обработка ошибок не входит в текущую версию entitiesStore

How to use

  1. Приложение должно обрабатывать ситуации, когда данных нет в хранилише, и когда они есть. Приложение подписывается на обновления данных в хранилище и реагирует на изменения. Это достигает использованием двух методов entitiesStore.getEntity и entitiesStore.getList и декоратором observer из mobx-react.
const EmployeeList = inject(ENTIRIES_STORE)(observer(({ [ENTIRIES_STORE]: store }) => {
    const employeeList = store.getList("employee")
    if (!list) {
        return <Spinner />
    }
    return (
        <>
            {employeeList.entities.map(employee => <Employee data={employee} key={employee._uid} />)}
        </>
    )
}))
 
const Employee = observer(({ data }) => (
    <div>
        {data.firstName} {data.lastName}
    </div>
)

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

  1. Когда на основании данных нам нужно сделать вычисления для вывода, то мы делаем это в @computed и используем результат в рендере. В рендере вычисления мы не делаем, чтобы не повторять вычисления при перерисовках, и чтобы не страдала читабельность.
@inject(ENTIRIES_STORE)
@observer
class EmployeeList extends Component {
    
    @computed get employeeList() {
        const { [ENTIRIES_STORE]store } = this.props
        return store.getList("employee")
    }
 
    @computed get employers() {
        if (!this.employeeList) {
            return []
        }
        const companies = new Set()
        this.employeeList.entities.map(({ employer }) => {
            if (employer) {
                companies.add(employer)
            }
        })
        return companies
    }
 
    render() {
        return (
            <>
                {this.employers.map(company => <Company data={company} key={company._uid} />)}
            </>
        )
    }
}))
  1. Создать сущность на сервере и положить результат в хранилище, обработать результат валидации на сервере:
@inject(ENTIRIES_STORE)
@observer
class CreateEmployee extends Component {
 
    @observable formData = { first_name: "", last_name: "" }
    @observable errors = { first_name: [], last_name: [] }
 
    @action.bound
    clearValidationErrors() {
        Object.keys(this.errors).forEach(key => this.errors[key] = "")
    }
 
    @action.bound
    setValidationErrors(detail) {
        Object.keys(detail)
            .forEach(key => this.errors[key] = detail[key].message)
    }
 
    handleSubmit() {
        const { [ENTIRIES_STORE]store } = this.props
        const promise = api.createEntity({ ...this.formData, _type: "employee" })
        promise
            .then(action((response) => {
                store.normalizeResult(response)
                this.clearValidationErrors()
            ))
            .catch(({ detail }) => this.setValidationErrors(detail))
    }
    // ...
}))

Работа с oAuth

Основные идеи

  1. Быстрая настройка, легкое использование
  2. Абстракция от имплементации необходимого API для авторизации/аутентефикации пользователя
  3. authService имплементирует механизмы продления/получения bearer токена
  4. Собственная имплементация (при необходимости) IAuthService для изменения механизма авторизации

Принцип работы

configuration
  1. Разработчик передает authUrl: string и clientId: number в api.config({...})
  2. api.config() валидирует полученные аргументы, в случае если authService: IAuthService === undefined используется стандартный сервис авторизации
api Usage
  1. При каждом вызове request() получаем токен из sessionManager.sessionData, используем полученный токен для добавления заголовка { authorization: `Bearer pik ${token}` }
  2. Обрабатываем каждый ответ, в случае если responseCode === 403, вызываем метод this.authService.authorize()
application Usage
  1. Применяя механизм авторизации для уникального роута разработчику необходимо передать контейнер в функцию PikAuthorization() и вернуть результат вызова в Route.component соответствующий уникальному роуту
  2. При переходе на уникальный роут PikAuthorization() валидирует api.authService.isAuthenticated, если возвращается true, то запускается механизм отрисовки компонента соответствующего уникальному роуту, в противном случае вызывается api.authService.authorize()

How to use

  1. Import and configure api
import { api } from "pik-react-utils"
 
const appParams = {
  authUrl: `http://yourathrerviceuri.com`,
  clientId: 3939323,
}
 
const axiosConfig = {
  withCredentials: false,
}
 
// with default authService
api.config({ appParams, axiosConfig })
 
// with custom authService
const myAwesomeAuthService: IAuthService = new myAwesomeAuthService()
api.config({ appParams, axiosConfig, authService: myAwesomeAuthService})
 
  1. Import PikAuthorization and enable oAuth token validation to specific route
import { PikAuthorization } from "pik-react-utils"
 
export default class AppRouter extends PureComponent {
  render() {
    return (
      <Router>
        <Switch>
          {/*your code here*/}
          <Route
            exact={true}
            path={`${BASE_PATH}${COMPANIES_LIST}`}
            component={PikAuthorization(CompaniesList)}
          />
          {/*your code here*/}
        </Switch>
      </Router>
    )
  }
}
 

Readme

Keywords

none

Package Sidebar

Install

npm i pik-react-utils

Weekly Downloads

16

Version

0.6.0

License

MIT

Unpacked Size

290 kB

Total Files

17

Last publish

Collaborators

  • dimitreee
  • g351
  • it-services
  • kalinaa
  • vhaldemario
  • whytimofey