Uncaught (in promise) Error: 在 fetch() 上调用无效的钩子函数

huangapple go评论83阅读模式
英文:

Uncaught (in promise) Error: Invalid hook call on fetch()

问题

CreateBlogPost.js中的代码有一个问题,导致出现“Invalid hook call”的错误。这是因为您在函数组件之外使用了React的Hooks。Hooks只能在函数组件的内部使用,而不能在函数外部或类组件中使用。

要解决这个问题,您需要将handlePostButtonPressed函数中的Hooks调用移到函数组件的内部。以下是修改后的代码:

import React, { useState } from "react";
import Grid from "@mui/material/Grid";
import { FormControl, FormHelperText, TextField, Typography, Button } from "@mui/material";
import { Link } from "react-router-dom";
import { Navigate } from "react-router-dom";

export default function CreateBlogPost(props) {

  const [title, setTitle] = useState('');
  const [content, setContent] = useState('');
  const [tags, setTags] = useState('');

  const handleTitle = event => {
    setTitle(event.target.value);
  }
  const handleContent = event => {
    setContent(event.target.value);
  }
  const handleTags = event => {
    setTags(event.target.value);
  }

  const handlePostButtonPressed = () => { // Remove the parameter
    const requestOptions = {
      method: "POST",
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({
        title: title,
        content: content,
        tags: tags,
      })
    };

    console.log(requestOptions);
    fetch("/blog/create", requestOptions)
      .then((response) => response.json())
      .then((data) => Navigate('/posts/' + data.postid));
  }

  return (
    <Grid container spacing={1} className="pl-2 pt-4">
      <Grid item xs={12}>
        <Typography component="h4" variant="h4">Create a post</Typography>
        <Grid item xs={12} className="-ml-2 mt-4">
          <FormHelperText>
            <div>Title:</div>
          </FormHelperText>
          <FormControl>
            <TextField required={true} type="text" value={title} onChange={handleTitle}/>
          </FormControl>
        </Grid>
        <Grid item xs={12} className="-ml-2 mt-4">
          <FormHelperText>
            <div>Content:</div>
          </FormHelperText>
          <FormControl component="fieldset">
            <TextField required={true} type="text" value={content} onChange={handleContent}/>
          </FormControl>
        </Grid>
        <Grid item xs={12} className="-ml-2 mt-4">
          <FormHelperText>
            <div>Tags:</div>
          </FormHelperText>
          <FormControl component="fieldset">
            <TextField required={true} type="text" value={tags} onChange={handleTags}/>
          </FormControl>
        </Grid>
        <Grid item xs={12}>
          <Button color="primary" variant="contained" onClick={handlePostButtonPressed}>
            Post
          </Button>
          <Button color="secondary" variant="contained" to="/blogs/" component={Link}>
            Back
          </Button>
        </Grid>
    </Grid>
  )
}

通过将handlePostButtonPressed函数中的Hooks调用移动到函数组件内部,您应该能够解决“Invalid hook call”错误。这样,您的代码应该可以正常工作了。

英文:

I'm working on a webapp for Django. It is a simple blog. The front end is being built in react and materials UI, it is based off of the tutorial here, but I have had to adapt much by myself because he is using a Class-oriented approach many methods of which are deprecated as React seems to be moving towards a function based model. In the model that the demonstrator created, we are passing data from our react frontend to our django backend by use of generating JSON objects which python serializes. I have built a blog post submission form that doesn't work for a reason I can't understand.

CreateBlogPost.js

