You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React+Rails应用提交预订后卡片不渲染需刷新的问题咨询

问题:React+Rails应用提交预订后跳转页面不渲染新预订,刷新才显示

提交预订表单发起POST请求后,页面跳转到/myreservations,但新的预订卡片无法渲染;只有刷新页面后,卡片才会正常显示并保留。

原因分析

  1. 异步请求未等待完成就跳转:在handleReservationSubmit中,navigate("/myreservations")是同步执行的,没有等待POST请求完成。这会导致跳转到/myreservations时,后端还没完成新预订的存储,此时MyReservations组件的useEffect(仅在组件挂载时执行一次)已经发起了GET请求,自然拿不到刚提交的预订数据。
  2. 全局状态未及时更新:提交新预订后,没有立即将新创建的预订数据添加到全局的reservations状态中,而是依赖刷新页面时重新拉取所有数据来更新状态。

解决方法

方案一:等待POST请求完成后更新状态再跳转

修改ReservationForm中的handleReservationSubmit函数,确保在POST请求成功后,将新创建的预订添加到全局状态,再执行跳转:

function handleReservationSubmit(event) {
  event.preventDefault();
  const newReservation = {
    ...reservationData,
    restaurant_id: params.id,
    user_id: user.id,
  };

  // 先在组件顶部补充获取全局状态的方法
  const { reservations, setReservations } = useContext(Cont);

  fetch(`/reservations`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(newReservation),
  })
    .then((r) => {
      if (!r.ok) throw new Error("预订提交失败");
      return r.json();
    })
    .then((createdReservation) => {
      // 重置表单数据
      setReservationData({
        name: "",
        date: "",
        time: "",
        num: "",
        contact: "",
        occasion: "",
      });
      // 将新预订添加到全局状态
      setReservations([...reservations, createdReservation]);
      // 等待状态更新后跳转
      navigate("/myreservations");
    })
    .catch((err) => {
      setErrors({ submit: err.message });
    });
}

方案二:跳转后触发MyReservations重新获取数据

如果不想直接修改全局状态,可以在MyReservations组件中监听路由变化,每次进入页面时重新拉取数据:

import { useEffect, useState } from "react";
import ReservationCard from "./ReservationCard";
import { useContext } from "react";
import { Cont } from "../Cont";
import { useLocation } from "react-router-dom";

function MyReservations() {
  const { reservations, setReservations } = useContext(Cont);
  const location = useLocation();

  useEffect(() => {
    fetch("/reservations")
      .then((res) => res.json())
      .then((reservationData) => {
        setReservations(reservationData);
      });
  }, [location]); // 监听路由变化,每次进入页面都重新请求

  // 其余代码保持不变
}
export default MyReservations;

相关代码

Reservation Form

function ReservationForm() {
  const navigate = useNavigate();
  const params = useParams();
  const { user,errors,setErrors } = useContext(Cont);

  const [reservationData, setReservationData] = useState({
    name: "",
    date: "",
    time: "",
    num: "",
    contact: "",
    occasion: "",
  });
  function handleReservationChange(event) {
    setReservationData({
      ...reservationData,
      [event.target.name]: event.target.value,
    });
  }
  function handleReservationChangeWithNameAndValue(name, newValue) {
    setReservationData({
      ...reservationData,
      [name]: newValue,
    });
  }
  function handleReservationSubmit(event) {
    event.preventDefault();
    const newReservation = {
      ...reservationData,
      restaurant_id: params.id,
      user_id: user.id,
    };

    fetch(`/reservations`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(newReservation),
    })
      .then((r) => r.json())
      .then(
        setReservationData({
          name: "",
          date: "",
          time: "",
          num: "",
          contact: "",
          occasion: "",
        })
      );
    navigate("/myreservations");
  }

  return (
    <>
    <div  className="overlay3">
      <Box
        component="form"
        sx={{
          "& > :not(style)": { m: 1 },
        }}
        noValidate
        autoComplete="off"
        onSubmit={handleReservationSubmit}
      >
        <h1 className="editheadings">Reserve 🍽️</h1>

        <FormControl className="inputstyle">
          <InputLabel htmlFor="component-outlined">Name</InputLabel>
          <OutlinedInput
            type="text"
            name="name"
            id="name"
            value={reservationData.name}
            onChange={handleReservationChange}
            label="Name"
          />
        </FormControl>
        <br />
        <FormControl name="date"  className="inputstyle">
          <LocalizationProvider
            dateAdapter={AdapterDayjs}
            name="date"
            fullWidth
          >
            <DatePicker
              label="Date"
              name="date"
              value={reservationData.date || null}
              onChange={(newVal) =>
                handleReservationChangeWithNameAndValue("date", newVal)
              }
              renderInput={(params) => <TextField {...params} />}
            />
          </LocalizationProvider>
        </FormControl>
        <FormControl  className="inputstyle">
          <LocalizationProvider dateAdapter={AdapterDayjs}>
            <TimePicker
              name="time"
              label="Time"
              value={reservationData.time || null}
              onChange={(newVal) =>
                handleReservationChangeWithNameAndValue("time", newVal)
              }
              renderInput={(params) => <TextField {...params} />}
            />
             
          </LocalizationProvider>
        </FormControl>
        <br />
        <FormControl className="inputstyle">
          <InputLabel htmlFor="component-outlined">No. of Guests</InputLabel>
          <OutlinedInput
            type="number"
            name="num"
            value={reservationData.num}
            onChange={handleReservationChange}
          />
        </FormControl>
        <br />
        <FormControl className="inputstyle">
          <InputLabel htmlFor="component-outlined">Contact</InputLabel>
          <OutlinedInput
            type="tel"
            name="contact"
            value={reservationData.contact}
            onChange={handleReservationChange}
            placeholder="contact"
          />
        </FormControl>
        <br />
        <FormControl className="inputstyle">
          <InputLabel htmlFor="component-outlined">Occasion</InputLabel>
          <OutlinedInput
            type="text"
            name="occasion"
            value={reservationData.occasion}
            onChange={handleReservationChange}
          />
        </FormControl>
        <Stack paddingLeft={15} direction="row" id="loginbutton">
          <ColorButton variant="contained" type="submit">
            {" "}
            Reserve
          </ColorButton>
        </Stack>
      </Box>
      </div>
    </>
  );
}
export default ReservationForm;

