Search code examples
mysqlreactjsdoublerows

React runs via Axios Mysql Query twice


I'm running into a double execution of my axios request in a functional react-app, which by random inserts 1 or 2 rows instead of always just 1 row into the database. Tried to wrap it in a useEffect-Hook...did not help. By logging the execution function it seems only to run once. But on the php-side it's kind of executed twice. The strange thing is, that I've implemented the same thing in two other parts of the app (just different items) and there the same code just works fine...any help very appreciated! Thx in advance!

Js-Code in React:

function ReservationObjectsDialogAdd() {
  const appState = useContext(StateContext)
  const appDispatch = useContext(DispatchContext)

  const [name, setName] = useState()

  const handleKeyPressAddDialog = e => {
    if (e.which == 13) {
      setReservationObject()
    }
  }

  // add new category
  async function setReservationObject() {
    try {
      // set new Category
      const response = await Axios.post("/Main.php?cmd=setReservationObject", { name })
      console.log(response.data)

      appDispatch({ type: "getReservationObjects" })
      appDispatch({ type: "setOpenAddDialog", data: false })
    } catch (e) {
      console.log(e.message)
      console.log(lang.reservationObjectAddProblem)
    }
  }

  return (
    <Dialog open={appState.openAddDialog} onClose={e => appDispatch({ type: "setOpenAddDialog", data: false })} aria-labelledby="form-dialog-title">
      <DialogTitle id="form-dialog-title">{lang.addTimeName}</DialogTitle>
      <DialogContent>
        <TextField onChange={e => setName(e.target.value)} autoFocus margin="dense" id="name" label={lang.timeName} type="text" fullWidth required={true} onKeyPress={handleKeyPressAddDialog} />
      </DialogContent>
      <DialogActions>
        <Button onClick={e => appDispatch({ type: "setOpenAddDialog", data: false })} color="primary">
          {lang.cancel}
        </Button>
        <Button onClick={setReservationObject} color="primary">
          {lang.add}
        </Button>
      </DialogActions>
    </Dialog>
  )
}

export default ReservationObjectsDialogAdd

PHP-Side:

case "setReservationObject":

    $conn = new DBConnection($host, $dbuser, $dbpassword, $db);
    $post = json_decode(file_get_contents('php://input'), true);
    $maxOrder = $conn->query("SELECT MAX(orderNumber) as maxorder FROM reservationObjects", [])->fetch(PDO::FETCH_ASSOC);
    $maxOrder = $maxOrder['maxorder'] + 1;
    $activeCategory = $conn->query("SELECT id FROM reservationCategories WHERE active=?", [1])->fetch(PDO::FETCH_ASSOC);
    $conn->query("INSERT INTO reservationObjects (category,name,orderNumber,visible) values(?,?,?,?)", [$activeCategory['id'], $post['name'], $maxOrder, 1]);

    break;

Here the rendering-code:

