我在redux世界中很新,还没有一个项目以 ducks方式构建.我试图理解它并使用它来制作一个模拟API,因为我还没有准备好后端.我正在使用遗留代码,我想弄清楚.有一个名为data的文件夹,它有一个duck和一个backendApi文件. Duck文件看起来像这样.

数据/ duck.jsx

import { createSelector } from 'reselect';
import { createReduxApi } from './backendApi';

const getDataContext = state => state.default.dataContext;

const backendReduxApi = createBackendReduxApi(getDataContext);

// Action creators
export const makeRestApiRequest = endpointName => backendReduxApi .makeRequestActionCreator(endpointName);

export const resetRestApi = endpointName => backendReduxApi .makeResetActionCreator(endpointName);

// Reducers
export const dataReducer = backendReduxApi .createReducer();

// Selectors
const getRestApiState = endpointName => backendReduxApi .getEndpointState(endpointName);
export const getRestApiData = endpointName => createSelector([getRestApiState(endpointName)],apiState => apiState.data);
export const getRestApiMeta = endpointName => createSelector([getRestApiState(endpointName)],apiState => apiState.Meta);
export const getRestApiError = endpointName => createSelector([getRestApiState(endpointName)],apiState => apiState.error);
export const getRestApiStarted = endpointName => createSelector([getRestApiState(endpointName)],apiState => apiState.started);
export const getRestApiFinished = endpointName => createSelector([getRestApiState(endpointName)],apiState => apiState.finished);

backendApi.jsx文件如下所示:

数据/ backendApi.jsx

import ReduxRestApi from './rest/ReduxRestApi';

export const BackendApi = { // NOSONAR
  LANGUAGE_FILE: 'languageFile',EMPLOYEE: 'employee',};

const backendReduxApiBuilder = ReduxRestApi.build()
  /* /api */

  /* /api/employee */
  .withGet('/myproject/api/employee',BackendApi.EMPLOYEE)

  /* /language*/
  .withGet('/myproject/language/nb_NO.json',BackendApi.LANGUAGE_FILE)

export const createBackendReduxApi = restApiSelector => backendReduxApiBuilder
  .withRestApiSelector(restApiSelector)
  .create();

然后在data / rest文件夹中我有4个文件:ReduxRestApi,restConfig,RestDuck和restMethods.

数据/休息/ ReduxRestApi.jsx

import { combineReducers } from 'redux';
import { get,post,postAndOpenBlob } from './restMethods';
import RestDuck from './RestDuck';

class ReduxRestApi {
  constructor(endpoints,getRestApiState) {
    this.createReducer = this.createReducer.bind(this);
    this.getEndpoint = this.getEndpoint.bind(this);
    this.makeRequestActionCreator = this.makeRequestActionCreator.bind(this);
    this.makeResetActionCreator = this.makeResetActionCreator.bind(this);
    this.getEndpointState = this.getEndpointState.bind(this);
    this.ducks = endpoints.map(({ name,path,restMethod }) => new RestDuck(name,restMethod,getRestApiState));
  }

  createReducer() {
    const reducers = this.ducks
      .map(duck => ({ [duck.name]: duck.reducer }))
      .reduce((a,b) => ({ ...a,...b }),{});
    return combineReducers(reducers);
  }

  getEndpoint(endpointName) {
    return this.ducks.find(duck => duck.name === endpointName)
      || { actionCreators: {} };
  }

  makeRequestActionCreator(endpointName) {
    return this.getEndpoint(endpointName).actionCreators.execRequest;
  }

  makeResetActionCreator(endpointName) {
    return this.getEndpoint(endpointName).actionCreators.reset;
  }

  getEndpointState(endpointName) {
    return this.getEndpoint(endpointName).stateSelector;
  }

  static build() {
    class RestApiBuilder {
      constructor() {
        this.withGet = this.withGet.bind(this);
        this.withPost = this.withPost.bind(this);
        this.withPostAndOpenBlob = this.withPostAndOpenBlob.bind(this);
        this.withRestApiSelector = this.withRestApiSelector.bind(this);
        this.endpoints = [];
      }

      withGet(path,name) {
        this.endpoints.push({ path,name,restMethod: get });
        return this;
      }

      withPost(path,restMethod: post });
        return this;
      }

      withPostAndOpenBlob(path,restMethod: postAndOpenBlob });
        return this;
      }

      withRestApiSelector(restApiSelector) {
        this.restApiSelector = restApiSelector;
        return this;
      }

      create() {
        return new ReduxRestApi(
          this.endpoints,this.restApiSelector
        );
      }
    }

    return new RestApiBuilder();
  }
}