My Reservations

import {useEffect, useState } from "react";
import ReservationCard from "./ReservationCard";
import { useContext } from "react";
import { Cont } from "../Cont";

function MyReservations(){
 
  const {reservations,setReservations}=useContext(Cont);
  
  useEffect(()=>{
    fetch("/reservations")
    .then(res=>res.json())
    .then(reservationData=>{
      setReservations(reservationData)
    })
  
   },[])
  
     
      function handleUpdateReservation(updatedReservation) {
        const updatedReservations = reservations.map((reservation) => {
          if (reservation.id === updatedReservation.id) {
            return updatedReservation;
          } else {
            return reservation;
          }
        });
        setReservations(updatedReservations);
      }
    

      function handleCancel(reservationtodelete){
        const newReservations=reservations.filter(r=>r.id !== reservationtodelete)
         setReservations(newReservations)
         
       }


const renderReservations=reservations.map((reservation)=>(
  <ReservationCard key={reservation.id} reservation={reservation} handleCancel={handleCancel}  onUpdateReservation={handleUpdateReservation}   />
  ))
  
    return(
        <> 
        {renderReservations}
        
        </>
    )
}
export default MyReservations;

Reservation Card

function ReservationCard({ reservation, handleCancel, onUpdateReservation }) {
  const { name, date, time, num, contact, occasion } = reservation;
  const [isEditing, setIsEditing] = useState(false);
  const handleReservationUpdate = (updatedReservation) => {
    setIsEditing(false);
    onUpdateReservation(updatedReservation);
  };

  function handleDeleteClick() {
    fetch(`/reservations/${reservation.id}`, {
      method: "DELETE",
    });
    handleCancel(reservation.id);
  }

  return (
    <>
      {isEditing ? (
        <Box m={4} sx={{ width: 500 }}>
          <div className="overlay2">
          <EditReservationForm
            reservation={reservation}
            onUpdateReservation={handleReservationChange}
          />
          </div>
        </Box>
      ) : (
        <>
          <Box m={4} sx={{ width: 500 }}>
            <Card width={5}>
              <CardContent>
                <Typography variant="h5" component="div">
                  {reservation.restaurant.name}
                </Typography>
                <br />
                <Typography sx={{ mb: 1.5 }} color="text.secondary">
                  Guest Details
                </Typography>
                <Typography variant="h6" component="div">
                  {name}
                </Typography>
                <Typography variant="h6">{contact}</Typography>
                <Typography variant="h6">Date:{date}</Typography>
                <Typography variant="h6">Time : {time}</Typography>
                <Typography variant="h6">Guests : {num}</Typography>
                <Typography variant="h6">Occasion:{occasion}</Typography>
              </CardContent>
              <CardActions>
                <Button onClick={() => setIsEditing((isEditing) => !isEditing)}>
                  {" "}
                  Modify Booking
                </Button>

                <Button onClick={handleDeleteClick}>Cancel Booking</Button>
              </CardActions>
            </Card>
          </Box>
        </>
      )}
    </>
  );
}
export default ReservationCard;

内容的提问来源于stack exchange,提问作者codertraveller

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 20:05:27