React-Redux: как убедиться, что все запросы на выборку успешно выполнены перед отправкой действия - PullRequest
1 голос
/ 22 апреля 2020

Я работаю над приложением React и использую Redux для хранения состояния. У меня есть следующий код:

category-arrow.component.jsx:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { increaseCategoryRank, decreaseCategoryRank, fetchCategoryRanks } from '../../redux/menu/menu.actions';
import './category-arrows.styles.scss';

class CategoryArrows extends Component {

    render() {

        const { category } = this.props;
        const categoryClicked = true;

        return (
            <div className="arrows-container">
                <div className="up-arrow" onClick={
                    () => {
                        this.props.increaseCategoryRank(category, categoryClicked)

                        this.props.fetchCategoryRanks(this.props.menu);    
                }}></div>
                <div className="category-rank">
                    <p>{category.rank}</p>
                </div>
                <div className="down-arrow" onClick={
                    () => {
                        this.props.decreaseCategoryRank(category, categoryClicked)

                        this.props.fetchCategoryRanks(this.props.menu);                   
                }}></div>
            </div>
        )
    }
}

const mapStateToProps = state => ({
    menu: state.menu
})

export default connect(mapStateToProps, { increaseCategoryRank, decreaseCategoryRank, fetchCategoryRanks } )(CategoryArrows);

menu.actions. js:

import { apiUrl, apiConfig } from '../../util/api';
import { INCREASE_CATEGORY_RANK, DECREASE_CATEGORY_RANK, FETCH_CATEGORY_RANKS } from './menu.types';

export const decreaseCategoryRank = (category, categoryClicked) => dispatch => {
    dispatch({ type: DECREASE_CATEGORY_RANK, category, categoryClicked })       
}

export const increaseCategoryRank = (category, categoryClicked)  => dispatch => {
    dispatch({ type: INCREASE_CATEGORY_RANK, category, categoryClicked })
}

export const fetchCategoryRanks = menu => async dispatch => {
    console.log("Printing menu (fetch category ranks)");
    console.log(menu);

    menu.map(async (category) => {

        console.log("PRINTING CATEGORY");
        console.log(category.name);
        console.log(category.rank);

        const options = {
            ...apiConfig(),
            method: 'PUT',
            body: JSON.stringify(category)
        }
        const response = await fetch(`${apiUrl}/category/${category._id}`, options)
        let data = await response.json()
        if (response.ok) {
            console.log("It got sent")
        } else {
            alert(data.error)
        }
    });

    dispatch({ type: FETCH_CATEGORY_RANKS, menu });

}

menu.types. js:

export const INCREASE_CATEGORY_RANK = "INCREASE_CATEGORY_RANK";
export const DECREASE_CATEGORY_RANK = "DECREASE_CATEGORY_RANK";
export const FETCH_CATEGORY_RANKS = "FETCH_CATEGORY_RANKS";

menu.reducer. js:

// import INITIAL_STATE from './menu.data';
import { INCREASE_CATEGORY_RANK, DECREASE_CATEGORY_RANK, FETCH_CATEGORY_RANKS } from './menu.types';

const INITIAL_STATE = []


export default (state = INITIAL_STATE, action) => {
    switch (action.type) {

        case INCREASE_CATEGORY_RANK: {
            console.log("Went into increase category rank");

            if(action.categoryClicked === false) {
                return state;
            }

            const menuArray = [...state];

            var index = menuArray.map(category => category._id).indexOf(action.category._id);

            //if it's the first element in array it won't move up
            if(index === 0) {
                return state;
            } else {
                const temp = menuArray[index];
                menuArray[index] = menuArray[index - 1];
                menuArray[index - 1] = temp;


                var newrank = 0;

                menuArray.forEach(category => {
                     category.rank = newrank++;
                });


                return menuArray;
            }

        }
        case DECREASE_CATEGORY_RANK: {
            console.log("Went into decrease category rank");

            if(action.categoryClicked === false) {
                return state;
            }

            const menuArray = [...state];

            console.log(menuArray);

            var index = menuArray.map(category => category._id).indexOf(action.category._id);

            //if it's the last element in the array, it won't move down
            if(index === menuArray.length - 1) {
                return state;
            } else {
                const temp = menuArray[index];
                menuArray[index] = menuArray[index + 1];
                menuArray[index + 1] = temp;


                var newrank = 0;

                menuArray.forEach(category => {
                    category.rank = newrank++;
                });


                return menuArray;
            }
        }
        case FETCH_CATEGORY_RANKS:
            return state;
        default:
            return state;
    }
}

В моем компоненте CategoryArrows при нажатии на элемент up-arrow div отправляется действие increaseCategoryRank, за которым следует действие fetchCategoryRanks. При нажатии down-arrow div отправляется действие decreaseCategoryRank, за которым следует действие fetchCategoryRanks.

Действия increaseCategoryRank и decreaseCategoryRank изменяют ранги категорий, находящихся в состоянии Redux. Действие fetchCategoryRanks выводит массив меню из состояния и отправляет данные об измененных категориях на сервер с помощью запросов на выборку.