function ReservationObjects() {
  const classes = useStyles()

  const appState = useContext(StateContext)
  const appDispatch = useContext(DispatchContext)

  const [reservationObjects, setReservationObjects] = useState([])

  const [images, setImages] = useState()

  //sort categories
  function onSortEnd({ oldIndex, newIndex }) {
    let newReservationObjects = reservationObjects.map((el, i) => {
      return el
    })

    newReservationObjects = arrayMove(newReservationObjects, oldIndex, newIndex)

    setReservationObjects(newReservationObjects)

    async function sortObjects(newReservationObjects) {
      try {
        // sort Data in DB
        const response = await Axios.post("/Main.php?cmd=sortObjects", { reservationObjects: newReservationObjects })

        appDispatch({ type: "getReservationObjects" })
        appDispatch({ type: "getReservationItems" })
      } catch (e) {
        console.log(e.message)
        console.log(lang.categorySortProblem)
      }
    }
    sortObjects(newReservationObjects)
  }

  // sort events- part 1
  function handleDragEndSortObjects(event) {
    const { active, over } = event

    if (active.id !== over.id) {
      const tempReservationObjects = reservationObjects.map((el, i) => {
        return el
      })
      let oldIndex = null
      let newIndex = null
      tempReservationObjects.map((el, i) => {
        if (active.id == el.id) {
          oldIndex = i
        }
        if (over.id == el.id) {
          newIndex = i
        }
      })
      onSortEnd({ oldIndex, newIndex })
    }
  }

  function handleDragEndAssignObjects(event) {
    console.log(event)
  }

  // in Sort-Mode check if the clicked target is a interface-entity
  function shouldCancelStart(e) {
    console.log("enter should cancel")
    if (e.target.hasAttribute("isadmin")) {
      if (e.target.attributes.isadmin.value) {
        console.log("enter should cancel return false")
        return false
      }
    }
    if (e.target.hasAttribute("interface")) {
      if (e.target.attributes.interface.value) {
        console.log("enter should cancel return true")
        return true
      }
    }
  }

  // initial loading of reservation objects
  useEffect(() => {
    async function getReservationObjects() {
      try {
        const response = await Axios.post("/Main.php?cmd=getReservationObjects", { isadmin: appState.isAdmin, category: appState.activeCategoryNumber }).then(response => {
          setReservationObjects(response.data)
          appDispatch({ type: "getReservationTimes" })
        })
      } catch (e) {
        console.log(lang.reservationCategoriesProblem)
      }
    }
    getReservationObjects()
  }, [appState.getReservationObjectsTrigger])

  //initial loading of images
  useEffect(() => {
    async function loadImages() {
      try {
        const response = await Axios.post("/Main.php?cmd=getImages")
        //console.log(response.data)
        setImages(response.data)
      } catch (e) {
        console.log(e.message)
      }
    }
    loadImages()
  }, [])

  // handle mouse leave -> background Image
  function handleObjectMouseLeave(e) {
    appDispatch({ type: "setBackgroundImage", data: "" })
  }

  //handle mouse enter -> background Image
  function handleObjectMouseEnter(e) {
    if (e.target.hasAttribute("image")) {
      let image = e.target.attributes.image.value
      appDispatch({ type: "setBackgroundImage", data: image })
    }
  }

  const sensors = useSensors(
    useSensor(PointerSensor),
    useSensor(KeyboardSensor, {
      coordinateGetter: sortableKeyboardCoordinates
    })
  )

  function Draggable(props) {
    const { attributes, listeners, setNodeRef, transform, isDragging, over } = useDraggable({
      id: props.id,
      category: props.category
    })
    const style = transform
      ? {
          transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`
        }
      : undefined

    return (
      <div ref={setNodeRef} className="reservationArea__reservationObjectDraggable" style={style} {...listeners} {...attributes}>
        <ReservationObject category={props.category} key={props.id} id={props.id} name={props.name} hover={appState.hoverObjectId == props.id ? "hovering" : ""} visible={props.visible} isadmin={appState.isAdmin.toString()} id={props.id} isactive={props.active} hovered={appState.reservationItems} image={props.image} onMouseEnter={handleObjectMouseEnter} onMouseLeave={handleObjectMouseLeave} />
      </div>
    )
  }

  function sortableVsDroppable() {
    if (appState.objectsSortable) {
      return (
        <DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEndSortObjects}>
          <SortableContext
            items={reservationObjects.map(item => {
              return item.id
            })}
            strategy={horizontalListSortingStrategy}
            className="reservationArea__reservationObjects"
          >
            <div className="reservationArea__reservationObjects">
              {reservationObjects.map((item, i) => (
                <ReservationObject key={item.id} id={item.id} name={item.name} hover={appState.hoverObjectId == item.id ? "hovering" : ""} visible={item.visible} isadmin={appState.isAdmin.toString()} id={item.id} isactive={item.active} hovered={appState.reservationItems} image={item.image} onMouseEnter={handleObjectMouseEnter} onMouseLeave={handleObjectMouseLeave} />
              ))}
            </div>
            {appState.isAdmin ? (
              <Link to="/" onClick={e => appDispatch({ type: "setOpenAddDialog", data: true })} className="reservationArea__addObject">
                <AddCircleOutlineIcon />
              </Link>
            ) : (
              ""
            )}
          </SortableContext>
        </DndContext>
      )
    } else {
      console.log("assignable")
      return (
        <>
          <div className="reservationArea__reservationObjects">
            {reservationObjects.map((item, i) => (
              <Draggable key={item.id} category={item.category} id={item.id} index={item.id} name={item.name} hover={appState.hoverObjectId == item.id ? "hovering" : ""} visible={item.visible} isadmin={appState.isAdmin.toString()} id={item.id} isactive={item.active} hovered={appState.reservationItems} image={item.image} onMouseEnter={handleObjectMouseEnter} onMouseLeave={handleObjectMouseLeave} />
            ))}
          </div>
          {appState.isAdmin ? (
            <Link to="/" onClick={e => appDispatch({ type: "setOpenAddDialog", data: true })} className="reservationArea__addObject">
              <AddCircleOutlineIcon />
            </Link>
          ) : (
            ""
          )}
        </>
      )
    }
  }

  return (
    <div className="reservationArea__reservationObjectsContainer">
      <ReservationObjectsImage />

      {sortableVsDroppable()}
      <ReservationObjectsDialogAdd />
      <ReservationObjectsDialogEdit />
      <ReservationObjectsDialogDelete />
    </div>
  )
}

export default ReservationObjects

Solution

  • Finally solved. It was in the php-part. I replaced the fetching of the activeCategory directly in mysql with a parameter that I sent with Axios. Somehow the activeCategory-fetching led to to this strange behaviour, that it randmly executed the insert-statement once or twice.