React запускается через запрос Axios Mysql дважды

#mysql #reactjs #double #rows

Вопрос:

Я сталкиваюсь с двойным выполнением моего запроса axios в функциональном приложении react, которое случайным образом вставляет 1 или 2 строки вместо всегда только 1 строки в базу данных. Попытался завернуть его в полезный крючок…не помогло. Регистрируя функцию выполнения, кажется, что она запускается только один раз. Но на стороне php это вроде как выполняется дважды. Странно то, что я реализовал то же самое в двух других частях приложения (просто разные элементы), и там тот же код просто отлично работает…любая помощь очень ценится! Thx заранее!

Js-Код в 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-Сторона:

 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;
 

Вот рендеринг-код:

 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
 

Комментарии:

1. Здесь вы можете увидеть, что происходит в приложении: recordit.co/M18FnajjUk

2. По этому gif нельзя определить, вызываете ли вы setReservationObject дважды или только один раз, или сколько запросов сделано. Вы подтвердили, что делаете только один запрос API? Похоже, это проблема с тем, где вы раздираете пользовательский интерфейс. Можете ли вы обновить свой вопрос, чтобы включить код рендеринга?

3. Просто добавил код рендеринга. Если я регистрирую выполнение setReservationObject() , оно регистрируется только один раз.

4. Как вы можете видеть здесь, setReservationObject() это выполняется только один раз. И если вы наблюдаете за добавлением объектов, как только он добавляет два элемента, как только он добавляет только один (что было бы правильно). recordit.co/mzJA3ygaEr

5. Итак, если я правильно прочитал, вы сопоставляете реквизит для SortableContext ? Попробуйте сократить свой код до гораздо более простого компонента, без сортировки/перетаскивания/перемещения/и т.д. Элементов массива, чтобы увидеть, где он нарушается. Мне это arrayMove кажется подозрительным.

Ответ №1:

Наконец-то решено. Это было в php-части. Я заменил выборку категории activeCategory непосредственно в mysql параметром, который я отправил с Axios. Каким-то образом выборка activeCategory привела к такому странному поведению, что она случайно выполнила инструкцию insert один или два раза.