Remote data provider for get/post requests by redux/components
npm install @aic/react-remote-data-provider 
- Установка
- Подключение
- Базовый пример
* Хук useRemoteData
* Компонент RemoteDataProvider
* HOC withRemoteData
- API
* Объект данных
* Общие параметры
* Хук useRemoteData
* Компонент RemoteDataProvider
* HOC withRemoteData
- Как это работает
* Начало загрузки
* Успешная загрузка
* Неудавшаяся загрузка
* Повторный рендер
* Изменение параметров
* Успешная загрузка новых данных
* Неудавшаяся загрузка новых данных
- Композиция провайдеров Composer
* Особенности
- Доступ к данным в redux
* Функция getLocalState
* Хук useLocalState
- Предзагрузка данных
- Расширения для remote-data-provider
* Расширение collector
+ Предназначение
+ Подключение
+ Компонент RemoteDataProviderCollector
+ Особенности
+ Расширенное использование
* Расширение globalError
+ Предназначение
+ Подключение
+ Использование
+ Получение и очистка ошибок
* Расширение serverReRender
+ Предназначение
+ Подключение
+ Использование serverReRender
+ Как это работает
+ Расширенное использование
- Класс AsyncActionController
- Пример использования в компонентах
- Пример использования в действиях (с redux-thunk)
npm i @aic/react-remote-data-provider
`
via yarn:
`
yarn add @aic/react-remote-data-provider
`Подключение
Подключите remote-data-provider редьюсер к redux по ключу
remoteData:
`jsx
import { combineReducers } from 'redux'
import { reducer as remoteDataReducer } from '@aic/react-remote-data-provider'const rootReducer = combineReducers({
// ... остальные редьюсеры
remoteData: remoteDataReducer
})
export default rootReducer
`Базовый пример
remote-data-provider можно использовать 3 способами: хук, компонент или HOC:$3
`jsx
import React from 'react'
import { useRemoteData } from '@aic/react-remote-data-provider'export default function MyGreatComponent (props) {
const {
response, // последний ответ от сервера, если есть, иначе undefined
request, // текущий запрос, аналогичный запросу из параметров
error, // последняя ошибка, если есть - содержит поля
status, message и data
isEmpty, // Boolean флаг, указывает, есть ли ответ с сервера
isAjax, // Boolean флаг, указывает, происходит ли загрузка данных
isError, // Boolean флаг, указывает, произошла ли ошибка во время последнего запроса
reload, // функция, при вызове которой происходит перезагрузка данных; возвращает Promise
clear, // функция, при вызове которой очищаются данные из redux
providerId // уникальный id провайдера
} = useRemoteData({
// уникальный ключ, по которому в redux будут лежать данные
reducerKey: 'myGreatData',
// параметры запроса, аналогичные axios
// если какие-либо параметры меняются, происходит автоматическая перезагрузка данных
request: {
url: 'http://my.perfect.api.com/get/some/data/',
params: {
count: props.count
}
}
}) // компонент будет перерендериваться каждый раз, когда меняется состояние загрузки
if (isEmpty) return null
return JSON.stringify(response)
}
`$3
`jsx
import React from 'react'
import { RemoteDataProvider } from '@aic/react-remote-data-provider'export default function MyGreatComponent (props) {
return (
// параметры аналогичны
useRemoteData
reducerKey: 'myGreatData',
request: {
url: 'http://my.perfect.api.com/get/some/data/',
params: {
count: props.count
}
},
// дополнительный параметр для RemoteDataProvider
// если true, то children функция будет вызываться только тогда,
// когда данные успешно загружены
onlyWithData: true // такая запись не обязательна, т.к. по умолчанию true
}}>
{/ children функция, вызывается каждый раз при изменении состояния загрузки /}
{({ // объект аналогичен результату useRemoteData
response, request, error, isEmpty, isAjax, isError, reload, clear
}) => {
// если параметр onlyWithData не установлен в false,
// проверять isEmpty нет необходимости
return JSON.stringify(response)
}}
)
}
`$3
`jsx
import React from 'react'
import { withRemoteData } from '@aic/react-remote-data-provider'//
withRemoteData может принимать как функцию, которая принимает props
// и возвращает один (или массив, для нескольких запросов) объект с параметрами,
// так и просто объект (или несколько) с параметрами
export default withRemoteData(props => ({
// параметры аналогичны RemoteDataProvider
reducerKey: 'myGreatData',
request: {
url: 'http://my.perfect.api.com/get/some/data/',
params: {
count: props.count
}
},
onlyWithData: true, // такая запись не обязательна, по умолчанию true
// дополнительный параметр для withRemoteData
// определяет ключ в props, где будут лежать данные
// если не указан, данные будут объеденены с props
stateKey: 'someData'
}))(MyGreatComponent)function MyGreatComponent (props) {
// данные лежат по ключу
someData
const { response, request, error, isEmpty, isAjax, isError, reload, clear } = props.someData
// если параметр onlyWithData не установлен в false,
// проверять isEmpty нет необходимости
return JSON.stringify(response)
}
`API
$3
Данный объект содержит информацию о текущем состоянии данных и функции для работы с ним. Он возвращается хуком useRemoteData, передается как первый параметр children функции у компонента RemoteDataProvider, и передается в props с помощью HOC withRemoteData.
* response: any: Ответ сервера, если запрос прошел успешно. Во время первой загрузки равен undefined. Во время последующих перезагрузок, когда меняется request или вызывается reload, предыдущий ответ сохраняется и доступен во время загрузки.
* request: AxiosConfig: Параметры запроса. Соответствуют переданным параметрам в remote-data-provider, по этому редко используются.
* error?: { status?: number, message?: string, data?: any }: Объект с ошибкой, если запрос закончился неудачно - при этом response, если он был, очищается. Если ошибки не произошло, равен undefined. Содержит (по возможности) поля status - статус (код) ответа сервера; message - сообщение ответа сервера; data - тело ответа сервера. Как только запрос начинается или заканчивается успешно, ошибка очищается.
* isEmpty: boolean: Флаг, true когда response пустой - если данные ни разу не были загружены или произошла ошибка.
* isAjax: boolean: Флаг, true когда происходит загрузка или обновление данных.
* isError: boolean: Флаг, true когда произошла ошибка загрузки данных.
* reload: () => Promise: Функция, которая перезагружает данные с последним request. Возвращает Promise загрузки.
* clear: Function: Функция, которая очищает данные в redux. Не используйте ее, если remote-data-provider еще смонтирован, иначе загрузка начнется по новой.
* providerId: string: Уникальный id для каждого инстанса remote-data-provider.$3
Данные параметры имеют все вариации - хук useRemoteData, компонент RemoteDataProvider, HOC withRemoteData.
* request: AxiosRequestConfig: remote-data-provider использует axios для выполнения запросов. request содержит параметры для axios, весь список находится здесь. Любые изменения в данном объекте (при перерисовке) приводят к перезагрузке данных - при этом происходит глубокое сравнение (_.equal). Не используйте функции в request.
* requestFunctions?: AxiosRequestConfig: Аналогично request, только для функций из axios конфига. Изменения в данном объекте не приводят к перезагрузке данных.
* reducerKey?: string: Все данные о состоянии и результате загрузки будут храниться в redux по ключу reducerKey. Обычно, это уникальное значение для каждого компонента. В случае, если reducerKey не указан, то он будет равен строковому значению request (для запроса из примера примерно "url='http://my.perfect.api.com/get/some/data'¶ms[count]=5" когда count равен 5).
* reducerPath?: string | string[]: Параметр reducerPath, как и reducerKey, определяет место хранения в redux store. reducerPath определяет вложенность данных. Например, если reducerPath = ['some', 'path'], а reducerKey = 'someKey', то данные будут храниться в redux по пути remoteData.some.path.someKey.
* axiosInstance?: AxiosInstance: экземпляр axios с предустановленными параметрами запроса. Создается с помощью axios.create({ ...params }).
* disabled?: boolean: если true, то загрузки и обновления данных происходить не будет. Если request из запроса и из стора совпадают - то будут возвращены актуальные данные, иначе - пустой объект данных.
* Могут быть дополнительные параметры, они все передаются в middlewares.$3
`jsx
import { useRemoteData } from '@aic/react-remote-data-provider'...
const remoteData = useRemoteData(params, requestDeps)
`
Хук загружает и обновляет данные, перерисовывая компонент при каждом изменении состояния. Возвращает объект данных.
* params: object: Соответствуют общим параметрам.
* requestDeps?: any[]: Зависимости, используемые в параметре request, аналогично второму аргументу для useMemo. Например, для запроса из примера
(request = { url: '...', params: { count: props.count } }) в качестве requestDeps нужно передать [props.count]. Если request постоянный (не меняется), нужно передавать пустой массив ([]). При этом зависимости для остальных параметров, кроме request, передавать не нужно. Используется для улучшения производительности. Если вы не уверены, что нужно передавать как requestDeps, не используйте этот параметр.$3
`jsx
import { RemoteDataProvider } from '@aic/react-remote-data-provider'...
{(remoteData, props) => { ... }}
`
Компонент является оберткой над хуком useRemoteData. Вызывает children функцию при каждом изменении состояния данных.*
params: object: В дополнение к общим параметрам может содержать:
- onlyWithData?: boolean = true: Если true, children функция будет вызываться только тогда, когда есть данные (isEmpty === false). По умолчанию true.
- requestDeps: any[]: соответствует второму аргументу хука useRemoteData.
* children: function (remoteData: object, props: object): ReactNode: children функция, принимает первым аргументом объект данных, вторым - все props, переданные RemoteDataProvider.По умолчанию,
children функция вызывается только тогда, когда есть загруженные данные (response). Это удобно, и позволяет с уверенностью использовать response, однако, уменьшает гибкость. Для большего контроля, можно использовать параметр onlyWithData=false, при котором RemoteDataProvider будет вызывать children функцию каждый раз, когда меняется состояние загрузки.
Например:`jsx
import React from 'react'
import { RemoteDataProvider } from '@aic/react-remote-data-provider'export const MyGreatComponent = (props) => (
reducerKey: 'myGreatData',
request: {
url: 'http://my.perfect.api.com/get/some/data/',
params: {
count: props.count
}
},
onlyWithData: false // меняем параметр
}}>
{({ isAjax, isEmpty, isError, response, error }) => {
if (isError) return
Упс, произошла ошибка ${error.status}
if (isEmpty) return Загрузка...
let result = JSON.stringify(response)
if (isAjax) result += ' новые данные уже близко...'
return result
}}
)
`$3
`jsx
import { withRemoteData } from '@aic/react-remote-data-provider'...
withRemoteData(params, params, ...)(Component)
withRemoteData(props => params)(Component)
withRemoteData(props => [params, params, ...])(Component)
`
HOC оборачивает компонент, передавая в props объект(ы) данных. Может принимать:
* Просто объекты с параметрами - один или несколько, как аргументы.
* Функцию, возвращающую объект с параметрами.
* Функцию, возвращающую массив с несколькими объектами с параметрами.*
params: object: В дополнение к общим параметрам может содержать:
- onlyWithData?: boolean = true: Если true, children функция будет вызываться только тогда, когда есть данные (isEmpty === false). По умолчанию true.
- requestDeps: any[]: соответствует второму аргументу хука useRemoteData.
- stateKey?: string | string[]: Ключ в props, в котором будет храниться объект данных. Если stateKey не указан, все ключи объекта данных будут записаны на прямую в props (props.isAjax, props.response, ...).
- propsKey?: string | string[]: Ключ в props, в котором будет храниться объект со всеми параметрами RemoteDataProvider (второй получаемый аргумент в функции children при обычном использовании). Если propsKey не указан, параметры RDP не будут переданы в props.
- provider?: RemoteDataProvider: Компонент, использованный как RemoteDataProvider. По умолчанию, это обычный RemoteDataProvider. Используйте параметр provider для установки кастомного RemoteDataProvider из расширений.
- Все остальные параметры будут переданы на прямую в RemoteDataProvider, оборачивающий компонент, и будут работать как обычно.
* Component: React.ComponentType: Оборачиваемый компонент.Обратите внимание, если параметр
onlyWithData в объектах данных задан как true (или не задан - он по умолчанию true), то компонент не будет смонтирован до того момента, как данные для всех объектов с onlyWithData == true не загрузятся.Как это работает
В этом разделе описывается жизненный цикл данных с использованием remote-data-provider.$3
Изначально в redux никаких данных нет:
`json5
// redux state
{
remoteData: {}
}
`
Используем наш компонент из базового примера:
`jsx
`
useRemoteData начинает загружать данные из http://my.perfect.api.com/get/some/data?count=1 и записывает данные в redux:
`json5
// redux state
{
remoteData: {
myGreatData: { // параметр reducerKey='myGreatData'
providerId: 's8dfj3nlsi',
isAjax: true,
isEmpty: true,
isError: false,
response: undefined,
request: {
url: 'http://my.perfect.api.com/get/some/data'
params: {
count: 1
}
},
error: undefined
}
}
}
`
Пока данных нет - isEmpty === false - наш компонент возвращает null
`jsx
// result
`
$3
Когда данные успешно загружены, они записываются в redux:
`json5
// redux state
{
remoteData: {
myGreatData: {
isAjax: false,
isEmpty: false,
response: ['somedata'],
// ... остальное без изменений
}
}
}
`
После чего происходит рендер с данными:
`jsx
// result
["somedata"]
`
$3
Когда происходит ошибка загрузки данных, данные об ошибке записываются в redux:
`json5
// redux state
{
remoteData: {
myGreatData: {
providerId: 's8dfj3nlsi',
isAjax: false,
isEmpty: true,
isError: true,
response: undefined,
request: {
url: 'http://my.perfect.api.com/get/some/data'
params: {
count: 1
}
},
error: {
status: 404, // статус ошибки, если есть
message: '404 Not Found', // сообщение об ошибке, если есть
data: {} // дополнительные данные (тело ответа), если есть
}
}
}
}
`
Так как при этом isEmpty === false, компонент возвращает null.
$3
Теперь, если вызвать этот компонент снова, с идентичными параметрами, повторной загрузки не произойдет:
`jsx
`
`jsx
// result
["somedata"]
`
$3
Если изменить параметр запроса count:
`jsx
`
RemoteDataProvider сравнит объекты запросов из redux и props, увидит изменения и начнет перезагружать данные:
`json5
// redux state
{
remoteData: {
myGreatData: {
isAjax: true, // началась загрузка
request: {
url: 'http://my.perfect.api.com/get/some/data'
params: {
count: 3 // изменившийся параметр
}
},
// ... остальное без изменений
}
}
}
`
При этом старые данные все еще будут доступны, и компонент сможет их использовать:
`jsx
// result
["somedata"] новые данные уже близко...
`
$3
Когда новые данные будут загружены, они будут записаны в redux:
`json5
// redux state
{
remoteData: {
myGreatData: {
isAjax: false,
response: ['somedata', 'somedata', 'somedata'],
// ... остальное без изменений
}
}
}
`
И компонент будет рендериться с новыми данными:
`jsx
// result
["somedata", "somedata", "somedata"]
`
$3
Если не удалось загрузить новые данные, то данные об ошибке записываются, а старые данные удаляются:
`json5
// redux state
{
remoteData: {
myGreatData: {
providerId: 's8dfj3nlsi',
isAjax: false,
isEmpty: true,
isError: true,
response: undefined,
request: {
url: 'http://my.perfect.api.com/get/some/data',
params: {
count: 1
}
},
error: {
status: 404, // статус ошибки, если есть
message: '404 Not Found', // сообщение об ошибке, если есть
data: {} // дополнительные данные об ошибке, если есть
}
}
}
}
`
Так как при этом isEmpty === false, компонент возвращает null.Композиция провайдеров
Composer
Если необходимо для нескольких загружаемых данных иметь одну children функцию, используйте Composer. Параметры компонента:
* providers: Object | Array - объект или список, содержащий готовые элементы или параметры (объектов) для провайдера.
* defaultProvider?: RemoteDataProvider - провайдер, использующийся для параметров (объектов) в providers. По умолчанию обычный RemoteDataProvider. Можно использовать кастомизированные провайдеры из расширений.`jsx
import React from 'react'
import { RemoteDataProvider, Composer } from '@aic/react-remote-data-provider'// ...
providers={{
someData: , // элемент
otherData: { request, onlyWithData, ... }, // параметры для RDP
}}
>
{({ someData, otherData }) => {
console.log(someData) // => { isEmpty, response, ... }
console.log(otherData) // => { isEmpty, response, ... }
}}
`
Вместо объекта в данном примере можно использовать массив в качестве параметра providers, тогда children функция должна принимать массив, порядок объектов данных которого будет соответствовать порядку в providers.$3
* Composer начинает загружать все данные одновременно,
* children функция вызывается при каждом изменении, после того, как у всех провайдеров с параметром onlyWithData === true имеются загруженные данные.Доступ к данным в redux
Время от времени появляется необходимость получить информацию о загружаемых данных вне remote-data-provider. Для этого можно использовать стандартный декоратор connect из redux, получая данные с помощью функции getLocalState или хук useLocalState.$3
`jsx
import { connect } from 'react-redux'
import { getLocalState } from '@aic/react-remote-data-provider'function mapStateToProps (state) {
const reducerKey = 'someKey'
const reducerPath = ['some', 'path']
return {
someDataState: getLocalState(state, reducerKey, reducerPath)
}
}
@connect(mapStateToProps)
export class SomeReactComponent extends React.Component {
// ...
}
`
(подробная информация про декоратор connect в официальном репозитории)
Функция getLocalState принимает следующие параметры:
* state - redux state;
* reducerKey: Такой же, как в remote-data-provider, который загружает эти данные;
* reducerPath: Такой же, как в remote-data-provider, который загружает эти данные, может отсутствовать.
Функция возвращает данные, находящиеся по указанным reducerPath и reducerKey. Если ничего не найдено, функция возвращает пустой объект данных:
`json5
{
providerId: undefined,
response: undefined,
request: undefined,
isEmpty: true,
isAjax: false,
isError: false,
error: undefined
}
`
$3
Хук работает аналогично getLocalState в связке с connect, но без необходимости передавать state.`jsx
import { useLocalState } from '@aic/react-remote-data-provider'export function SomeReactComponent (props) {
const reducerKey = 'someKey'
const reducerPath = ['some', 'path']
const someDataState = useLocalState(reducerKey, reducerPath)
...
}
`
Предзагрузка данных
Пакет remote-data-provider предоставляет два действия:
* getData(props): function (dispatch): Promise - принимает props - все параметры RemoteDataProvider, и возвращает функцию, которая загружает данные, записывая в redux информацию о начале загрузке, и в последствии об успешной или неудавшейся загрузке. Возвращает Promise.
* clearData(props): function (dispatch): void - принимает props - все параметры RemoteDataProvider, и возвращает функцию, которая очищает состояние в state.
Данные действия используются непосредственно в useRemoteData, по этому, чтобы вызывать их вне компонента, используйте функцию getRDPStaticProps, которая возвращает все внутренние параметры useRemoteData. Функция getRDPStaticProps принимает:
* state: redux state.
* props: Внешние параметры для useRemoteData. Например, мы используем
RemoteDataProvider со следующими параметрами:
`jsx
const someDataOptions = {
reducerKey: 'someKey',
request: {
url: '/some/url'
}
}return (
{(remoteData) => { ... }}
)
`
Предзагрузка данных для этого компонента:
`jsx
import { getData, getRDPStaticProps } from '@aic/react-remote-data-provider'
import { store } from 'src/store' // ваш redux storeconst state = store.getState()
const staticSomeDataProps = getRDPStaticProps(
state,
someDataOptions // RDP параметры из примера выше
)
const loadingPromise = getData(staticSomeDataProps)(store.dispatch) // => Promise
loadingPromise.then(() => {
console.log('Загрузка данных окончена') // данные сохраннены в redux
})
`
Для получения store и dispatch в компоненте, можно использовать хуки useStore и useDispatch из redux:
`jsx
import React, { useEffect } from 'react'
import { useStore, useDispatch } from 'react-redux'
import { getData, getRDPStaticProps } from '@aic/react-remote-data-provider'function SomeComponent (props) {
const store = useStore()
const dispatch = useDispatch()
useEffect(() => {
const staticSomeDataProps = getRDPStaticProps(
store.getState(),
someDataOptions // RDP параметры из примера выше
)
const loadingPromise = getData(staticSomeDataProps)(dispatch) // => Promise
loadingPromise.then(() => console.log('Загрузка данных окончена'))
}, []) // загружаем данные один раз
// ...
}
`Предзагрузка данных ведет себя так же, как если бы данные загружал
RemoteDataProvider. Данные записываются в redux, отрабатывают все middleware, и т.д.
Когда вы используете RemoteDataProvider с параметрами someDataOptions (из примера), у него уже будут все данные, и он не будет повторно их загружать.
Подробнее про действие
getData в API referenceРасширения для remote-data-provider
Расширения позволяют увеличить гибкость и базовый функционал remote-data-provider. Обычно, расширения подключаются с помощью redux middleware, и могут предоставлять компоненты-обертки над стандартным RemoteDataProvider. Пакет remote-data-provider предоставляет несколько расширений. Те расширения, которые вы не используете, не попадают в финальную сборку вашего приложения.
Вы также можете сами создавать расширения для remote-data-provider.$3
Данное расширение позволяет реализовывать подзагрузку данных, совмещая старые и новые данные.#### Предназначение
Нередко возникает потребность разделить данные, получаемые из API, на части, и в последствии догружать их. Расширение
collector упрощает дозагрузку и совмещение данных для таких случаев.#### Подключение
Подключите
collectorMiddleware к вашему store как middleware
`jsx
// configureStore.js
import { createStore, applyMiddleware, compose } from 'redux'
import { collectorMiddleware } from '@aic/react-remote-data-provider/extensions/collector'export default function configureStore () {
// ...
const store = compose(
applyMiddleware(collectorMiddleware())
// ...
)(createStore)(rootReducer, initialState)
return store
}
`
Подробнее про collectorMiddleware в API reference#### Компонент
RemoteDataProviderCollector
После подключения collectorMiddleware, вы можете использовать RemoteDataProviderCollector для подзагрузки и совмещения данных. Этот компонент является оберткой над обычным RemoteDataProvider, имея такие же параметры, плюс несколько новых:
* changeableRequest: object - часть обычного request, которую можно впоследствии изменять, при этом данные будут собираться вместе; обязательный параметр.
* path?: Array - путь в response, в котором будут собираться данные. Если не указан, данные будут собираться в корне response.
* unshift?: boolean - если true, новые данные будут добавляться перед старыми (в начало), иначе - наоборот (по умолчанию false).
При этом, RemoteDataProviderCollector передает во второй аргумент children функции метод setChangeableRequest, позволяющий устанавливать новый changeableRequest.
Пример использования:
`jsx
import { RemoteDataProviderCollector } from '@aic/react-remote-data-provider/extensions/collector'const props = {
reducerKey: 'someKey',
request: {
url: '/api/users',
params: {
permission: 'admin'
}
},
changeableRequest: { // начальный changeableRequest
params: {
page: 1
}
},
path: 'user',
// остальные RemoteDataProvider props
}
{({ response, request }, { setChangeableRequest }) => {
console.log('response:', response)
console.log('request:', request)
const loadNextPage = () => setChangeableRequest({
params: {
page: request.params.page + 1
}
})
return
}}
// выведет в консоль:
// 'response:'
{
user: [ // данные собираются тут (path === 'user')
{ / некоторая информация о пользователе со страницы 1 / }
],
// остальные данные ответа только со страницы 1
}
// 'request:'
{
url: '/api/users',
params: {
permission: 'admin',
page: 1 // добавлено из
changeableRequest
}
}
// выведет в консоль после нажатия на кнопку 'load next page' и дозагрузки страницы 2:
// 'response:'
{
user: [
{ / некоторая информация о пользователе со страницы 1 / },
{ / некоторая информация о пользователе со страницы 2 / }
],
// остальные данные ответа только со страницы 2
}
// 'request:'
{
url: '/api/users',
params: {
permission: 'admin',
page: 2 // добавлено из changeableRequest
}
}
`
#### Особенности
* Параметр changeableRequest задает только начальные изменяющиеся параметры запроса. После использования метода setChangeableRequest в request попадают только новые установленные параметры.
* Используемый changeableRequest является частью конечного request, но также его можно получить во втором параметре children функции:
({ request, ... }, { setChangeableRequest, changeableRequest }) => { ... }
* Аргумент метода setChangeableRequest полностью заменяет changeableRequest, дублируйте параметры, даже если они не изменились. Например:
setChangeableRequest({ ...changeableRequest, someParam: 'new value' })
* Для корректной работы, ключи объекта changeableRequest не должны меняться. Если какой-то параметр изначально не используется, но в будущем изменится, задайте его undefined:
changeableRequest={{ params: { someParam: 'value', otherParam: undefined } }}
* После того, как компонент отмонтируется (например, вы перейдете на другую страницу), а потом обратно примонтируется (вернетесь обратно на предыдущую страницу) - RemoteDataProviderCollector восстановит последний changeableRequest из redux state. Для полного восстановления, необходимо, чтобы ключи changeableRequest не менялись (см. предыдущий пункт).
Если данные по ключу path в response - не* массив, то они оборачиваются в массив. Например, если path === 'user', а response === { user: { name: 'Dan', ... } } - response преобразуется в { user: [{ name: 'Dan', ... }] }. Если в текущем примере path не задан, response преобразуется в [{ user: { name: 'Dan', ... } }]
Подробнее про RemoteDataProviderCollector в API reference
#### Расширенное использование
collectorMiddleware добавляет несколько дополнительных параметров для обычного RemoteDataProvider:
* exCollectorChangeableRequest?: Array< string | Array - массив ключей в request, которые в нем могут меняться от запроса к запросу. При новой загрузке данных, если есть предыдущий результат, и в request поменялись значения только у ключей данного параметра, старые данные будут добавлены к новым. Иначе в redux будут записаны только новые данные. Если данный параметр не указан, все будет работать как обычно.
Ключи могут быть строками ('param', 'some.deep.param', 'some.array[0]') или массивами (['param'], ['some', 'deep', 'param'], ['some', 'array', '0']).
* exCollectorPath?: Array - путь в response, в котором будут собираться данные. Если не указан, данные будут собираться в корне response.
Если данные по собираемому пути - не массив, то они оборачиваются в массив. Например, если exCollectorPath === 'user', а response === { user: { name: 'Dan', ... } } - response преобразуется в { user: [{ name: 'Dan', ... }] }. Если в текущем примере exCollectorPath не задан, response преобразуется в [{ user: { name: 'Dan', ... } }].
Данный параметр работет, только если указан exCollectorChangeableRequest, иначе игнорируется и преобразования не происходит.
* exCollectorUnshift?: boolean - если true, новые данные будут добавляться перед старыми (в начало), иначе - наоборот (по умолчанию false).
Компонент-обертка RemoteDataProviderCollector использует эти параметры для своей работы. При необходимости, вы можете использовать их напрямую в обычном RemoteDataProvider, или разработать свой компонент-обертку.$3
Данное расширение позволяет собирать ошибки при загрузке данных любого RemoteDataProvider компонента в одном месте и получать их.
#### Предназначение
Хранение ошибок в одном месте упрощает ведение статистики, выдачу окна ошибки или страницы, описывающей ошибку.
#### Подключение
Подключите
globalErrorMiddleware к вашему store как middleware. Данная функция принимает один не обязательный параметр - объект, содержащий настройки для middleware:
* reduxKey?: string = '_globalError' - этот параметр определяет место храния ошибок в redux. Ошибки хранятся внутри remoteData, по этому, вы не можете использовать значение этого параметра как параметр reducerKey в компоненте RemoteDataProvider. По умолчанию он равен '_globalError'. Меняйте этот параметр, только если есть пересечения в названиях.
* setErrorKey?: string = 'exGlobalError' - ключ для параметра компонента RemoteDataProvider. Меняйте этот параметр, если у вас подключено несколько globalErrorMiddleware, или этот ключ пересекается с другим параметром. Далее в описании будет использоваться стандартное значение - 'exGlobalError'
* setByDefault?: boolean = false - этот параметр определяет exGlobalError (может называться иначе, если изменен setErrorKey) по умолчанию, если он не определен в параметрах RemoteDataProvider компонента. `jsx
// configureStore.js
import { createStore, applyMiddleware, compose } from 'redux'
import { collectorMiddleware } from '@aic/react-remote-data-provider/extensions/globalError'export default function configureStore () {
// ...
const store = compose(
applyMiddleware(
globalErrorMiddleware(), // без параметров
// или
globalErrorMiddleware({ // с параметрами
reduxKey: '_globalError',
setByDefault: false
})
)
// ...
)(createStore)(rootReducer, initialState)
return store
}
`
Подробнее про globalErrorMiddleware в API reference
#### Использование
globalErrorMiddleware добавляет для компонента RemoteDataProvider параметр exGlobalError (может называться иначе, если установлено значение setErrorKey в настройках middleware). Если данный параметр равен true, то ошибки загрузки, вызванные этим компонентом, будут собираться в глобальном хранилище redux.
`jsx
// ... обычные параметры
exGlobalError
/> // ошибки будут собираться в глобальном хранилище
// ... обычные параметры
/> // ошибки НЕ будут собираться в глобальном хранилище
`
Если вы установили значение setByDefault === true в настройках globalErrorMiddleware, то по умолчанию ошибки будут записываться в глобальное хранилище с любого RemoteDataProvider. Чтобы не собирать ошибки для отдельного компонента, установите значение exGlobalError в false:
`jsx
// ... обычные параметры
/> // ошибки будут собираться в глобальном хранилище
// ... обычные параметры
exGlobalError={false}
/> // ошибки НЕ будут собираться в глобальном хранилище
`
#### Получение и очистка ошибок
Для получения ошибок из глобального хранилища, используйте функции getGlobalErrors и hasGlobalError. Обе функции имеют одинаковые аргументы:
1. state - redux state
2. reduxKey = '_globalError' - должен быть аналогичным значению reduxKey из настроек globalErrorMiddleware. Если вы не устанавливали данную настройку для middleware, не используйте этот параметр.Функция
getGlobalErrors возвращает массив собранных ошибок, каждая из которых имеет такой формат:
`jsx
{
reducerKey: string,
reducerPath?: string,
error: {
status?: number,
message?: string,
data?: any
}
}
`
reducerKey и reducerPath равны соответствующим параметрам компонента RemoteDataProvider, в котором произошла ошибка.
Функция hasGlobalError возвращает true, если есть хотя бы одна ошибка в глобальном хранилище, иначе false.
Для очистки глобального хранилища от всех ошибок, используйте функцию clearGlobalError, которая создает действие. Используйте его в dispatch, и глобальное хранилище очистится.
`jsx
import { connect } from 'react-redux'
import { hasGlobalError, getGlobalErrors, clearGlobalError } from '@aic/react-remote-data-provider/extensions/globalError'function mapStateToProps (state) {
return {
isGlobalError: hasGlobalError(state),
globalErrors: getGlobalErrors(state)
}
}
function mapDispatchToProps (dispatch) {
clearGlobalError: () => dispatch(clearGlobalError())
}
@connect(mapStateToProps, mapDispatchToProps)
export class AppErrorWrapper extends React.Component {
render () {
const { isGlobalError, globalErrors, clearGlobalError } = this.props
console.log(isGlobalError) // => true
console.log(globalErrors) // => [{ reducerKey: 'someKey', error: { status: 404 } }]
if (isGlobalError) {
return (
Упс, что-то пошло не так...
Ошибок на странице: {globalErrors.length}
)
} else {
return
}
}
}
`Подробнее про расширение
globalError в API reference$3
Данное расширение позволяет организовывать асинхронные действия во время SSR (Server Side Render), включая загрузку данных с помощью RemoteDataProvider.#### Предназначение
react-dom/server при рендере сам по себе не позволяет производить асинхронные действия, которые часто требуются для загрузки данных и правильного отображения страницы. Это расширение позволяет проводить асинхронные операции между синхронными рендерами react-dom/server.#### Подключение
Подключите
asyncActionMiddleware к вашему store как middleware. Данная функция принимает один обязательный параметр - экземпляр класса AsyncActionController (будет рассмотрен позже).
`jsx
// configureStore.js
import { createStore, applyMiddleware, compose } from 'redux'
import { asyncActionMiddleware } from '@aic/react-remote-data-provider/extensions/serverReRender'export default function configureStore (asyncActionController) { // принимаем контроллер извне
// ...
const store = compose(
applyMiddleware(asyncActionMiddleware(asyncActionController))
// ...
)(createStore)(rootReducer, initialState)
return store
}
`#### Использование
serverReRender
asyncActionMiddleware позволяет отслеживать загрузки данных внутри компонентов RemoteDataProvider с помощью AsyncActionController. Теперь, для перерисовок между загрузками данных, необходимо использовать функцию serverReRender(options: object): Promise