简体   繁体   English

redux-api-middleware如何将数据添加到商店?

[英]How does redux-api-middleware add data to the store?

Background 背景

I am creating a universal react application with redux and react router. 我正在用redux和react router创建一个通用的react应用程序。 I have most of the application setup with server-side rendering and basic redux actions (modifying a Boolean in the store). 我的大多数应用程序设置都带有服务器端渲染和基本的redux操作(在商店中修改布尔值)。 Now I would like to make some api calls to get data for my app. 现在,我想进行一些api调用以获取我的应用程序的数据。

Current Implementation 当前实施

I thought it would be a good idea to use the redux-api-middleware but I can't get the data to add to the store. 我认为使用redux-api-middleware是一个好主意,但是我无法将数据添加到存储中。 I followed the documentation and created an action that looks like this. 我按照文档进行操作,并创建了一个类似于以下的操作。

example-action.js 例如-action.js

import { CALL_API } from `redux-api-middleware`;


export function fn_get_data () {
  [CALL_API]: {
    endpoint: 'http://www.example.com/api/users',
    method: 'GET',
    types: ['REQUEST', 'SUCCESS', 'FAILURE']
  }
}

I run this action on a button click in my page. 我在页面上单击一个按钮时执行此操作。 I see the action get fired but nothing goes into the store. 我看到该动作被解雇了,但没有任何东西进入商店。 I even added some custom code for the SUCCESS action and was able to console.log() the response but still could not get the data into the store. 我什至为SUCCESS操作添加了一些自定义代码,并且能够console.log()响应,但是仍然无法将数据放入存储中。

Also I have added the middleware to the store in the same way the documentation says to. 另外,我已经按照文档说明的方式将中间件添加到了商店中。

configureStore.js configureStore.js

import { createStore, applyMiddleware, combineReducers } from 'redux';
import { apiMiddleware } from 'redux-api-middleware';
import reducers from './reducers';

const reducer = combineReducers(reducers);
const createStoreWithMiddleware = applyMiddleware(apiMiddleware)(createStore);

export default function configureStore(initialState) {
  return createStoreWithMiddleware(reducer, initialState);
}

Tried so far 到目前为止尝试过

So far I have tried a bunch of different things with the action, like making the actions into exportable symbols and calling them in a reducer and trying to merge the data into the current state that comes from the payload attribute in redux-api-middleware, but no luck. 到目前为止,我已经对该操作进行了很多尝试,例如将这些操作制作成可导出的符号并在化简器中调用它们,然后尝试将数据合并到来自redux-api-middleware中的payload属性的当前状态,但没有运气。

Question

I really have 2 questions 我真的有两个问题

  • Why are there no reducers in the documentation, is this just overlooked or does the data from the response just go directly into the store? 为什么文档中没有reduces,这只是被忽略还是来自响应的数据直接进入了商店?
  • Why doesn't the data I call get added to the store (what am I missing)? 为什么我调用的数据没有添加到商店中(我丢失了什么)?

Any help, explanation or constructive criticism on the matter is really appreciated, thanks. 非常感谢您对此事的任何帮助,解释或建设性批评。

As far as I can see, redux-api-middleware provides a bunch of facilities for making an API call, dispatching success/failure actions, and possibly processing the response a bit. 据我所知, redux-api-middleware提供了许多用于进行API调用,调度成功/失败操作以及可能稍微处理响应的工具。 However, how you handle those actions in your reducers is up to you. 但是,如何处理减速器中的这些动作取决于您自己。 So, if you ask the middleware to dispatch "MY_REQUEST_SUCCESS" , you'd need to add a "MY_REQUEST_SUCCESS" handler into your reducer logic, and update the state appropriately. 因此,如果您要求中间件调度"MY_REQUEST_SUCCESS" ,则需要在您的reducer逻辑中添加"MY_REQUEST_SUCCESS"处理函数,并适当地更新状态。

Solution

Like markerikson said, the library only provides you with the tools, you still have to write reduceres to respond to the action. 就像markerikson所说的那样,该库只为您提供工具,您仍然必须编写reduceres来响应操作。 In my case I finally got data with the following reducer. 在我的情况下,我终于通过以下减速器获得了数据。

