npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

pik-react-utils

v0.6.0

Published

Put a description here

Downloads

17

Readme

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>
    )
  }
}