简体   繁体   English

如何创建一个较短的redux-promise-middleware reducer函数,可以处理多种操作类型?

[英]How to create a shorter redux-promise-middleware reducer function that can handle multiple action types?

I've utilized redux-promise-middleware with react and redux to build an application that has several buttons (each will have its own unique ajax request that will update the state accordingly) 我已经将redux-promise-middleware与react和redux结合使用,以构建具有多个按钮的应用程序(每个按钮都有其自己的唯一ajax请求,并将相应地更新状态)

I started to make the dataReducer for three of the many buttons I'm making. 我开始为正在制作的许多按钮中的三个制作dataReducer。 dataReducer is already over 100 lines and its also very tedious to make this reducer for the remainder of the buttons. dataReducer已经超过100行,因此在其余按钮上制作该reducer也非常繁琐。

Is there a more efficient way of creating this dataReducer function and shortening while ensuring that the dataReducer function remains pure 有没有更有效的方法来创建此dataReducer函数并进行缩短, 同时确保dataReducer函数保持纯净

const dataReducer = (state = dataInitialState, action) => {
  console.log({ action_type: action.type });
  switch (action.type) {
    case "UPDATE_URL_ONE_PENDING":
      return {
        ...state,
        url_one: {
          ...state["url_one"],
          error: false,
          success: false,
          loading: true
        }
      };
    case "UPDATE_URL_ONE_FULFILLED":
      console.log({ payload: action["payload"] });
      return {
        ...state,
        url_one: {
          ...state["url_one"],
          error: false,
          success: true,
          loading: false,
          payload: action["payload"]
        }
      };
    case "UPDATE_URL_ONE_REJECTED":
      return {
        ...state,
        url_one: {
          ...state["url_one"],
          error: true,
          success: false,
          loading: false
        }
      };
    case "UPDATE_URL_TWO_PENDING":
      return {
        ...state,
        url_two: {
          ...state["url_two"],
          error: false,
          success: false,
          loading: true
        }
      };
    case "UPDATE_URL_TWO_FULFILLED":
      return {
        ...state,
        url_two: {
          ...state["url_two"],
          error: false,
          success: true,
          loading: false,
          payload: action["payload"]
        }
      };
    case "UPDATE_URL_TWO_REJECTED":
      return {
        ...state,
        url_two: {
          ...state["url_two"],
          error: true,
          success: false,
          loading: false
        }
      };
    case "UPDATE_URL_THREE_PENDING":
      return {
        ...state,
        url_three: {
          ...state["url_three"],
          error: false,
          success: false,
          loading: true
        }
      };
    case "UPDATE_URL_THREE_FULFILLED":
      return {
        ...state,
        url_three: {
          ...state["url_three"],
          error: false,
          success: true,
          loading: false,
          payload: action["payload"]
        }
      };
    case "UPDATE_URL_THREE_REJECTED":
      return {
        ...state,
        url_three: {
          ...state["url_three"],
          error: true,
          success: false,
          loading: false
        }
      };
    default:
      return state;
  }
};