export default ReduxRestApi;

restConfig.jsx

import axios from 'axios';
import { removeErrorMessage,showErrorMessage } from '../../app/duck';
import { is401Error,isHandledError } from '../../app/ErrorTypes';

const isDevelopment = process.env.NODE_ENV === 'development';

const configureRequestInterceptors = (store) => {
  const onRequestAccepted = (config) => {
    store.dispatch(removeErrorMessage());
    return config;
  };

  const onRequestRejected = error => Promise.reject(error);

  axios.interceptors.request.use(onRequestAccepted,onRequestRejected);
};

const configureResponseInterceptors = (store) => {
  const onSuccessResponse = response => response;

  const onErrorResponse = (error) => {
    if (is401Error(error) && !isDevelopment) {
      window.location.reload();
    }
    if (!isHandledError(error)) {
      store.dispatch(showErrorMessage(error));
    }
    return Promise.reject(error);
  };

  axios.interceptors.response.use(onSuccessResponse,onErrorResponse);
};

const configureRestInterceptors = (store) => {
  configureRequestInterceptors(store);
  configureResponseInterceptors(store);
};

export default configureRestInterceptors;

数据/休息/ RestDuck.jsx

import { createSelector } from 'reselect';

import { get,getBlob,postAndOpenBlob,postBlob } from './restMethods';

/**
 * getmethodName
 * Helper function that maps given AJAX-method to a name
 *
 * Ex. getmethodName(getBlob) -> 'GET'
 */
const getmethodName = (restMethod) => {
  switch (restMethod) {
    case get:
    case getBlob:
      return 'GET';
    case post:
    case postBlob:
    case postAndOpenBlob:
      return 'POST';
    default:
      return '';
  }
};

/**
 * createRequestActionType
 * Helper function to generate actionType for actions related to AJAX calls
 *
 * Ex: createRequestActionType('fetchEmployee','ERROR',get,'/myproject/api/employee') -> '@@REST/fetchEmployee GET /myproject/api/employeeERROR'
 */
const createRequestActionType = (name,qualifier,restMethod = '',path = '') => [`@@REST/${name}`,getmethodName(restMethod),qualifier]
  .filter(s => s !== '')
  .join(' ');

/**
 * createRequestActionTypes
 * Helper function to generate ActionTypes for a given AJAX method and resource.
 *
 * Ex. createRequestActionType(fetchEmployee,'/myproject/api/employee') -> {
 *   reset: '@@REST GET /myproject/api/employee RESET',*   requestStarted: '@@REST GET /myproject/api/employee STARTED',*   requestError: '@@REST GET /myproject/api/employee ERROR',*   requestFinished: '@@REST GET /myproject/api/employee FINISHED',* }
 */
