Как создать более короткую функцию редуктора промежуточного программного обеспечения, которая может обрабатывать несколько типов действий? - PullRequest
0 голосов
/ 10 марта 2019

Я использовал промежуточное программное обеспечение redux-обещание-посредник с реакцией и избыточностью для создания приложения, имеющего несколько кнопок (каждая будет иметь свой собственный уникальный запрос ajax, который соответствующим образом обновит состояние)

Я начал делать dataReducer для трех из множества кнопок, которые я делаю. В dataReducer уже более 100 строк, и очень утомительно создавать этот редуктор для оставшихся кнопок.

Существует ли более эффективный способ создания этой функции 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;
  }
};

Полный Код ( Код Песочница Здесь )

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")
);

Ответы [ 2 ]

0 голосов
/ 11 марта 2019

Конечно - я бы создал вспомогательную функцию для удаления некоторого дублирования в редукторе, например:

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

Тогда вы можете использовать его так:

    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);
          ...

Однако кажется, что многие из ваших действий редуктора не являются необходимыми и могут быть объединены - например, два вышеуказанных действия могут легко стать одним:

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

Возможно, вы можете дополнительно удалить отдельные действия с помощью простого "UPDATE_URL"action.

например,

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

, и в вашем файле действий вы можете использовать общее updateUrlAction, например:

const updateUrlaction = (url, error, success, loading, payload) => dispatch => {
  dispatch( {
    type: 'UPDATE_URL',
    url, error, success, loading, payload }
}
0 голосов
/ 11 марта 2019

Большая часть вашего кода может быть сведена к одному повторно используемому компоненту, который обновляет свой собственный локальный state в соответствии с успехом или неудачей запроса ajax. Слишком часто разработчики бегут к redux, не понимая почему. В этом случае / примере вам это не нужно. Однако, если ваше приложение было сильно вложено и разделено на несколько родительских компонентов, тогда redux может быть приемлемым вариантом. В любом случае, он все еще может быть уменьшен до одного повторно используемого компонента.

Рабочий пример : https://codesandbox.io/s/0x0478x4wp


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"));

компоненты / 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)}
        
); } StockButton.propTypes = { символ: PropTypes.string.isRequired }; экспорт по умолчанию StockButton;

компоненты / Заполнитель

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;
...