0

I need to run call 3 dispatch one by one (not parallel) with redux-toolkit. During debugging it works fine, but in runtime it gives me a weird error.

Code:

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:

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

The whole component:

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;

I don't know if it's enough or not.

4
  • That's a lot of code. Hard to guess where the problem is located. It sounds like purely a react error, not necessarily related to redux at all. One unrelated thing I noticed is that you're using both a store instance and useSelector. Why? Theoretically useDispatch and useSelector should be sufficient for everything. Commented Nov 13, 2021 at 12:28
  • i had to use store .do you have any suggestion to write it with useselector? Commented Nov 13, 2021 at 12:36
  • 1
    useSelector( state => selectPointByPathIDAndOrder(point.path_id, point.order - 1 )(state)[0]) Commented Nov 13, 2021 at 12:38
  • @phry thanks. i changed it in my code but it doe not resolve the problem Commented Nov 13, 2021 at 12:41

1 Answer 1

2

There is a lot to unpack here. While I'm not sure if it will work with your problem itself, I want to give some feedback.

Generally, the calculation of a new state should not happen in your component, but in your reducer. (The Redux Style guide on this: Put as Much Logic as Possible in Reducers) You should usually also not dispatch multiple (Avoid Dispatching Many Actions Sequentially) "setter-style" dispatch calls, but just one "event-type" dispatch call. (Model Actions as Events, Not Setters). Dispatching three times in a row might lead to your component rerendering three times, with unexpected combinations of state in-between.

That would move a lot of code out of this component, make it more readable and maybe make it more obvious finding the error source (or even eliminate it, if it had to do with rendering on inconsistent state)

Sign up to request clarification or add additional context in comments.

4 Comments

what should i do if i need to dispatch one event from one slice then another event from anoter slice?
Using extraReducers, one slice can listen to actions from another slice. So just decide where the event-action should live as a normal action - or, if none of the slices seems fitting create an action creator with createAction and listen to it with extraReducers from all the other slices. Just make sure the action payload contains all the necessary informations.
can you give me sample code?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.