example-reducer.js 例如-reducer.js

import * as ExampleActionType from "../action/example-action";
import Immutable from "immutable";

let defaultState = Immutable.fromJS({
  fakeData: {}
});

function exampleState (state = defaultState, action) {
  switch (action.type) {

    case ExampleActionType.REQUEST : {
        console.log(action);
        return state;
    }

    case ExampleActionType.SUCCESS : {
        console.log(action);
        return state.merge({fakeData: action.payload });
    }

    case ExampleActionType.FAILURE : {
        console.log(action);
        return state;
    }

    default:
        return state;
  }
}

I also had to export the symbols like this 我也不得不导出这样的符号

example-action.js 例如-action.js

export const REQUEST = Symbol('REQUEST');
export const SUCCESS = Symbol('SUCCESS');
export const FAILURE = Symbol('FAILURE');

Conclusion 结论

Awesome library that gives you all the tools you need to creat api requests with very little code. 很棒的库为您提供了用很少的代码创建api请求所需的所有工具。 Hope this helps someone who is confused about this like I was. 希望这可以帮助像我一样对此感到困惑的人。

redux-api-middleware isn't meant to store data to the store (that's why you don't need to setup any reducers). redux-api-middleware并不意味着将数据存储到商店(这就是为什么您不需要设置任何reducer的原因)。 I actually built a library redux-cached-api-middleware that acts as a thin layer on top of redux-api-middleware and adds caching (can easily be used as a simple storing) capabilities. 我实际上构建了一个库redux-cached-api-middleware ,该库充当redux-api-middleware之上的薄层,并添加了缓存(可以轻松地用作简单存储)功能。

Here's an example component: 这是一个示例组件:

import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import api from 'redux-cached-api-middleware';
import Items from './Items';
import Error from './Error';

class ExampleApp extends React.Component {
  componentDidMount() {
    this.props.fetchData();
  }

  render() {
    const { result } = this.props;
    if (!result) return null;
    if (result.fetching) return <div>Loading...</div>;
    if (result.error) return <Error data={result.errorPayload} />;
    if (result.successPayload) return <Items data={result.successPayload} />;
    return <div>No items</div>;
  }
}

ExampleApp.propTypes = {
  fetchData: PropTypes.func.isRequired,
  result: PropTypes.shape({}),
};

const CACHE_KEY = 'GET/items';

const enhance = connect(
  state => ({
    result: api.selectors.getResult(state, CACHE_KEY),
  }),
  dispatch => ({
    fetchData() {
      return dispatch(
        api.actions.invoke({
          method: 'GET',
          headers: { Accept: 'application/json' },
          endpoint: 'https://my-api.com/items/',
          cache: {
            key: CACHE_KEY,
            strategy: api.cache
              .get(api.constants.CACHE_TYPES.TTL_SUCCESS)
              .buildStrategy({ ttl: 10 * 60 * 1000 }), // 10 minutes
          },
        })
      );
    },
  })
);

export default enhance(ExampleApp);

This work in cached manner, but you could easily pass custom shouldFetch function, and you would always refetch from API: 这项工作以缓存方式进行,但是您可以轻松地传递自定义的shouldFetch函数,并且始终可以从API进行重新提取:

const enhance = connect(
  state => ({
    result: api.selectors.getResult(state, CACHE_KEY),
  }),
  dispatch => ({
    fetchData() {
      return dispatch(
        api.actions.invoke({
          method: 'GET',
          headers: { Accept: 'application/json' },
          endpoint: 'https://my-api.com/items/',
          cache: {
            key: CACHE_KEY,
            shouldFetch: () => true
          },
        })
      );
    },
  })
);

And the setup is as follows (notice the api reducer, that actually handles storing responses to redux state): 设置如下(注意api reducer,它实际上处理存储对redux状态的响应):

import { createStore, combineReducers, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { apiMiddleware } from 'redux-api-middleware';
import api from 'redux-cached-api-middleware';
import reducers from './reducers';

const store = createStore(
  combineReducers({
    ...reducers,
    [api.constants.NAME]: api.reducer,
  }),
  applyMiddleware(thunk, apiMiddleware)
);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM