网站搜索

javascript - Next.js: `onSubmit` 未执行回调函数


在我的 Next.js 模块中,我声明了一个表单,如下所示:

<form onSubmit = {() => {
async() => await getCertificate(id)
.then(async resp => await resp.json())
.then(data => console.log(data))
.catch(err => console.error(err))
console.log("getCertificate completed")
}}>

函数 getCertificate() 定义在与以下内容相同的 .tsx 文件中:

async function getCertificate(id: string) : Promise<Response> {
  console.log("in getCertificate")
  const resp = await fetch(`${serverUri}/certs/${id}`)
  console.log(`Response data: ${await resp.json()}`)
  return resp
}

看起来 getCertificate() 根本没有被调用。虽然我在 onSubmit 处理函数中的最终 console.log("getCertificate completed") 正确执行,但 getCertificate() 内的任何 console.log() 语句都没有被执行。

没有错误被抛出或记录到控制台。控制台中出现的唯一内容(除了getCertificate completed)是Navigated to http://localhost:3000/?certId=my_cert_id。

有什么想法为什么它会这样表现吗?

(唯一需要注意的其他事项是,我的 .tsx 文件使用了 'use client' 装饰,因为我在某些其他功能中使用了客户端库,并且我的表单定义位于 export default function 的 return 语句内。除此之外,所有功能都按预期工作,唯一的问题是 getCertificates() 没有被触发。)

答案#1

可能的解决方案

如果唯一的问题是执行 getCertificate,那么你可以尝试做如下操作:

import React, { useState } from "react";

const serverUri = "http://myserver.com";

export default function Certificate() {
  const [id, setId] = useState("");
  async function getCertificate(id: string): Promise<Response> {
    console.log("in getCertificate");
    const resp = await fetch(`${serverUri}/certs/${id}`);
    console.log(`Response data: ${await resp.json()}`);
    return resp;
  }
  return (
    <form onSubmit={async (event: any) => {
      event.preventDefault();
      try {
        const response = await getCertificate(id);
        const data = await response.json();
        console.log(data);
      } catch (err) {
        console.error(err);
      }
      console.log("getCertificate completed");
    }}>
      <label>
        Certificate ID:
        <input type="text" value={id} onChange={e => setId(e.target.value)} />
      </label>
      <button type="submit">Submit</button>
    </form>
  );
}

如你在截图中所见,in getCertificate 已打印到控制台。

无法执行的原因

让我们仔细看看原始函数:

() => {
    async() => await getCertificate(id)
    .then(async resp => await resp.json())
    .then(data => console.log(data))
    .catch(err => console.error(err))
    console.log("getCertificate completed")
}

如您在这里看到的,您只需在此处定义一个匿名异步箭头函数(不将该函数赋值给任何变量,因此该函数从未被调用),然后只执行 console.log。所以 console.log 是唯一实际被执行的内容。

另外,你在调用 getCertificate 前添加了 await,这是不必要的,可能会导致额外的混淆。

让我们简化这行代码

async() => await getCertificate(id)
.then(async resp => await resp.json())
.then(data => console.log(data))
.catch(err => console.error(err))

所以首先我们将重新格式化代码以查看其结构:

async() => { 
    await getCertificate(id)
        .then(async resp => await resp.json())
        .then(data => console.log(data))
        .catch(err => console.error(err))
}

然后我们删除不必要的函数定义:

await getCertificate(id)
    .then(async resp => await resp.json())
    .then(data => console.log(data))
    .catch(err => console.error(err))

然后我们将移除不必要的 await:

getCertificate(id)
    .then(async resp => await resp.json())
    .then(data => console.log(data))
    .catch(err => console.error(err))

另一个解决方案

因此,使用前一节中的变换,我们可以得到定义整个函数的另一种方法。

(event: any) => {
    event.preventDefault();
    getCertificate(id)
        .then(async resp => await resp.json())
        .then(data => console.log(data))
        .then(() => console.log("getCertificate completed with no errors"))
        .catch(err => console.error(err))
        .then(() => console.log("getCertificate completed with errors"))
}

附言

请注意,两个解决方案中的 event.preventDefault() 都会阻止表单数据的实际传输,并将强制在 JS 中处理所有内容。它是可选的,可以省略。

答案#2

你将 getCertificate 调用包装在一个匿名函数中,而你实际上从未调用过它。从表面上看,根本不需要那个包装函数,所以你可以将其内部逻辑直接提升到 onSubmit 函数中,像这样:

<form onSubmit = {async () => {
    try {
        const resp = await getCertificate(id);
        const data = await resp.json();
        console.log(data);
        console.log("getCertificate completed");
    } catch (err) {
        console.error(err)
    }
}}>

或者使用 promise 链式调用:

<form onSubmit = {() => {
    getCertificate(id)
        .then(async resp => await resp.json())
        .then(data => console.log(data))
        .then(() => console.log("getCertificate completed"))
        .catch(err => console.error(err))
}}>

我建议你坚持使用 async/await 或者 promise 链,而不是混合使用,因为混合使用会让逻辑更难理解。就我个人而言,我推荐 async/await。