redux-requests-factory

栏目: IT技术 · 发布时间: 4年前

Redux Requests Factory

npm install redux-requests-factory

// or

npm install redux-requests-factory --save

Table of Contents

  • Redux Requests Factory
    • Requests Factory Config
        • config.stateRequestKey
        • config.serializeRequestParameters
        • config.debounceOptions
        • config.stringifyParamsForDebounce
        • config.transformError
        • config.transformResponse
        • config.rejectedActions
        • config.fulfilledActions
        • config.includeInGlobalLoading
    • Requests Factory Instance
      • Requests Factory Instance Actions
      • isSomethingLoadingSelector
    • Create Redux Requests Factory

Examples

All examples

create-react-app

next.js

redux-observabl

Installation

redux-requests-factory is available on npm .

npm install redux-requests-factory

store.js

import { createStore, applyMiddleware, combineReducers } from 'redux';
import {
  stateRequestsKey,
  requestsReducer,
  createRequestsFactoryMiddleware,
} from 'redux-requests-factory';

export const reducer = combineReducers({
  [stateRequestsKey]: requestsReducer,
  // ... others reducers
});

const {
  middleware: requestsFactoryMiddleware
} = createRequestsFactoryMiddleware();

const reduxMiddleware = applyMiddleware(requestsFactoryMiddleware);

const store = createStore(reducer, reduxMiddleware);

export default store;

Usage

import { requestsFactory } from 'redux-requests-factory';

const loadUsersRequest = () =>
  fetch('https://mysite.com/users').then(res => res.json());

export const {
  // actions
  loadDataAction, // do request once (can be dispatched many times, but do request once)
  forcedLoadDataAction, // do request every time (used when need reload data)
  doRequestAction, // do request every time (used for create, update and delete requests)
  cancelRequestAction, // cancel request
  requestFulfilledAction, // dispatched when request fulfilled
  requestRejectedAction, // dispatched when request rejected
  setErrorAction, // set custom Error for this request (requestRejectedAction will be dispatched)
  setResponseAction, // set response for this request (requestFulfilledAction will be dispatched)
  resetRequestAction, // reset request data
  // selectors
  responseSelector, // returns `response || []`
  errorSelector, // returns Error when request rejected or undefined
  requestStatusSelector, // returns request status ('none', 'loading', 'success', 'failed', 'canceled')
  isLoadingSelector, // returns true when request status === 'loading'
  isLoadedSelector, // returns true when request status === 'success'
} = requestsFactory({
  request: loadUsersRequest,
  stateRequestKey: 'users',
  transformResponse: (response) => response || [],
});
import React from 'react';
import { useSelector } from 'react-redux';
import { isSomethingLoadingSelector } from 'redux-requests-factory';

const App = () => {
  const isSomethingLoading = useSelector(isSomethingLoadingSelector); // returns true when something loads

  return (
    <>
      {isSomethingLoading ? <div>'Something Loading...'</div> : null}
    </>
  );
}

Example

requests/users.js

Click to expand example!
import { requestsFactory } from 'redux-requests-factory';

const loadUsersRequest = () =>
  fetch('https://mysite.com/users').then(res => res.json());

export const {
  loadDataAction: loadUsersAction, // do request once
  forcedLoadDataAction: forcedLoadUsersAction, // do request every time
  cancelRequestAction: cancelLoadUsersAction,
  responseSelector: usersSelector, // return `response || []`
  errorSelector: loadUsersErrorSelector,
  isLoadingSelector: isLoadingUsersSelector,
  isLoadedSelector: isLoadedUsersSelector,
} = requestsFactory({
  request: loadUsersRequest,
  stateRequestKey: 'users',
  transformResponse: (response) => response || [],
});

requests/posts-by-user.js

Click to expand example!
import { requestsFactory } from 'redux-requests-factory';

const loadUserPostsRequest = ({ userId }) =>
  fetch(
    `https://mysite.com/posts?userId=${userId}`
  ).then(res => res.json());

