React表单onSubmit调用useReadCypher违反Hook规则的解决方法咨询
问题根因
核心错误有两点:
- React Hook只能在组件顶层作用域调用,不能在事件回调、循环、条件判断里执行,你最开始在
onSubmit里调用useReadCypher直接违反了这个规则。 - 第二次修改的代码存在两处语法逻辑错误:
setResult是状态更新函数,不能直接赋值,需要调用setResult(新值)完成状态更新- 初始值设为空字符串,后续读取
result.query必然是undefined,且useReadCypher默认会在组件初始化时就执行查询,会发送无效请求。
可行实现方案
方案1:使用useLazyReadCypher(最推荐)
use-neo4j原生提供了懒执行查询的Hook useLazyReadCypher,不会在组件渲染时自动执行,只会在你手动调用触发函数时执行,完美匹配表单提交按需触发的场景。
完整修正代码如下:
import React, { useState, useEffect } from "react"; import { useLazyReadCypher } from "use-neo4j"; import { useForm } from "react-hook-form"; function Login() { const { register, handleSubmit } = useForm(); // 第一个返回值是手动触发查询的函数,第二个是查询结果状态 const [executeQuery, { loading, first }] = useLazyReadCypher(); // 监听查询返回结果,处理登录逻辑 useEffect(() => { if (first) { // 校验通过,可自行添加跳转、存储登录态逻辑 console.log("登录成功", first); } }, [first]); const onSubmit = (form) => { // 修正原Cypher语法错误:属性筛选要写在节点的括号里 const query = "MATCH (u:User {username: $username, password: $password}) RETURN u"; const params = { username: form.username, password: form.password }; // 手动触发查询 executeQuery(query, params); }; return ( <form className="loginForm bg-white shadow-md rounded-md px-8 pt-6 pb-8 mb-4 mx-8" onSubmit={handleSubmit(onSubmit)}> <p className="font-bold text-xl">Login</p> {/* 可加loading状态提示 */} {loading && <p className="text-blue-500 my-2">登录中...</p>} <hr className="my-3" /> <div className="mb-4"> <label className="text-left block text-gray-700 text-sm font-bold mb-2" htmlFor="username"> Gebruikersnaam </label> <input className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="username" type="text" placeholder="Gebruikersnaam" {...register("username")} /> </div> <div className="mb-6"> <label className="text-left block text-gray-700 text-sm font-bold mb-2" htmlFor="password"> Wachtwoord </label> <input className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="password" name="password" type="password" placeholder="Wachtwoord" {...register("password")} /> </div> <div className="flex items-center justify-center"> <input className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline" type="submit" value="Aanmelden" disabled={loading} // 登录中禁用提交按钮避免重复请求 /> </div> </form> ); } export default Login;
方案2:使用useReadCypher配合状态控制
如果你坚持要用useReadCypher,可以通过enabled参数控制查询是否执行,修正后的核心代码如下:
const { register, handleSubmit } = useForm(); // 初始参数设为null,查询默认不执行 const [queryParams, setQueryParams] = useState(null); const query = "MATCH (u:User {username: $username, password: $password}) RETURN u"; const { loading, first } = useReadCypher( query, queryParams, { enabled: !!queryParams } // 只有queryParams存在时才执行查询 ); const onSubmit = (form) => { setQueryParams({ username: form.username, password: form.password }); }; // 同方案1,监听first处理登录结果
注意这个方案会在queryParams更新后自动触发重渲染执行查询,多一次重渲染开销,性能比方案1差。
额外注意事项
- 原始Cypher语句存在语法错误,节点属性筛选需要写在节点声明的括号内,不要单独写在外面的大括号。
- 不要在数据库中明文存储用户密码,建议加密后存储,登录时先加密输入的密码再和数据库存储的密文对比。
内容的提问来源于stack exchange,提问作者Lode
相关产品推荐
相关产品推荐

