首页 > 解决方案 > redux-toolkit 中的多次调度问题

问题描述

我需要使用 redux-toolkit 一个一个(不是并行)运行 call 3 dispatch。在调试期间它工作正常,但在运行时它给了我一个奇怪的错误。

代码:

dispatch(pointUpdateAll(items));
        // dispatch(pathUpdateOne({ id: path_id, changes: { wps: wps } }));
        dispatch(
          pathUpdateOne({
            id: path_id,
            // changes: { wps: wps, sumdist: new_sum_dist },
            changes: { wps: wps, sumdist: new_sum_dist },
          })
        );
        dispatch(pointRemove(point_id));

错误:

Error: Rendered fewer hooks than expected. This may be caused by an accidental early return statement. EDIT:

整个组件:

import * as Cesium from "cesium";
import { DrawSetting } from "../../../Cesium/DrawConfig";
import React, { useEffect, useContext } from "react";
import { Entity, PointGraphics } from "resium";
import { useSelector, useDispatch, ReactReduxContext } from "react-redux";
import { updateDist } from "./pathutil";
import {
  wpsSelectors,
  pointUpdateOne,
  pointUpdateAll,
  pointRemove,
  selectPointByPathIDAndOrder,
} from "../../../../../store/slices/wayPoints";
import {
  pathUpdateOne,
  pathesSelectors,
} from "../../../../../store/slices/pathess";
import {
  TogglepathPanel,
  SetEditing_path_id,
  SetEditable_entity_id,
  setFinishEditing,
  setEntityNewPosiotion,
} from "../../../../../store/slices/config";
//TODO: remove first point bug
function PointItem({ point_id }) {
  const { store } = useContext(ReactReduxContext);

  const dispatch = useDispatch();

  const point = useSelector((state) =>
    wpsSelectors.selectById(state, point_id)
  );

  const prev_point = selectPointByPathIDAndOrder(
    point.path_id,
    point.order - 1
  )(store.getState())[0];

  const next_point = selectPointByPathIDAndOrder(
    point.path_id,
    point.order + 1
  )(store.getState())[0];
  // const viewer = useSelector((state) => state.config.cViewer);

  const all_points_ids = useSelector((state) => wpsSelectors.selectIds(state));
  const ShowPathPanel = useSelector((state) => state.config.ShowPathPanel);
  const path = useSelector((state) =>
    pathesSelectors.selectById(state, point.path_id)
  );
  const drawingMode = useSelector((state) => state.config.drawingMode);

  // let dragging = undefined;
  const editable_entity_id = useSelector(
    (state) => state.config.editable_entity_id
  );
  const is_finish_editing = useSelector((state) => state.config.finish_editing);

  // const dragging_id = useSelector((state) => state.config.dragging_id);
  const newPosition = useSelector((state) => state.config.entity_new_posiotion);
  useEffect(() => {
    debugger;
    console.log("point changed");
    if (point.is_focused) {
      dispatch(TogglepathPanel(1));
      var ele = document.getElementById("wp" + point.order);
      if (
        document.getElementsByClassName("pathrow").length > 0 &&
        ele !== undefined
      ) {
        document.getElementsByClassName("pathrow")[0].scrollTo({
          top: ele.offsetTop,
          left: ele.offsetLeft,
          behavior: "smooth",
        });
      }
      if (
        is_finish_editing &&
        editable_entity_id === "path_" + point.path_id + "point_" + point_id
      ) {
        _updateDist(newPosition[0], newPosition[1], newPosition[2]);

        dispatch(setEntityNewPosiotion(undefined));
        dispatch(setFinishEditing(undefined));
        dispatch(SetEditable_entity_id(undefined));
      }
    }
  }, [point, is_finish_editing]);

  const _updateDist = (lat, lon, alt) => {
    //update dist  // update before point distance and path sumdist
    let out = updateDist(path, lat, lon, alt, point, next_point, prev_point);
    let items = out[0];
    let new_sum_dist = out[1];
    dispatch(pointUpdateAll(items));
    dispatch(
      pathUpdateOne({
        id: path.id,
        changes: { sumdist: new_sum_dist },
      })
    );
    dispatch(
      pointUpdateOne({
        id: point.id,
        changes: { is_focused: false, is_moving: false },
      })
    );
  };

  //do focus
  const handleOnClick = (pid) => {
    if (drawingMode === undefined) {
      dispatch(setEntityNewPosiotion(undefined));
      dispatch(setFinishEditing(undefined));
      dispatch(SetEditable_entity_id(undefined));
      console.log("click handler point called");

      // if (ShowPathPanel === 0) {
      //   //open panel
      //   dispatch(TogglepathPanel(1));
      // }
      //

      dispatch(SetEditing_path_id(point.path_id));

      if (ShowPathPanel === 0) {
        //open panel
        dispatch(TogglepathPanel(1));
      }

      // defocus all
      let items = [];
      for (let i = 0; i < all_points_ids.length; i++) {
        let item = undefined;
        if (all_points_ids[i] !== pid) {
          item = {
            id: all_points_ids[i],
            changes: { is_focused: false },
          };
        } else {
          //  focus one and enable move
          item = {
            id: pid,
            changes: { is_focused: true, is_moving: true },
          };
        }
        items.push(item);
      }

      dispatch(pointUpdateAll(items));

      //tell  the app which entity is movable
      dispatch(
        SetEditable_entity_id("path_" + point.path_id + "point_" + point_id)
      );
    }
  };

  //delete point
  const handleOnRClick = (path_id, point_id) => {
    // const wps = path.wps.slice();
    let wps = path.wps.slice();

    if (wps.length > 2) {
      const index = wps.indexOf(point_id);
      let after_points_ids = wps.slice(index + 1, wps.length);
      if (index > -1) {
        wps.splice(index, 1);
      }
      let order = point.order;
      let items = [];

      if (after_points_ids.length > 0) {
        for (let i = 0; i < after_points_ids.length; i++) {
          items.push({ id: after_points_ids[i], changes: { order: order } });
          order = order + 1;
        }
      }

      // update before point distance and path sumdist
      let new_sum_dist = 0;
      let entitys_id_to_remove = [];
      switch (index) {
        // first point of path
        case 0:
          new_sum_dist = path.sumdist - point.dist;
          entitys_id_to_remove.push("line" + point_id + "_" + next_point.id);

          break;

        // last point of path
        case path.wps.length - 1:
          entitys_id_to_remove.push("line" + prev_point.id + "_" + point_id);
          new_sum_dist = path.sumdist - prev_point.dist;
          items.push({ id: prev_point.id, changes: { dist: 0 } });
          break;

        //between two points
        default:
          let new_prev_point_dist = Cesium.Cartesian3.distance(
            Cesium.Cartesian3.fromDegrees(
              next_point.long,
              next_point.lat,
              next_point.alt
            ),
            Cesium.Cartesian3.fromDegrees(
              prev_point.long,
              prev_point.lat,
              prev_point.alt
            )
          );

          items.push({
            id: prev_point.id,
            changes: { dist: new_prev_point_dist },
          });
          new_sum_dist =
            path.sumdist - point.dist - prev_point.dist + new_prev_point_dist;
          break;
      }

      if (items.length > 0) {
        debugger;

        // dispatch(pathUpdateOne({ id: path_id, changes: { wps: wps } }));

        dispatch(pointUpdateAll(items));

        // TODO: should find another approach to update path after delete
        dispatch(
          pathUpdateOne({
            id: path_id,
            // changes: { wps: wps, sumdist: new_sum_dist },
            changes: { wps: wps, sumdist: new_sum_dist },
          })
        );
        dispatch(pointRemove(point_id));

        // dispatch(pointUpdateAll(items));
        //
      }
    }
  };

  return (
    <div>
      <Entity
        // show={point.is_user_made}

        selected={point.is_focused}
        id={"path_" + point.path_id + "point_" + point_id}
        name={"path_" + point.path_id + "point_" + point_id}
        description={"path_" + point.path_id + "point_" + point_id}
        position={Cesium.Cartesian3.fromDegrees(
          parseFloat(point["long"]),
          parseFloat(point["lat"]),
          parseFloat(point["alt"])
        )}
        onClick={() => handleOnClick(point_id)}
        onRightClick={(e) =>
          window.confirm("آیا از حذف این نقطه مطمین هستید؟") &&
          handleOnRClick(point.path_id, point_id)
        }
      >
        <PointGraphics
          // show={point.is_user_made}
          color={DrawSetting["path_point_Color"]}
          pixelSize={DrawSetting["path_point_size"]}
          outlineColor={DrawSetting["path_point_outlineColor"]}
          outlineWidth={DrawSetting["path_point_outlineWidth"]}
        />
      </Entity>
    </div>
  );
}
export default PointItem;

不知道够不够。

标签: reactjsreduxreact-redux

解决方案


这里有很多东西要解压。虽然我不确定它是否能解决您的问题,但我想提供一些反馈。

通常,新状态的计算不应该发生在您的组件中,而应该发生在您的 reducer 中。(Redux 风格指南:Put as Much Logic as possible in Reducers)您通常也不应该分派多个(避免顺序分派许多动作)“setter 风格”分派调用,而应该只分派一个“事件类型”分派调用。(将动作建模为事件,而不是设置器)。连续分派 3 次可能会导致组件重新渲染 3 次,中间会出现意想不到的状态组合。

这将从该组件中移出大量代码,使其更具可读性,并可能使查找错误源变得更加明显(如果与在不一致状态下渲染有关,甚至可以消除它)


推荐阅读