Full Code ( Code Sandbox Here ) 完整代码( 此处为代码沙箱

import React from "react";
import ReactDOM from "react-dom";
import { Provider, connect } from "react-redux";
import { createStore, applyMiddleware, combineReducers } from "redux";
import thunk from "redux-thunk";
import promise from "redux-promise-middleware";

//Action Creator
function updateData(url, type) {
  console.log({ url, type });
  return dispatch => {
    dispatch({
      type: type,
      payload: $.ajax({
        type: "GET",
        url: url,
        dataType: "json",
        async: false
      })
    });
  };
}

//App Component
class App extends React.Component {
  render() {
    return (
      <div>
        <button
          onClick={() => {
            console.log({ props: this.props });
          }}
        >
          Check Props
        </button>
        <button
          onClick={() => {
            this.props.updateData(
              "https://api.iextrading.com/1.0/stock/market/batch?symbols=TSLA&types=quote,stats,news,chart&range=1m&last=5",
              "UPDATE_URL_ONE"
            );
          }}
        >
          UPDATE URL ONE
        </button>
        <button
          onClick={() => {
            this.props.updateData(
              "https://api.iextrading.com/1.0/stock/market/batch?symbols=GE&types=quote,stats,news,chart&range=1m&last=5",
              "UPDATE_URL_TWO"
            );
          }}
        >
          UPDATE_URL_TWO
        </button>
        <button
          onClick={() => {
            this.props.updateData(
              "https://api.iextrading.com/1.0/stock/market/batch?symbols=PZZA&types=quote,stats,news,chart&range=1m&last=5",
              "UPDATE_URL_THREE"
            );
          }}
        >
          UPDATE_URL_THREE
        </button>
        <button
          onClick={() => {
            this.props.updateData(
              "https://api.iextrading.com/1.0/stock/market/batch?symbols=PZZA&types=quote,stats,news,chart&range=1m&last=5",
              "UPDATE_URL_FOUR"
            );
          }}
        >
          UPDATE_URL_FOUR
        </button>
        <button
          onClick={() => {
            this.props.updateData(
              "https://api.iextrading.com/1.0/stock/market/batch?symbols=PZZA&types=quote,stats,news,chart&range=1m&last=5",
              "UPDATE_URL_FIVE"
            );
          }}
        >
          UPDATE_URL_FIVE
        </button>
        <button
          onClick={() => {
            this.props.updateData(
              "https://api.iextrading.com/1.0/stock/market/batch?symbols=PZZA&types=quote,stats,news,chart&range=1m&last=5",
              "UPDATE_URL_SIX"
            );
          }}
        >
          UPDATE_URL_SIX
        </button>
        <button
          onClick={() => {
            this.props.updateData(
              "https://api.iextrading.com/1.0/stock/market/batch?symbols=PZZA&types=quote,stats,news,chart&range=1m&last=5",
              "UPDATE_URL_SEVEN"
            );
          }}
        >
          UPDATE_URL_SEVEN
        </button>
        <button
          onClick={() => {
            this.props.updateData(
              "https://api.iextrading.com/1.0/stock/market/batch?symbols=PZZA&types=quote,stats,news,chart&range=1m&last=5",
              "UPDATE_URL_EIGHT"
            );
          }}
        >
          UPDATE_URL_EIGHT
        </button>
        <button
          onClick={() => {
            this.props.updateData(
              "https://api.iextrading.com/1.0/stock/market/batch?symbols=PZZA&types=quote,stats,news,chart&range=1m&last=5",
              "UPDATE_URL_NINE"
            );
          }}
        >
          UPDATE_URL_NINE
        </button>
        <button
          onClick={() => {
            this.props.updateData(
              "https://api.iextrading.com/1.0/stock/market/batch?symbols=PZZA&types=quote,stats,news,chart&range=1m&last=5",
              "UPDATE_URL_TEN"
            );
          }}
        >
          UPDATE_URL_TEN
        </button>
      </div>
    );
  }
}

const mapStateToProps = state => state;
const mapDispatchToProps = dispatch => {
  return {
    updateData: (data, type) => {
      dispatch(updateData(data, type));
    }
  };
};

const AppEnhanced = connect(
  mapStateToProps,
  mapDispatchToProps
)(App);

//reducer below that will determine how the state updates based on the action reducer above

const dataInitialState = {
  url_one: { error: false, success: false, loading: true, payload: [] },
  url_two: { error: false, success: false, loading: true, payload: [] },
  url_three: { error: false, success: false, loading: true, payload: [] },
  url_four: { error: false, success: false, loading: true, payload: [] },
  url_five: { error: false, success: false, loading: true, payload: [] },
  url_size: { error: false, success: false, loading: true, payload: [] },
  url_seven: { error: false, success: false, loading: true, payload: [] },
  url_eight: { error: false, success: false, loading: true, payload: [] },
  url_nine: { error: false, success: false, loading: true, payload: [] },
  url_ten: { error: false, success: false, loading: true, payload: [] }
};

const dataReducer = (state = dataInitialState, action) => {
  console.log({ action_type: action.type });
  switch (action.type) {
    case "UPDATE_URL_ONE_PENDING":
      return {
        ...state,
        url_one: {
          ...state["url_one"],
          error: false,
          success: false,
          loading: true
        }
      };
    case "UPDATE_URL_ONE_FULFILLED":
      console.log({ payload: action["payload"] });
      return {
        ...state,
        url_one: {
          ...state["url_one"],
          error: false,
          success: true,
          loading: false,
          payload: action["payload"]
        }
      };
    case "UPDATE_URL_ONE_REJECTED":
      return {
        ...state,
        url_one: {
          ...state["url_one"],
          error: true,
          success: false,
          loading: false
        }
      };
    case "UPDATE_URL_TWO_PENDING":
      return {
        ...state,
        url_two: {
          ...state["url_two"],
          error: false,
          success: false,
          loading: true
        }
      };
    case "UPDATE_URL_TWO_FULFILLED":
      return {
        ...state,
        url_two: {
          ...state["url_two"],
          error: false,
          success: true,
          loading: false,
          payload: action["payload"]
        }
      };
    case "UPDATE_URL_TWO_REJECTED":
      return {
        ...state,
        url_two: {
          ...state["url_two"],
          error: true,
          success: false,
          loading: false
        }
      };
    case "UPDATE_URL_THREE_PENDING":
      return {
        ...state,
        url_three: {
          ...state["url_three"],
          error: false,
          success: false,
          loading: true
        }
      };
    case "UPDATE_URL_THREE_FULFILLED":
      return {
        ...state,
        url_three: {
          ...state["url_three"],
          error: false,
          success: true,
          loading: false,
          payload: action["payload"]
        }
      };
    case "UPDATE_URL_THREE_REJECTED":
      return {
        ...state,
        url_three: {
          ...state["url_three"],
          error: true,
          success: false,
          loading: false
        }
      };
    default:
      return state;
  }
};

const reducers = combineReducers({
  data: dataReducer
});
const store = createStore(reducers, applyMiddleware(thunk, promise));

ReactDOM.render(
  <Provider store={store}>
    <AppEnhanced />
  </Provider>,
  document.getElementById("root")
);

A lot of your code can be reduced to a single reusable component that updates its own local state according to the success or failure of the ajax request. 您可以将许多代码简化为一个可重用的组件,该组件根据ajax请求的成功或失败来更新其自身的本地state Too often developers run to redux without understanding why. 开发人员经常在不了解原因的情况下运行redux In this case/example, you don't need it. 在这种情况/示例中,您不需要它。 However, if your application was heavily nested and split across multiple parent components, then redux could be a viable option. 但是,如果您的应用程序被大量嵌套并拆分为多个父组件,那么redux可能是一个可行的选择。 Either way, it can still be reduced to a single reusable component. 无论哪种方式,它仍然可以简化为单个可重用组件。

Working example : https://codesandbox.io/s/0x0478x4wp 工作示例https : //codesandbox.io/s/0x0478x4wp


index.js index.js

import React, { Fragment } from "react";
import { render } from "react-dom";
import StockButton from "./components/StockButton";

const App = () => (
  <Fragment>
    <StockButton symbol="PZZA" />
    <StockButton symbol="AAPL" />
    <StockButton symbol="MSFT" />
  </Fragment>
);

render(<App />, document.getElementById("root"));

components/StockButton 组件/ StockButton

import React, { Component } from "react";
import PropTypes from "prop-types";
import axios from "axios";
import Placeholder from "../Placeholder";

const initialState = {
  error: false,
  success: false,
  loading: false,
  payload: []
};

class StockButton extends Component {
  state = { ...initialState };

  componentDidMount = () => this.fetchData();

  fetchData = () => {
    this.setState({ loading: true }, () => {
      const { symbol } = this.props;
      const URL = `https://api.iextrading.com/1.0/stock/market/batch?symbols=${symbol}&types=quote,stats,news,chart&range=1m&last=5`;
      axios
        .get(URL)
        .then(({ data }) => this.setState({ loading: false, payload: data }))
        .catch(error => this.setState({ ...initialState, error }));
    });
  };

  render = () =>
    this.state.error ? (
      <p>Error loading data!</p>
    ) : this.state.loading ? (
      <Placeholder />
    ) : (
      <div>
        <button onClick={this.fetchData}>Update {this.props.symbol}</button>
        <pre style={{ width: 500, height: 300, overflowY: "auto" }}>
          <code>{JSON.stringify(this.state.payload, null, 4)}</code>
        </pre>
      </div>
    );
}

StockButton.propTypes = {
  symbol: PropTypes.string.isRequired
};

export default StockButton;

components/Placeholder 组件/占位符

import React, { Fragment } from "react";

const Placeholder = () => (
  <Fragment>
    <button style={{ width: 95, height: 24 }}>Loading</button>
    <pre style={{ width: 500, minHeight: 300, overflowY: "scroll" }} />
  </Fragment>
);

export default Placeholder;

Sure - I would create a helper function to remove some of the duplication in the reducer like this: 当然-我将创建一个辅助函数来删除化简器中的某些重复项,如下所示:

   const updateUrl = (state, urlKey, error, success, loading, payload) => {
      return {
        ...state,
        [urlKey]: { // 'computed key'
          ...state[urlKey],
          error, success, loading, payload
        }
    }

Then you can use it like this: 然后你可以像这样使用它:

    const dataReducer = (state = dataInitialState, action) => {
      console.log({ action_type: action.type });
      switch (action.type) {
        case "UPDATE_URL_ONE_PENDING":
          // call our helper func
          return updateUrl(state, "url_one", false, false, true);
        case "UPDATE_URL_ONE_FULFILLED":
          console.log({ payload: action["payload"] });
          return updateUrl(state, "url_one", false, true, false, action.payload);
          ...

However it seems like many of your reducer actions are unnecessary and could be merged - eg the two actions above could easily become one: 但是,您的许多reducer动作似乎是不必要的,可以合并-例如,上面的两个动作很容易变成一个:

case "UPDATE_URL_ONE":
  // call our helper func
  return updateUrl(state, "url_one", action.error, action.success, action.loading);

You can probably further remove the individual actions with a simple "UPDATE_URL" action. 您可能可以通过简单的“ UPDATE_URL”操作进一步删除各个操作。

eg 例如

case "UPDATE_URL":
  // call our helper func
  return updateUrl(state, action.url, action.error, action.success, action.loading, action.payload);

and in your actions file, you could have a generic updateUrlAction like so: 在您的操作文件中,您可以有一个通用的updateUrlAction,如下所示:

const updateUrlaction = (url, error, success, loading, payload) => dispatch => {
  dispatch( {
    type: 'UPDATE_URL',
    url, error, success, loading, payload }
}

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

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