export const {
  loadDataAction: loadUserPostsAction,
  forcedLoadDataAction: forcedLoadUserPostsAction,
  setResponseAction: setUserPostsAction,
  responseSelector: userPostsSelector, // return function `({ userId }) => response || []`
} = requestsFactory({
  request: loadUserPostsRequest,
  stateRequestKey: 'user-posts',
  useDebounce: true,
  serializeRequestParameters: ({ userId }) => `${userId}`, // selector will return function
  transformResponse: (response) => response || [],
});

requests/add-post.js

Click to expand example!
import { requestsFactory } from 'redux-requests-factory';

import { setUserPostsAction, userPostsSelector } from './posts-by-user';

const addPostRequest = ({ userId, title, body }) =>
  fetch('https://mysite.com/posts', {
    method: 'POST',
    body: JSON.stringify({
      title,
      body,
      userId,
    }),
    headers: {
      'Content-type': 'application/json; charset=UTF-8',
    },
  }).then(res => res.json());

export const {
  doRequestAction: addPostAction,
  cancelRequestAction: cancelAddPostAction,
  isLoadingSelector: isLoadingAddPostSelector,
} = requestsFactory({
  request: addPostRequest,
  stateRequestKey: 'add-post',
  includeInGlobalLoading: false, // not include in isSomethingLoadingSelector
  serializeRequestParameters: ({ userId }) => `${userId}`,
  fulfilledActions: [ // this actions calls when addPostRequest fulfilled
    ({ response, request: { userId }, state }) => {
      return setUserPostsAction({
        response: [...userPostsSelector(state)({ userId }), response],
        params: { userId },
      });
    },
  ],
});

App.js

