React+Rails应用提交预订后卡片不渲染需刷新的问题咨询
问题:React+Rails应用提交预订后跳转页面不渲染新预订,刷新才显示
提交预订表单发起POST请求后,页面跳转到/myreservations,但新的预订卡片无法渲染;只有刷新页面后,卡片才会正常显示并保留。
原因分析
- 异步请求未等待完成就跳转:在
handleReservationSubmit中,navigate("/myreservations")是同步执行的,没有等待POST请求完成。这会导致跳转到/myreservations时,后端还没完成新预订的存储,此时MyReservations组件的useEffect(仅在组件挂载时执行一次)已经发起了GET请求,自然拿不到刚提交的预订数据。 - 全局状态未及时更新:提交新预订后,没有立即将新创建的预订数据添加到全局的
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
相关产品推荐
相关产品推荐