const createRequestActionTypes = (name,path) => ({
  reset: createRequestActionType(name,'RESET'),requestStarted: createRequestActionType(name,'STARTED',path),requestError: createRequestActionType(name,requestFinished: createRequestActionType(name,'FINISHED',path)
});

/**
 * createRequestThunk
 * Helper function that generates a thunk that performs an AJAX call specified by 'restMethod' and 'restEndpoint'
 *
 * When the thunk is running,the action 'requestStarted' will be dispatched immediately.
 * Then,it performs the AJAX call that returns a promise.
 *  If the call goes well,the action 'requestFinished' will be dispatched with data from the call.
 * If the call fails,the action 'requestError' is dispatched with the contents of the error.
 */
const createRequestThunk = (restMethod,restEndpoint,requestStarted,requestFinished,requestError) => (
  (params,options = {}) => (dispatch) => {
    dispatch(requestStarted(params,options));
    return restMethod(restEndpoint,params)
      .catch((error) => {
        const data = error.response && error.response.data ? error.response.data : error;
        dispatch(requestError(data));
        return Promise.reject(error);
      })
      .then((response) => {
        dispatch(requestFinished(response.data));
        return response;
      });
  }
);

/**
 * createRequestActionCreators
 * Helper function that creates action creators 'requestStarted','requestFinished' and 'requestError',* @see createRequestThunkCreator
 */
const createRequestActionCreators = (restMethod,actionTypes) => {
  const reset = () => ({ type: actionTypes.reset });
  const requestStarted = (params,options = {}) => ({ type: actionTypes.requestStarted,payload: { params,timestamp: Date.Now() },Meta: { options } });
  const requestFinished = data => ({ type: actionTypes.requestFinished,payload: data });
  const requestError = error => ({ type: actionTypes.requestError,payload: error });
  const execRequest = createRequestThunk(restMethod,requestError);
  return {
    reset,requestError,execRequest
  };
};

/**
 * createRequestReducer
 *
 * Helper function that creates a reducer for an AJAX call.
 * Reducer alters the state of the actions with the name defined by
 *   actionTypes.requestStarted
 *   actionTypes.requestFinished
 *   actionTypes.requestError
 */
const createRequestReducer = (restMethod,resourceName,actionTypes) => {
  const initialState = {
    data: undefined,Meta: undefined,error: undefined,started: false,finished: false
  };

  return (state = initialState,action = {}) => {
    switch (action.type) {
      case actionTypes.requestStarted:
        return {
          ...initialState,data: action.Meta.options.keepData ? state.data : initialState.data,started: true,Meta: action.payload
        };
      case actionTypes.requestFinished:
        return {
          ...state,finished: true,data: action.payload
        };
      case actionTypes.requestError:
        return {
          ...state,error: action.payload
        };
      case actionTypes.reset:
        return {
          ...initialState
        };
      default:
        return state;
    }
  };
};

/**
 * RestDuck
 * Class that offers action types,action creators,reducers and selectors for an AJAX call.
 * @see createRequestActionTypes
 * @see createRequestActionCreators
 * @see createRequestReducer
 *
 * Ex.
 * const getEmployeeDuck = new RestDuck(execGetRequest,'employee',GET_EMPLOYEE_SERVER_URL);
 * // Action creators
 * export const fetchEmployee = getEmployeeDuck.actionCreators.execRequest;
 * // Reducer
 * export const dataReducer = combineReducers(
 *   ...,*   getEmployeeDuck.reducer,* }
 * // Selectors
 * export const getDataContext = state => state.default.dataContext;
 * export const getEmployeeData = getEmployeeDuck.selectors.getRequestData(getDataContext);
 * export const getEmployeeStarted = getEmployeeDuck.selectors.getRequestStarted(getDataContext);
 * ...
 */
class RestDuck {
  constructor(name,getApiContext) {
    this.restMethod = restMethod;
    this.name = name;
    this.path = path;
    this.getApiContext = getApiContext;
    this.$$duck = {}; // for class internal use
  }

  get actionTypes() {
    if (!this.$$duck.actionTypes) {
      this.$$duck.actionTypes = createRequestActionTypes(this.name,this.restMethod,this.path);
    }
    return this.$$duck.actionTypes;
  }

  get actionCreators() {
    if (!this.$$duck.actionCreators) {
      this.$$duck.actionCreators = createRequestActionCreators(this.restMethod,this.path,this.actionTypes);
    }
    return this.$$duck.actionCreators;
  }

  get reducer() {
    if (!this.$$duck.reducer) {
      this.$$duck.reducer = createRequestReducer(this.restMethod,this.name,this.actionTypes);
    }
    return this.$$duck.reducer;
  }

  get stateSelector() {
    return createSelector([this.getApiContext],restApiContext => restApiContext[this.name]);
  }
}

export default RestDuck;

数据/休息/ restMethods.jsx

import axios,{ CancelToken } from 'axios';

const openPreview = (data) => {
  if (window.navigator.msSaveOrOpenBlob) {
    window.navigator.msSaveOrOpenBlob(data);
  } else {
    window.open(URL.createObjectURL(data));
  }
};

const cancellable = (config) => {
  let cancel;
  const request = axios({
    ...config,cancelToken: new CancelToken((c) => { cancel = c; })
  });
  request.cancel = cancel;
  return request.catch(error => (axios.isCancel(error) ? Promise.reject(new Error(null)) : Promise.reject(error)));
};

const defaultHeaders = {
  'Cache-Control': 'no-cache',Pragma: 'no-cache',Expires: 0
};

const defaultPostHeaders = {
  'Content-Type': 'application/json'
};

export const get = (url,params,responseType = 'json') => cancellable({
  url,responseType,method: 'get',headers: {
    ...defaultHeaders
  }
});

export const post = (url,data,data: JSON.stringify(data),method: 'post',headers: {
    ...defaultHeaders,...defaultPostHeaders
  },cache: false
});

export const getBlob = (url,params) => get(url,'blob');

export const postBlob = (url,data) => post(url,'blob');

export const postAndOpenBlob = (url,data) => postBlob(url,data)
  .then((response) => {
    openPreview(response.data);
    return {
      ...response,data: 'blob opened as preview' // Don't waste memory by storing blob in state
    };
  });

我不知道在这个结构中如何放置以及如何模拟api调用.我正在考虑制作一个类似于这个one的模拟api,在那里我会模仿ajax调用并将它们存储在redux中,但是不知道如何在这种设置中执行此操作?