Click to expand example!
import React, { useCallback, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { isSomethingLoadingSelector } from 'redux-requests-factory';

import {
  loadUsersAction,
  forcedLoadUsersAction,
  cancelLoadUsersAction,
  usersSelector,
} from '../requests/users';
import {
  loadUserPostsAction,
  userPostsSelector,
  forcedLoadUserPostsAction,
} from '../requests/posts-by-user';
import {
  addPostAction,
  isLoadingAddPostSelector,
  cancelAddPostAction,
} from '../requests/add-post';

const App = () => {
  const dispatch = useDispatch();

  const users = useSelector(usersSelector);
  const postsByUser = useSelector(userPostsSelector);
  const isSomethingLoading = useSelector(isSomethingLoadingSelector);
  const isLoadingAddPost = useSelector(isLoadingAddPostSelector);

  const onLoadUsers = useCallback(() => dispatch(loadUsersAction()), [
    dispatch,
  ]);
  const onForcedLoadUsers = useCallback(
    () => dispatch(forcedLoadUsersAction()),
    [dispatch]
  );
  const onCancelLoadUsers = useCallback(
    () => dispatch(cancelLoadUsersAction()),
    [dispatch]
  );
  const onLoadUserPosts = useCallback(
    (userId) => dispatch(loadUserPostsAction({ userId })),
    [dispatch]
  );
  const onForcedLoadUserPosts = useCallback(
    (event) => {
      dispatch(
        forcedLoadUserPostsAction({
          userId: event.currentTarget.dataset.userId,
        })
      );
    },
    [dispatch]
  );
  const onAddPost = useCallback(
    (even) => {
      event.preventDefault();
      const form = event.currentTarget;
      const elements = form.elements;
      const userId = form.dataset.userId;

      dispatch(cancelAddPostAction({ userId }));
      dispatch(
        addPostAction({
          userId,
          title: elements.title.value,
          body: elements.body.value,
        })
      );
    },
    [dispatch]
  );

  useEffect(() => {
    onLoadUsers();
  }, [onLoadUsers]);

  useEffect(() => {
    if (users) {
      users.forEach(({ id }) => {
        onLoadUserPosts(id);
      });
    }
  }, [users, onLoadUserPosts]);

  return (
    <div>
      <div>
        {isSomethingLoading ? 'Something Loading...' : null}
      </div>

      <button onClick={onLoadUsers}>
        Load Users
      </button>
      <button onClick={onForcedLoadUsers}>
        Forced Load Users
      </button>
      <button onClick={onCancelLoadUsers}>
        Cancel Load Users
      </button>

      <ul>
        {users.map(({ id, name }) => (
          <li key={id}>
            {name}
            <ul>
              {postsByUser({ userId: id }).map(({ id, title }, index) => (
                <li key={`${id}_${index}`}>{title}</li>
              ))}
              <button
                data-user-id={id}
                onClick={onForcedLoadUserPosts}
              >
                Forced Load User Posts With Debounce 500ms
              </button>
              <form
                data-user-id={id}
                onSubmit={onAddPost}
              >
                <h3>Add new post </h3>
                <label>
                  Title
                  <input id={`title_${id}`} name="title" />
                </label>
                <label>
                  Body
                  <textarea name="body" />
                </label>
                <button
                  type="submit"
                  disabled={isLoadingAddPost({ userId: id })}
                >
                  {isLoadingAddPost({ userId: id }) ? 'Loading...' : 'Add'}
                </button>
              </form>
            </ul>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default App;

Requests Factory Config

import { requestsFactory } from 'redux-requests-factory';

const {...} = requestsFactory({
  request: ({ id }) => fetch(`https://mysite.com/api/user/${id}`).then(res => res.json()),
  stateRequestKey: 'user',
  serializeRequestParameters: ({ id }) => `${id}`,
  // Debounce
  useDebounce: true,
  debounceWait: 500,
  stringifyParamsForDebounce: ({ id }) => `${id}`,
  debounceOptions: {
    leading: true,
    trailing: false,
    maxWait: 500,
  },
  // Request rejected
  transformError: (error) => error && error.message,
  rejectedActions: [({ error, request: { id }, state }) => {
    // return the actions that should be dispatched when request is rejected
    return { type: 'SHOW_NOTIFICATION' }
  }],
  // Request fulfilled
  transformResponse: (response) => response || {},
  fulfilledActions: [({ response, request: { id }, state }) => {
    // return the actions that should be dispatched when request is fulfilled
    return { type: 'SHOW_NOTIFICATION' }
  }],
  // Loading
  includeInGlobalLoading: true,
});

Required

config.request

request is required field, it is should be function that takes parameters (or not) and returns Promise

with params:

const {
  doRequestAction,
  forcedLoadDataAction,
  loadDataAction,
} = requestsFactory({
  request: ({ id }) => fetch(`https://mysite.com/api/user/${id}`).then(res => res.json()),
  stateRequestKey: 'user',
});

doRequestAction({ id: 1 });
// or
forcedLoadDataAction({ id: 1 });
// or
loadDataAction({ id: 1 });

or without params:

const {
  doRequestAction,
  forcedLoadDataAction,
  loadDataAction,
} = requestsFactory({
  request: () => fetch('https://mysite.com/api/users').then(res => res.json()),
  stateRequestKey: 'users',
});

doRequestAction();
// or
forcedLoadDataAction();
// or
loadDataAction();

config.stateRequestKey

stateRequestKey is required field, it is should be unique string key between all requests

const {...} = requestsFactory({
  stateRequestKey: 'users',
});

const {...} = requestsFactory({
  stateRequestKey: 'user-posts',
});

// Now the state is like here
// state = {
//   [stateRequestsKey]: {
//     responses: {
//       users: {
//         status: RequestsStatuses.None,
//         response: undefined,
//         error: undefined,
//       },
//       'user-posts': {
//         status: RequestsStatuses.None,
//         response: undefined,
//         error: undefined,
//       },
//     },
//   },
// };

Serialize

config.serializeRequestParameters

serializeRequestParameters is not required field, it is should be function that takes parameters and returns string .

When used serializeRequestParameters all selectors return function that takes parameters and returns selected value. When used serializeRequestParameters params are required for all actions.

const {
  loadDataAction,
  forcedLoadDataAction,
  doRequestAction,
  cancelRequestAction,
  requestFulfilledAction,
  requestRejectedAction,
  setErrorAction,
  setResponseAction,
  resetRequestAction,

  responseSelector,
  errorSelector,
  requestStatusSelector,
  isLoadingSelector,
  isLoadedSelector,
} = requestsFactory({
  request: ({ id }) => fetch(`https://mysite.com/api/user/${id}`).then(res => res.json()),
  stateRequestKey: 'user',
  serializeRequestParameters: ({ id }) => `${id}`,
});

loadDataAction({ id: 1 });
forcedLoadDataAction({ id: 1 });
doRequestAction({ id: 1 });
cancelRequestAction({ id: 1 });
setErrorAction({
  error: new Error(),
  params: { id: 1 },
});
setResponseAction({
  response: { id: 1, name: 'Name' },
  params: { id: 1 },
});
resetRequestAction({ id: 1 });

responseSelector(store)({ id: 1 });
errorSelector(store)({ id: 1 });
requestStatusSelector(store)({ id: 1 });
isLoadingSelector(store)({ id: 1 });
isLoadedSelector(store)({ id: 1 });


// loadDataAction({ id: 1 });
// loadDataAction({ id: 2 });
// loadDataAction({ id: 3 });
//
// Now the state is like here
// state = {
//   [stateRequestsKey]: {
//     responses: {
//       user: {
//         '1': {
//           status: RequestsStatuses.Loading,
//           response: undefined,
//           error: undefined,
//         },
//         '2': {
//           status: RequestsStatuses.Loading,
//           response: undefined,
//           error: undefined,
//         },
//         '3': {
//           status: RequestsStatuses.Loading,
//           response: undefined,
//           error: undefined,
//         },
//       },
//     },
//   },
// };

Debounce

config.useDebounce

useDebounce is not required field, default value - false .

When useDebounce: true requestsFactory creates debounced actions doRequestAction , forcedLoadDataAction and loadDataAction that delays dispatch action with same params until after wait config.debounceWait milliseconds have elapsed since the last time the debounced action was dispatched. Detect same params helps config.stringifyParamsForDebounce . For debounce used lodash.debounce and you can use own debounce options config.debounceOptions .

const {...} = requestsFactory({
  useDebounce: true,
});

config.debounceWait

debounceWait is not required field, default value - 500 . Used when config.useDebounce: true .

const {...} = requestsFactory({
  debounceWait: 300,
});

config.debounceOptions

debounceOptions is not required field, default value:

{
  leading: true,
  trailing: false,
  maxWait: config.debounceWait,
}

It is options for lodash.debounce . Used when config.useDebounce: true .

const {...} = requestsFactory({
  debounceOptions: {
    leading: true,
    trailing: false,
    maxWait: 300,
  },
});

config.stringifyParamsForDebounce

stringifyParamsForDebounce is not required field, default value - JSON.stringify . It is should be function that takes parameters and returns string . Used when config.useDebounce: true .

const {...} = requestsFactory({
  request: ({ id }) => fetch(`https://mysite.com/api/user/${id}`).then(res => res.json()),
  stateRequestKey: 'user',
  stringifyParamsForDebounce: ({ id }) => `${id}`,
});

Transform

config.transformError

transformError is not required field, it is should be function that takes request error or undefined and returns transformed error or undefined . transformError used for errorSelector .

const {
  errorSelector,
} = requestsFactory({
  transformError: (error) => error && `Error: ${error.message}`,
});

errorSelector(state); // undefined  or `Error: ${error.message}`

config.transformResponse

transformResponse is not required field, it is should be function that takes request response or undefined and returns transformed response . transformResponse used for responseSelector . Better use transformResponse for setting default value.

NOTE: For best performance, do not use transformResponse with serializeRequestParameters for expensive transformations. For all expensive transforms better use reselect .

const {
  responseSelector,
} = requestsFactory({
  transformResponse: (response) => response || [],,
});

responseSelector(state); // []

Actions

config.rejectedActions

rejectedActions is not required field, default value - [] . It is should be array with actions or with functions that takes object { error, request, state } as parameter and returns action or [action, action, ...] that will be dispatched when request is rejected.

const {...} = requestsFactory({
  request: ({ id }) => fetch(`https://mysite.com/api/user/${id}`).then(res => res.json()),
  stateRequestKey: 'user',
  rejectedActions: [
    { type: 'SHOW_NOTIFICATION' }, // simple action
    ({ error, request: { id }, state }) => {
      // ...
      return id === 1 ? { type: 'SHOW_ERROR' } : null;
    }, // function that returns an action or null
    ({ error, request: { id }, state }) => {
      // ...
      return [{ type: 'SHOW_ERROR' }, (id === 1 ? { type: 'SHOW_ERROR' } : null) ];
    }, // function that returns an array with actions or null
  ],
});

config.fulfilledActions

fulfilledActions is not required field, default value - [] . It is should be array with actions or with functions that takes object { response, request, state } as parameter and returns action or [action, action, ...] that will be dispatched when request is fulfilled.

const {...} = requestsFactory({
  request: ({ id }) => fetch(`https://mysite.com/api/user/${id}`).then(res => res.json()),
  stateRequestKey: 'user',
  fulfilledActions: [
    { type: 'SHOW_NOTIFICATION' }, // simple action
    ({ response, request: { id }, state }) => {
      // ...
      return !response ? { type: 'SHOW_ERROR' } : null;
    }, // function that returns an action or null
    ({ response, request: { id }, state }) => {
      // ...
      return [{ type: 'SHOW_NOTIFICATION' }, (!response ? { type: 'SHOW_ERROR' } : null) ];
    }, // function that returns an array with actions or null
  ],
});

Global Loading

config.includeInGlobalLoading

includeInGlobalLoading is not required field, default value - true . It is should be boolean. When includeInGlobalLoading: true and request is loading, global isSomethingLoadingSelector will be return true . If includeInGlobalLoading: false you can use isLoadingSelector

import { isSomethingLoadingSelector } from 'redux-requests-factory';

const {
  isLoadingSelector,
} = requestsFactory({
  includeInGlobalLoading: false,
});

Requests Factory Instance

import { requestsFactory } from 'redux-requests-factory';

export const {
  // actions
  loadDataAction, // do request once (can be dispatched many times, but do request once)
  forcedLoadDataAction, // do request every time (used when need reload data)
  doRequestAction, // do request every time (used for create, update and delete requests)
  cancelRequestAction, // cancel request
  requestFulfilledAction, // dispatched when request fulfilled
  requestRejectedAction, // dispatched when request rejected
  setErrorAction, // set custom Error for this request (requestRejectedAction will be dispatched)
  setResponseAction, // set response for this request (requestFulfilledAction will be dispatched)
  resetRequestAction, // reset request data
  // selectors
  responseSelector, // returns `response || []`
  errorSelector, // returns Error when request rejected or undefined
  requestStatusSelector, // returns request status ('none', 'loading', 'success', 'failed', 'canceled')
  isLoadingSelector, // returns true when request status === 'loading'
  isLoadedSelector, // returns true when request status === 'success'
} = requestsFactory({
  request: ({ id }) => fetch(`https://mysite.com/api/user/${id}`).then(res => res.json()),
  stateRequestKey: 'user',
  transformResponse: (response) => response || [],
});

Requests Factory Instance Actions

loadDataAction

loadDataAction do request once (can be dispatched many times, but do request once)

export const {
  loadDataAction,
} = requestsFactory({
  request: ({ id }) => fetch(`https://mysite.com/api/user/${id}`).then(res => res.json()),
  stateRequestKey: 'user',
});

dispatch(loadDataAction({ id: 1 }));

forcedLoadDataAction

forcedLoadDataAction do request every time (used when need reload data)

export const {
  forcedLoadDataAction,
} = requestsFactory({
  request: ({ id }) => fetch(`https://mysite.com/api/user/${id}`).then(res => res.json()),
  stateRequestKey: 'user',
});

dispatch(forcedLoadDataAction({ id: 1 }));

doRequestAction

doRequestAction do request every time (used for create, update and delete requests)

export const {
  doRequestAction,
} = requestsFactory({
  request: ({ id }) => fetch(`https://mysite.com/api/user/${id}`).then(res => res.json()),
  stateRequestKey: 'user',
});

dispatch(doRequestAction({ id: 1 }));

cancelRequestAction

cancelRequestAction cancel active request

export const {
  cancelRequestAction,
} = requestsFactory({
  request: ({ id }) => fetch(`https://mysite.com/api/user/${id}`).then(res => res.json()),
  stateRequestKey: 'user',
});

dispatch(cancelRequestAction());

requestFulfilledAction

requestFulfilledAction dispatched with payload: { params, response } when request fulfilled. Can be used for subscriptions ( redux-observable , redux-saga ).

import { ofType } from 'redux-observable';

export const {
  requestFulfilledAction,
} = requestsFactory({
  request: ({ id }) => fetch(`https://mysite.com/api/user/${id}`).then(res => res.json()),
  stateRequestKey: 'user',
});

const loadUserFulfilledEpic = (action$, state$) =>
  action$.pipe(
    ofType(requestFulfilledAction),
    tap(({ payload: { params: { id }, response } }) => {
      alert(`User ${id} is loaded`);
    }),
    ignoreElements()
  );

requestRejectedAction

requestRejectedAction dispatched with payload: { params, error } when request rejected. Can be used for subscriptions ( redux-observable , redux-saga ).

import { ofType } from 'redux-observable';

export const {
  requestRejectedAction,
} = requestsFactory({
  request: ({ id }) => fetch(`https://mysite.com/api/user/${id}`).then(res => res.json()),
  stateRequestKey: 'user',
});

const loadUserRejectedEpic = (action$, state$) =>
  action$.pipe(
    ofType(requestRejectedAction),
    tap(({ payload: { params: { id }, error } }) => {
      alert(`User ${id} is not loaded`);
    }),
    ignoreElements()
  );

setErrorAction

setErrorAction set custom Error for this request ( requestRejectedAction will be dispatched)

export const {
  setErrorAction,
} = requestsFactory({
  request: ({ id }) => fetch(`https://mysite.com/api/user/${id}`).then(res => res.json()),
  stateRequestKey: 'user',
});

dispatch(setErrorAction({ error: 'some error' }));

setResponseAction

setResponseAction set response for this request ( requestFulfilledAction will be dispatched)

export const {
  setResponseAction,
} = requestsFactory({
  request: ({ id }) => fetch(`https://mysite.com/api/user/${id}`).then(res => res.json()),
  stateRequestKey: 'user',
});

dispatch(setResponseAction({ response: { id: 1, name: 'Name' } }));

resetRequestAction

resetRequestAction reset request data. Set undefined to response and error , and set RequestsStatuses.None to status .

export const {
  resetRequestAction,
  responseSelector,
  errorSelector,
  requestStatusSelector,
} = requestsFactory({
  request: ({ id }) => fetch(`https://mysite.com/api/user/${id}`).then(res => res.json()),
  stateRequestKey: 'user',
});

dispatch(resetRequestAction());

responseSelector(state); // undefined
errorSelector(state); // undefined
requestStatusSelector(state); // RequestsStatuses.None

Selectors

responseSelector

responseSelector returns response when request fulfilled or undefined

export const {
  responseSelector,
} = requestsFactory({
  request: ({ id }) => fetch(`https://mysite.com/api/user/${id}`).then(res => res.json()),
  stateRequestKey: 'user',
});

responseSelector(state);

errorSelector

errorSelector returns Error when request rejected or undefined

export const {
  errorSelector,
} = requestsFactory({
  request: ({ id }) => fetch(`https://mysite.com/api/user/${id}`).then(res => res.json()),
  stateRequestKey: 'user',
});

errorSelector(state);

requestStatusSelector

requestStatusSelector returns request status ( RequestsStatuses.None , RequestsStatuses.Loading , RequestsStatuses.Success , RequestsStatuses.Failed , RequestsStatuses.Canceled )

import { RequestsStatuses } from 'redux-requests-factory';

export const {
  requestStatusSelector,
} = requestsFactory({
  request: ({ id }) => fetch(`https://mysite.com/api/user/${id}`).then(res => res.json()),
  stateRequestKey: 'user',
});

requestStatusSelector(state) === RequestsStatuses.None;

isLoadingSelector

isLoadingSelector returns true when request status === RequestsStatuses.Loading

export const {
  isLoadingSelector,
} = requestsFactory({
  request: ({ id }) => fetch(`https://mysite.com/api/user/${id}`).then(res => res.json()),
  stateRequestKey: 'user',
});

isLoadingSelector(state);

isLoadedSelector

isLoadedSelector returns true when request status === RequestsStatuses.Success

export const {
  isLoadedSelector,
} = requestsFactory({
  request: ({ id }) => fetch(`https://mysite.com/api/user/${id}`).then(res => res.json()),
  stateRequestKey: 'user',
});

isLoadedSelector(state);

Global Selectors

isSomethingLoadingSelector

isSomethingLoadingSelector returns true when something loads

import { isSomethingLoadingSelector } from 'redux-requests-factory';

isSomethingLoadingSelector(state);

Create Redux Requests Factory

Used if you need more than one instance of createReduxRequestsFactory

import createReduxRequestsFactory from 'redux-requests-factory';

export const {
  stateRequestsKey, // 'api-key-one'
  createRequestsFactoryMiddleware, // Middleware for 'api-key-one'
  requestsFactory, // requestsFactory for 'api-key-one'
  requestsReducer, // requestsReducer for 'api-key-one'
  isSomethingLoadingSelector, // isSomethingLoadingSelector for 'api-key-one'
} = createReduxRequestsFactory({
  stateRequestsKey: 'api-key-one',
});

export const {
  stateRequestsKey, // 'api-key-two'
  createRequestsFactoryMiddleware, // Middleware for 'api-key-two'
  requestsFactory, // requestsFactory for 'api-key-two'
  requestsReducer, // requestsReducer for 'api-key-two'
  isSomethingLoadingSelector, // isSomethingLoadingSelector for 'api-key-two'
} = createReduxRequestsFactory({
  stateRequestsKey: 'api-key-two',
});

SSR

store.js

const makeStore = (initialState) => {
  const { middleware, toPromise } = createRequestsFactoryMiddleware();
  const reduxMiddleware = applyMiddleware(middleware);

  const store = createStore(reducer, initialState, reduxMiddleware);

  store.asyncRequests = toPromise;

  return store;
};

app.js

const loadData = async ({ isServer, store }) => {
  store.dispatch(loadUsersAction());

  if (isServer) {
    await store.asyncRequests();
  }
}

TypeScript

Full support TypeScript

TypeScript + create-react-app + redux-requests-factory

TypeScript + next.js + redux-requests-factory

License

MIT


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

乔布斯离开了,马斯克来了

乔布斯离开了,马斯克来了

[日]竹内一正 / 干太阳 / 中信出版社 / 2015-11

在电动汽车的创新上,特斯拉抓住了一个群体的独特需求,外形很酷,不烧油,智能化控制。所有的颠覆式创新都不是敲锣打鼓来的,而是隐藏在一片噪声里,马斯克给我们带来的特斯拉虽然不尽完美,但他做产品的思维和执着于未来的勇气,值得学习。埃隆•马斯克创办公司也不是为了赚钱,而是为了拯救人类和地球,电动汽车、太阳能发电、宇宙火箭,不管是哪一项都足以令一个国家付出巨大的代价去研究开发,但埃隆•马斯克却一个人在做这些......一起来看看 《乔布斯离开了,马斯克来了》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

随机密码生成器
随机密码生成器

多种字符组合密码

URL 编码/解码
URL 编码/解码

URL 编码/解码