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

Next.js向Supabase发送时间数据失败,求类型兼容解决方案

Next.js 向 Supabase 提交时间数据失败的解决方案

我在使用 Next.js 向 Supabase 提交时间数据时始终失败,推测是数据类型不符合 Supabase 的要求,但不知道如何修复。尝试用 new Date() 转换类型也没有效果。以下是我的代码:

原 EventForm 组件代码

"use client";

import { useState, useEffect } from "react";
import {
  Box,
  TextField,
  Button,
  Checkbox,
  Container,
  Grid,
} from "@mui/material";
import { publishEvent } from "../../utils/supabaseFunction";
import { useRouter } from "next/navigation";
import Link from "next/link";
import dayjs from "dayjs";
import { Session } from "@supabase/auth-helpers-nextjs";
import { supabase } from "@/utils/supabase";
import { start } from "repl";

const EventForm = ({ session }: { session: Session | null }) => {
  const router = useRouter();
  const [title, setTitle] = useState<string>("");
  const [description, setDescription] = useState<string>("");
  const [date, setDate] = useState<Date | undefined>();
  const [start_time, setStart_time] = useState<Date | undefined>();
  const [capacity, setCapacity] = useState<string>("");
  const [host_id, setHost_id] = useState<string>("");
  const [is_published, setIs_Published] = useState<boolean>(false);

  const user = session?.user;
  useEffect(() => {
    const fetchUserId = async () => {
      try {
        const { data: users, error } = await supabase
          .from("profiles")
          .select(`id`)
          .eq("id", user?.id)
          .single();
        if (error) {
          throw error;
        }
        setHost_id(users.id);
      } catch (error) {
        console.error("Error fetching data:", error);
      }
    };
    fetchUserId();
  }, []);

  console.log(start_time);

  const handlePublishEvent = async (e: any) => {
    e.preventDefault();
    await publishEvent(
      title,
      description,
      capacity,
      date,
      start_time,
      host_id,
      is_published
    );
    router.push("/event");
  };

  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setIs_Published(event.target.checked);
  };

  return (
    <>
      <div>
        <Container component="main" maxWidth="xs">
          {" "}
          <Box component="form" onSubmit={(e) => handlePublishEvent(e)}>
            <Grid item xs={12}>
              <TextField
                type="text"
                name="title"
                value={title}
                onChange={(e) => setTitle(e.target.value)}
              />
            </Grid>
            <Grid item xs={12}>
              <TextField
                type="text"
                name="description"
                value={description}
                onChange={(e) => setDescription(e.target.value)}
              />
            </Grid>
            <Grid item xs={12}>
              <TextField
                type="date"
                name="date"
                value={dayjs(date).format("YYYY-MM-DD")}
                onChange={(e) => setDate(new Date(e.target.value))}
              />
            </Grid>
            <Grid item xs={12}>
              <TextField
                type="time"
                name="time"
                value={dayjs(date).format("HH:mm:ss")}
                onChange={(e) => setStart_time(new Date(e.target.value))}
              />
            </Grid>
            <Grid item xs={12}>
              <TextField
                type="text"
                name="capacity"
                value={capacity}
                onChange={(e) => setCapacity(e.target.value)}
              />
            </Grid>
            <Checkbox
              checked={is_published}
              onChange={handleChange}
              inputProps={{ "aria-label": "controlled" }}
            />
            Check
            <div>
              <div>
                <Button variant="contained" type="submit">
                  {is_published == true ? <>Publish</> : <>Save</>}
                </Button>
              </div>
              <div>
                <Button variant="text">
                  <Link href="/event">Back</Link>
                </Button>
              </div>
            </div>
          </Box>
        </Container>
      </div>
    </>
  );
};

export default EventForm;

原 publishEvent 函数代码

export const publishEvent = async (
  title: string,
  description: string,
  capacity: string,
  date: Date | undefined,
  start_time: Date | undefined,
  host_id: string,
  is_published: boolean
) => {
  await supabase.from("events").insert({
    title: title,
    description: description,
    capacity: capacity,
    date: date,
    start_time: start_time,
    host_id: host_id,
    is_published: is_published,
  });
};

问题根源

  1. 时间输入处理错误:type="time" 的输入框返回 HH:mm 格式字符串,直接用 new Date(e.target.value) 会生成无效 Date 对象(比如 new Date("14:30") 返回 Invalid Date),Supabase 无法识别。
  2. 日期与时间未合并:Supabase 的 timestamp 类型需要完整的日期时间信息,当前代码将日期和时间拆分为两个独立对象,不符合字段预期。
  3. 初始值异常:当 date 为 undefined 时,dayjs(date).format("HH:mm:ss") 生成无效字符串,导致输入框显示异常。
  4. 无错误捕获:publishEvent 未处理 Supabase 请求错误,无法定位具体失败原因。

修复方案

修改后的 EventForm 组件