我尝试使用mockApi文件夹而不是使用restMethods来使用我将编写将解析mockData的promises的文件.这是我的尝试:

mockRestMethods

const employee = {
  name: 'Joe Doe'
}
const data = {
  employee 
};

export const get = item => new Promise((resolve) => {
  setTimeout(() => {
    resolve({ data: data[item] });
  },1000);
});

但是,如果我在RestDuck文件中的createRequestThunk函数内检查返回的内容作为response.data,我得到数据:undefined there.为什么,我做错了什么?

我可能有这个错误,但似乎你正在取代

export const get =(url,responseType =’json’)=>撤销({

with export const get = item =>新的Promise((resolve)=> {具有不同的API.

无论如何,您是否尝试在mock get函数中记录item的值.我猜它不是“员工”,这是数据中唯一的属性.

Yes,that was my goal,to replace the call that was pointing to the backend API,with the call where I would return the mock data. I have tried to log the value of the item,but I get undefined

好的,所以那里有很多抽象.我建议首先使用返回promise的版本替换get in data / rest / restMethods.jsx,让它工作,然后将其分解.这样你就不会立刻处理太多未知数.

ajax – 模拟api in react redux-thunk项目返回undefined的更多相关文章

  1. ios – React native链接到另一个应用程序

    如果是错误的,有人知道如何调用正确的吗?

  2. ios – React Native – 在异步操作后导航

    我正在使用ReactNative和Redux开发移动应用程序,我正面临着软件设计问题.我想调用RESTAPI进行登录,如果该操作成功,则导航到主视图.我正在使用redux和thunk所以我已经实现了异步操作,所以我的主要疑问是:我应该把逻辑导航到主视图?我可以直接从动作访问导航器对象并在那里执行导航吗?.我对组件中的逻辑没有信心.似乎不是一个好习惯.有没有其他方法可以做到这一点?

  3. 在ios中使用带有React Native(0.43.4)的cocoapods的正确方法是什么?

    我已经挖掘了很多帖子试图使用cocoapods为本地ios库设置一个反应原生项目,但我不可避免地在#import中找到了丢失文件的错误.我的AppDelegate.m文件中的语句.什么是使用反应原生的可可豆荚的正确方法?在这篇文章发表时,我目前的RN版本是0.43.4,而我正在使用Xcode8.2.1.这是我的过程,好奇我可能会出错:1)

  4. ios – React Native WebView滚动行为无法按预期工作

    如何确保滚动事件的行为与ReactNative应用程序中的浏览器相同?

  5. ios – React Native – BVLinearGradient – 找不到’React/RCTViewManager.h’文件

    谢谢.解决方法几天前我遇到了完全相同的问题.问题是在构建应用程序时React尚未链接.试试这个:转到Product=>Scheme=>管理方案…=>点击你的应用程序Scheme,然后点击Edit=>转到Build选项卡=>取消选中ParallelizeBuild然后点击标志添加目标=>搜索React,选择第一个名为React的目标,然后单击Add然后在目标列表中选择React并将其向上拖动到该列表中的第一个.然后转到Product=>再次清理并构建项目.这应该有所帮助.

  6. ios – React Native – NSNumber无法转换为NSString

    解决方法在你的fontWeight()函数中也许变成:

  7. ios – React native error – react-native-xcode.sh:line 45:react-native:command not found命令/ bin/sh失败,退出代码127

    尝试构建任何(新的或旧的)项目时出现此错误.我的节点是版本4.2.1,react-native是版本0.1.7.我看过其他有相同问题的人,所以我已经更新了本机的最新版本,但是我仍然无法通过xcode构建任何项目.解决方法要解决此问题,请使用以下步骤:>使用节点版本v4.2.1>cd进入[你的应用]/node_modules/react-native/packager>$sh./packager.s

  8. 反应原生 – 如何通过Xcode构建React Native iOS应用程序到设备?

    我试图将AwesomeProject应用程序构建到设备上.构建成功并启动屏幕显示,但后来我看到一个红色的“无法连接到开发服务器”屏幕.它表示“确保节点服务器正在运行–从Reactroot运行”npmstart“.看起来节点服务器已经运行,因为当我做npm启动时,我收到一个EADDRINUSE消息,表示该端口已经在使用.解决方法从设备访问开发服务器您可以使用开发服务器快速迭代设备.要做到这一点,你的

  9. 静音iOS推送通知与React Native应用程序在后台

    我有一个ReactNative应用程序,我试图获得一个发送到JavaScript处理程序的静默iOS推送通知.我看到的行为是AppDelegate中的didReceiveRemoteNotification函数被调用,但是我的JavaScript中的处理程序不会被调用,除非应用程序在前台,或者最近才被关闭.我很困惑的事情显然是应用程序正在被唤醒,并且它的didReceiveRemoteNotifi

  10. 如何为iOS的React Native设置分析

    所以我已经完成了一个针对iOS的ReactNative项目,但是我想在其中分析.我尝试了react-native-google-analytics软件包,但是问题阻止了它的正常工作.此外,react-native-cordova-plugin软件包只适用于Android,因此插入Cordova插件进行分析的能力现在已成为问题.我也没有Swift/ObjectiveC的经验,所以将完全失去GA的插入.有没有人有任何建议如何连接GoogleAnalytics的ReactNativeforiOS?

随机推荐

  1. xe-ajax-mock 前端虚拟服务

    最新版本见Github,点击查看历史版本基于XEAjax扩展的Mock虚拟服务插件;对于前后端分离的开发模式,ajax+mock使前端不再依赖后端接口开发效率更高。CDN使用script方式安装,XEAjaxMock会定义为全局变量生产环境请使用xe-ajax-mock.min.js,更小的压缩版本,可以带来更快的速度体验。

  2. vue 使用 xe-ajax

    安装完成后自动挂载在vue实例this.$ajaxCDN安装使用script方式安装,VXEAjax会定义为全局变量生产环境请使用vxe-ajax.min.js,更小的压缩版本,可以带来更快的速度体验。cdnjs获取最新版本点击浏览已发布的所有npm包源码unpkg获取最新版本点击浏览已发布的所有npm包源码AMD安装require.js安装示例ES6Module安装通过Vue.use()来全局安装示例./Home.vue

  3. AJAX POST数据中文乱码解决

    前端使用encodeURI进行编码后台java.net.URLDecoder进行解码编解码工具

  4. Koa2框架利用CORS完成跨域ajax请求

    实现跨域ajax请求的方式有很多,其中一个是利用CORS,而这个方法关键是在服务器端进行配置。本文仅对能够完成正常跨域ajax响应的,最基本的配置进行说明。这样OPTIONS请求就能够通过了。至此为止,相当于仅仅完成了预检,还没发送真正的请求呢。

  5. form提交时,ajax上传文件并更新到<input>中的value字段

  6. ajax的cache作用

    filePath="+escape;},error:{alert;}});解决方案:1.加cache:false2.url加随机数正常代码:网上高人解读:cache的作用就是第一次请求完毕之后,如果再次去请求,可以直接从缓存里面读取而不是再到服务器端读取。

  7. 浅谈ajax上传文件属性contentType = false

    默认值为contentType="application/x-www-form-urlencoded".在默认情况下,内容编码类型满足大多数情况。在这里,我们主要谈谈contentType=false.在使用ajax上传文件时:在其中先封装了一个formData对象,然后使用post方法将文件传给服务器。说到这,我们发现在JQueryajax()方法中我们使contentType=false,这不是冲突了吗?这就是因为当我们在form标签中设置了enctype=“multipart/form-data”,

  8. 909422229_ajaxFileUpload上传文件

    ajaxFileUpload.js很多同名的,因为做出来一个很容易。我上github搜AjaxFileUpload出来很多类似js。ajaxFileUpload是一个异步上传文件的jQuery插件传一个不知道什么版本的上来,以后不用到处找了。语法:$.ajaxFileUploadoptions参数说明:1、url上传处理程序地址。2,fileElementId需要上传的文件域的ID,即的ID。3,secureuri是否启用安全提交,默认为false。4,dataType服务器返回的数据类型。6,error

  9. AJAX-Cache:一款好用的Ajax缓存插件

    原文链接AJAX-Cache是什么Ajax是前端开发必不可少的数据获取手段,在频繁的异步请求业务中,我们往往需要利用“缓存”提升界面响应速度,减少网络资源占用。AJAX-Cache是一款jQuery缓存插件,可以为$.ajax()方法扩展缓存功能。

  10. jsf – Ajax update/render在已渲染属性的组件上不起作用

    我试图ajax更新一个有条件渲染的组件。我可以确保#{user}实际上是可用的。这是怎么引起的,我该如何解决呢?必须始终在ajax可以重新呈现之前呈现组件。Ajax正在使用JavaScriptdocument.getElementById()来查找需要更新的组件。但是如果JSF没有将组件放在第一位,那么JavaScript找不到要更新的内容。解决方案是简单地引用总是渲染的父组件。

返回
顶部