Одна проблема, с которой я сталкиваюсь в моем приложении, заключается в том, что increaseCategoryRank или decreaseCategoryRank действия могут быть отправлены снова (путем нажатия на стрелку div) до того, как все запросы на выборку будут отправлены в fetchCategoryRanks. Это приводит к тому, что на сервер отправляется неверная информация, которая затем сохраняется в базе данных.

Я хочу, чтобы мой код не позволял повторно отправлять действие increaseCategoryRank/decreaseCategoryRank, если все запросы выборки не были успешно выполнены отправлено на сервер в действии fetchCategoryRanks.

Однако я не уверен, как проверить, были ли отправлены все запросы перед отправкой действий increaseCategoryRank/decreaseCategoryRank. Любые идеи приветствуются.

1 Ответ

1 голос
/ 22 апреля 2020

Может помочь добавление состояния «отложенные запросы», которое вы можете заблокировать.

menu.reducer.js - Обновить срез состояния, чтобы он содержал массив меню и флаг isPending, добавить новую категорию выборки, ранжировать успех, тип действия

const INITIAL_STATE = {
  menus: [],
  isPending: false // <-- add new pending flag to state slice
};

export default (state = INITIAL_STATE, action) => {
  switch (action.type) {
    case INCREASE_CATEGORY_RANK: {
      console.log("Went into increase category rank");

      if (action.categoryClicked === false) {
        return state;
      }

      const menuArray = [...state];

      var index = menuArray
        .map(category => category._id)
        .indexOf(action.category._id);

      //if it's the first element in array it won't move up
      if (index === 0) {
        return state;
      } else {
        const temp = menuArray[index];
        menuArray[index] = menuArray[index - 1];
        menuArray[index - 1] = temp;

        var newrank = 0;

        menuArray.forEach(category => {
          category.rank = newrank++;
        });

        return { ...state, menus: menuArray };
      }
    }
    case DECREASE_CATEGORY_RANK: {
      console.log("Went into decrease category rank");

      if (action.categoryClicked === false) {
        return state;
      }

      const menuArray = [...state];

      console.log(menuArray);

      var index = menuArray
        .map(category => category._id)
        .indexOf(action.category._id);

      //if it's the last element in the array, it won't move down
      if (index === menuArray.length - 1) {
        return state;
      } else {
        const temp = menuArray[index];
        menuArray[index] = menuArray[index + 1];
        menuArray[index + 1] = temp;

        var newrank = 0;

        menuArray.forEach(category => {
          category.rank = newrank++;
        });

        return { ...state, menus: menuArray };
      }
    }
    case FETCH_CATEGORY_RANKS:
      return { ...state, isPending: true }; // <-- set true when fetching ranks
    case FETCH_CATEGORY_RANKS_COMPLETE:
      return {
        ...state,
        isPending: false, // <-- clear isPending to unblock UI
        menus: [...state.menus, action.menu], // Wasn't sure about this menu the fetchCategoryRanks function dispatches when complete
      };
    default:
      return state;
  }
};

menu.actions.js - отправляет действие, когда выполняются запросы на выборку, и отправляет действие завершения выборки после завершения

export const fetchCategoryRanks = menu => async dispatch => {
  console.log("Printing menu (fetch category ranks)");
  console.log(menu);

  dispatch({ type: FETCH_CATEGORY_RANKS }); // <-- trigger isPending to block UI

  menu.map(async category => {
    console.log("PRINTING CATEGORY");
    console.log(category.name);
    console.log(category.rank);

    const options = {
      ...apiConfig(),
      method: "PUT",
      body: JSON.stringify(category)
    };
    const response = await fetch(`${apiUrl}/category/${category._id}`, options);
    let data = await response.json();
    if (response.ok) {
      console.log("It got sent");
    } else {
      alert(data.error);
    }
  });

  dispatch({ type: FETCH_CATEGORY_RANKS_COMPLETE, menu });
};

category-arrow.component.jsx - сопоставление isPending с реквизитами и проверкой перед отправкой лишних действий

class CategoryArrows extends Component {
  render() {
    const { category, isPending, menu } = this.props;
    const categoryClicked = true;

    return (
      <div className="arrows-container">
        <div
          className="up-arrow"
          onClick={() => {
            if (!isPending) {
              this.props.increaseCategoryRank(category, categoryClicked);
              this.props.fetchCategoryRanks(menu);
            }
          }}
        />
        <div className="category-rank">
          <p>{category.rank}</p>
        </div>
        <div
          className="down-arrow"
          onClick={() => {
            if (!isPending) {
              this.props.decreaseCategoryRank(category, categoryClicked);
              this.props.fetchCategoryRanks(menu);
            }
          }}
        />
      </div>
    );
  }
}

const mapStateToProps = state => ({
  isPending: state.menu.isPending,
  menu: state.menu.menus,
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...