import React, { Component } from &quot;react&quot;;
import Grid from &quot;@mui/material/Grid&quot;;
import { FormControl, FormHelperText, TextField, Typography, Button } from &quot;@mui/material&quot;;
import { Link } from &quot;react-router-dom&quot;;
import { useState } from &quot;react&quot;;
import { Navigate } from &quot;react-router-dom&quot;;
export default function CreateBlogPost(props) {
const[title,setTitle] = useState(&#39;&#39;);
const[content,setContent] = useState(&#39;&#39;);
const[tags, setTags] = useState(&#39;&#39;);
const handleTitle = event =&gt; {
setTitle(event.target.value);
}
const handleContent = event =&gt; {
setContent(event.target.value);
}
const handleTags = event =&gt; {
setTags(event.target.value);
}
const requestOptions = {
method: &quot;POST&quot;,
headers: {&#39;Content-Type&#39;: &#39;application/json&#39;},
body: JSON.stringify({
title: title,
content: content,
tags: tags,
})
};
const handlePostButtonPressed = (requestOptions) =&gt; {
console.log(requestOptions)
fetch(&quot;/blog/create&quot;,requestOptions)
.then((response) =&gt; response.json())
.then((data) =&gt; Navigate(&#39;/posts/&#39; + data.postid));
}
return (
&lt;Grid container spacing={1} className=&quot;pl-2 pt-4&quot;&gt;
&lt;Grid item xs={12}&gt;
&lt;Typography component=&quot;h4&quot; variant=&quot;h4&quot;&gt;Create a post&lt;/Typography&gt;
&lt;Grid item xs={12} className=&quot;-ml-2 mt-4&quot;&gt;
&lt;FormHelperText&gt;
&lt;div&gt;Title:&lt;/div&gt;
&lt;/FormHelperText&gt;
&lt;FormControl&gt;
&lt;TextField required={true} type=&quot;text&quot; value={title} onChange={handleTitle}/&gt;
&lt;/FormControl&gt;
&lt;/Grid&gt;
&lt;/Grid&gt;
&lt;Grid item xs={12} className=&quot;-ml-2 mt-4&quot;&gt;
&lt;FormHelperText&gt;
&lt;div&gt;Content:&lt;/div&gt;
&lt;/FormHelperText&gt;
&lt;FormControl component=&quot;fieldset&quot;&gt;
&lt;TextField required={true} type=&quot;text&quot; value={content} onChange={handleContent}/&gt;
&lt;/FormControl&gt;
&lt;/Grid&gt;
&lt;Grid item xs={12} className=&quot;-ml-2 mt-4&quot;&gt;
&lt;FormHelperText&gt;
&lt;div&gt;Tags:&lt;/div&gt;
&lt;/FormHelperText&gt;
&lt;FormControl component=&quot;fieldset&quot;&gt;
&lt;TextField required={true} type=&quot;text&quot; value={tags} onChange={handleTags}/&gt;
&lt;/FormControl&gt;
&lt;/Grid&gt;
&lt;Grid item xs={12}&gt;
&lt;Button color=&quot;primary&quot; variant=&quot;contained&quot; onClick={handlePostButtonPressed}&gt;
Post
&lt;/Button&gt;
&lt;Button color=&quot;secondary&quot; variant=&quot;contained&quot; to=&quot;/blogs/&quot; component={Link}&gt;
Back
&lt;/Button&gt;
&lt;/Grid&gt;
&lt;/Grid&gt;
)
}

Everything in this works fine until I press the Post button. In the log I see the JSON object of the post I want to submit properly rendered (so I know this is not an error with any of the data handler functions), but I get an error where the fetch function occurs:

"Uncaught (in promise) Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:

  1. You might have mismatching versions of React and the renderer (such as React DOM)
  2. You might be breaking the Rules of Hooks
  3. You might have more than one copy of React in the same app"

I do not believe I have mismatching versions of React. Here is my package.json:

{
&quot;name&quot;: &quot;frontend&quot;,
&quot;version&quot;: &quot;1.0.0&quot;,
&quot;description&quot;: &quot;&quot;,
&quot;main&quot;: &quot;index.js&quot;,
&quot;scripts&quot;: {
&quot;dev&quot;: &quot;webpack watch --mode development &quot;,
&quot;build&quot;: &quot;webpack --mode production&quot;
},
&quot;keywords&quot;: [],
&quot;author&quot;: &quot;&quot;,
&quot;license&quot;: &quot;ISC&quot;,
&quot;devDependencies&quot;: {
&quot;@babel/core&quot;: &quot;^7.20.7&quot;,
&quot;@babel/preset-env&quot;: &quot;^7.20.2&quot;,
&quot;@babel/preset-react&quot;: &quot;^7.18.6&quot;,
&quot;babel-loader&quot;: &quot;^9.1.0&quot;,
&quot;glob-all&quot;: &quot;^3.3.1&quot;,
&quot;react&quot;: &quot;^18.2.0&quot;,
&quot;react-dom&quot;: &quot;^18.2.0&quot;,
&quot;tailwindcss&quot;: &quot;^3.2.4&quot;,
&quot;webpack&quot;: &quot;^5.75.0&quot;,
&quot;webpack-cli&quot;: &quot;^5.0.1&quot;
},
&quot;dependencies&quot;: {
&quot;@babel/plugin-proposal-class-properties&quot;: &quot;^7.18.6&quot;,
&quot;@emotion/react&quot;: &quot;^11.10.5&quot;,
&quot;@emotion/styled&quot;: &quot;^11.10.5&quot;,
&quot;@mui/icons-material&quot;: &quot;^5.11.0&quot;,
&quot;@mui/material&quot;: &quot;^5.11.3&quot;,
&quot;autoprefixer&quot;: &quot;^10.4.13&quot;,
&quot;css-loader&quot;: &quot;^6.7.3&quot;,
&quot;draft-js&quot;: &quot;^0.11.7&quot;,
&quot;postcss-cli&quot;: &quot;^10.1.0&quot;,
&quot;postcss-loader&quot;: &quot;^7.0.2&quot;,
&quot;react-draft-wysiwyg&quot;: &quot;^1.15.0&quot;,
&quot;react-router-dom&quot;: &quot;^6.6.1&quot;,
&quot;style-loader&quot;: &quot;^3.3.1&quot;
}
}

I have researched this problem and have not been able to find a solution. I have found other ways of building forms in react, but that would change how my backend receives the data. I would prefer to avoid that unless absolutely necessary.

答案1

得分: 0

问题

代码导入了 Navigate 组件并尝试直接调用它。

import { Navigate } from &quot;react-router-dom&quot;;
export default function CreateBlogPost(props) {
  ...

  const handlePostButtonPressed = (requestOptions) =&gt; {
    console.log(requestOptions)
    fetch(&quot;/blog/create&quot;, requestOptions)
      .then((response) =&gt; response.json())
      .then((data) =&gt; Navigate(&#39;/posts/&#39; + data.postid)); // &lt;-- invoked here
  };

  ...

在 React 中,我们不直接调用/调用 React 组件,而是在 React 组件的渲染返回中 以 JSX 形式 渲染它们,即 &lt;Navigate to=&quot;...&quot; /&gt;。通过直接将 React 组件 作为函数 调用,您正在 违反 hooks 规则间接地)。

Navigate 是一个组件,当 渲染 时,会产生一个 声明式 导航操作,即导航到指定的路由路径。我确信您打算导入 useNavigate 钩子来使用 navigate 函数,并在处理程序中执行 命令式 导航操作。

解决方案

导入 useNavigate 并发出命令式导航操作。删除 requestOptions 参数,并使用在函数组件作用域中声明的 requestOptions。这是因为 handlePostButtonPressed 直接传递为事件处理程序。

import { useNavigate } from &quot;react-router-dom&quot;;
export default function CreateBlogPost(props) {
  const navigate = useNavigate();

  ...

  const requestOptions = {
    method: &quot;POST&quot;,
    headers: { &#39;Content-Type&#39;: &#39;application/json&#39; },
    body: JSON.stringify({
      title: title,
      content: content,
      tags: tags,
    })
  };

  const handlePostButtonPressed = () =&gt; {
    console.log(requestOptions)
    fetch(&quot;/blog/create&quot;, requestOptions)
      .then((response) =&gt; response.json())
      .then((data) =&gt; navigate(`/posts/${data.postid}`);
  };

  ...

  &lt;Button
    color=&quot;primary&quot;
    variant=&quot;contained&quot;
    onClick={handlePostButtonPressed}
  &gt;
    Post
  &lt;/Button&gt;

  ...
}
英文:

Issue

The code imports the Navigate component and tries to directly invoke it.

import { Navigate } from &quot;react-router-dom&quot;;
export default function CreateBlogPost(props) {
...
const handlePostButtonPressed = (requestOptions) =&gt; {
console.log(requestOptions)
fetch(&quot;/blog/create&quot;, requestOptions)
.then((response) =&gt; response.json())
.then((data) =&gt; Navigate(&#39;/posts/&#39; + data.postid)); // &lt;-- invoked here
};
...

In React we don't directly call/invoke React components, we render them as JSX in the render return of a React component, i.e. &lt;Navigate to=&quot;...&quot; /&gt;. By directly calling the React component as a function you are breaking the rules of hooks (indirectly).

Navigate is a component that, when rendered, effects a declarative navigate action, i.e. navigates to the specified route path. I'm certain you meant to import the useNavigate hook to use the navigate function and effect an imperative navigation action in the handler.

Solution

Import useNavigate and issue an imperative navigate action. Remove the requestOptions arg and use the requestOptions that is declared in function component scope. This is because handlePostButtonPressed is passed directly as an event handler.

import { useNavigate } from &quot;react-router-dom&quot;;
export default function CreateBlogPost(props) {
const navigate = useNavigate();
...
const requestOptions = {
method: &quot;POST&quot;,
headers: { &#39;Content-Type&#39;: &#39;application/json&#39; },
body: JSON.stringify({
title: title,
content: content,
tags: tags,
})
};
const handlePostButtonPressed = () =&gt; {
console.log(requestOptions)
fetch(&quot;/blog/create&quot;, requestOptions)
.then((response) =&gt; response.json())
.then((data) =&gt; navigate(`/posts/${data.postid}`);
};
...
&lt;Button
color=&quot;primary&quot;
variant=&quot;contained&quot;
onClick={handlePostButtonPressed}
&gt;
Post
&lt;/Button&gt;
...

huangapple
  • 本文由 发表于 2023年1月6日 14:30:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/75027682.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定