"use client";

import { useState, useEffect } from "react";
import {
  Box,
  TextField,
  Button,
  Checkbox,
  Container,
  Grid,
} from "@mui/material";
import { publishEvent } from "../../utils/supabaseFunction";
import { useRouter } from "next/navigation";
import Link from "next/link";
import { Session } from "@supabase/auth-helpers-nextjs";
import { supabase } from "@/utils/supabase";

const EventForm = ({ session }: { session: Session | null }) => {
  const router = useRouter();
  const [title, setTitle] = useState<string>("");
  const [description, setDescription] = useState<string>("");
  const [date, setDate] = useState<string>(""); // 存储YYYY-MM-DD字符串
  const [startTimeStr, setStartTimeStr] = useState<string>(""); // 存储HH:mm字符串
  const [capacity, setCapacity] = useState<string>("");
  const [host_id, setHost_id] = useState<string>("");
  const [is_published, setIs_Published] = useState<boolean>(false);

  const user = session?.user;
  useEffect(() => {
    const fetchUserId = async () => {
      try {
        const { data: users, error } = await supabase
          .from("profiles")
          .select(`id`)
          .eq("id", user?.id)
          .single();
        if (error) throw error;
        setHost_id(users.id);
      } catch (error) {
        console.error("获取用户ID失败:", error);
      }
    };
    if (user) fetchUserId(); // 避免user为undefined时执行
  }, [user]);

  const handlePublishEvent = async (e: React.FormEvent) => {
    e.preventDefault();
    // 合并日期与时间为完整Date对象
    const fullStartTime = date && startTimeStr 
      ? new Date(`${date}T${startTimeStr}:00`)
      : undefined;

    try {
      await publishEvent(
        title,
        description,
        capacity,
        date ? new Date(date) : undefined,
        fullStartTime,
        host_id,
        is_published
      );
      router.push("/event");
    } catch (error) {
      console.error("提交事件失败:", error);
    }
  };

  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setIs_Published(event.target.checked);
  };

  return (
    <Container component="main" maxWidth="xs">
      <Box component="form" onSubmit={handlePublishEvent}>
        <Grid item xs={12} mb={2}>
          <TextField
            fullWidth
            type="text"
            name="title"
            label="标题"
            value={title}
            onChange={(e) => setTitle(e.target.value)}
            required
          />
        </Grid>
        <Grid item xs={12} mb={2}>
          <TextField
            fullWidth
            type="text"
            name="description"
            label="描述"
            value={description}
            onChange={(e) => setDescription(e.target.value)}
            required
          />
        </Grid>
        <Grid item xs={12} mb={2}>
          <TextField
            fullWidth
            type="date"
            name="date"
            label="日期"
            value={date}
            onChange={(e) => setDate(e.target.value)}
            InputLabelProps={{ shrink: true }}
            required
          />
        </Grid>
        <Grid item xs={12} mb={2}>
          <TextField
            fullWidth
            type="time"
            name="time"
            label="开始时间"
            value={startTimeStr}
            onChange={(e) => setStartTimeStr(e.target.value)}
            InputLabelProps={{ shrink: true }}
            required
          />
        </Grid>
        <Grid item xs={12} mb={2}>
          <TextField
            fullWidth
            type="number"
            name="capacity"
            label="容量"
            value={capacity}
            onChange={(e) => setCapacity(e.target.value)}
            required
          />
        </Grid>
        <Grid item xs={12} mb={2}>
          <Checkbox
            checked={is_published}
            onChange={handleChange}
            inputProps={{ "aria-label": "是否发布" }}
          />
          发布事件
        </Grid>
        <Grid container spacing={2}>
          <Grid item xs={6}>
            <Button fullWidth variant="contained" type="submit">
              {is_published ? "发布" : "保存"}
            </Button>
          </Grid>
          <Grid item xs={6}>
            <Button fullWidth variant="text">
              <Link href="/event">返回</Link>
            </Button>
          </Grid>
        </Grid>
      </Box>
    </Container>
  );
};

export default EventForm;

修改后的 publishEvent 函数

import { supabase } from "@/utils/supabase";

export const publishEvent = async (
  title: string,
  description: string,
  capacity: string,
  date: Date | undefined,
  start_time: Date | undefined,
  host_id: string,
  is_published: boolean
) => {
  const { error } = await supabase.from("events").insert({
    title,
    description,
    capacity: parseInt(capacity, 10), // 转换为数字匹配数据库integer字段
    date,
    start_time,
    host_id,
    is_published,
  });

  if (error) {
    throw new Error(`Supabase 插入错误: ${error.message}`);
  }
};

额外注意事项

  • 确保 Supabase events 表中,date 字段类型为 date,start_time 字段类型为 timestamp with time zone 或 timestamp。
  • 若无需拆分日期和时间,可直接使用单个 datetime 字段简化逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 03:51:02