React Redux-Toolkit:无效的挂钩调用 - 只能在函数组件的主体内部调用挂钩

React Redux-Toolkit: Invalid hook call - Hooks can only be called inside of the body of a function component

我正在尝试构建我的第一个 React 应用程序,并且我正在关注一些标准库及其快速入门指南,但是对于 react-redux@reduxjs/toolkit,有一个我无法解决的错误'弄清楚为什么它首先显示:

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 
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.

相关文件:

../../redux/stateSlices/user.slice

import { createSlice } from "@reduxjs/toolkit";

export const userSlice = createSlice({
    name: "user",
    initialState: {
        loggedInUser: null
    },
    reducers: {
        logToConsole: (state, action) => {
            console.log("tryout", state, action.payload);
        }
    }
});

export const {
    logToConsole
} = userSlice.actions;

export default userSlice.reducer;

../../redux/store

import { configureStore } from '@reduxjs/toolkit';
import userReducer from './stateSlices/user.slice';

export const store = configureStore({
    reducer: {
        user: userReducer
    }
});

./pages/register/register

import React, { useState } from 'react'
import { 
    signIn,
    createUser,
    deleteUser
} from '../../services/user.service';
import { useDispatch, useSelector } from 'react-redux';
import { Navigate, Link } from 'react-router-dom';
import { 
  logToConsole
} from '../../redux/stateSlices/user.slice';

export function Register() {
    const [errorMessage, setErrorMessage] = useState('');
    const [Username, setUsername] = useState('');
    const [MailAddress, setMailAddress] = useState('');
    const [Password, setPassword] = useState('');
    const [Name, setName] = useState('');
    const [Surname, setSurname] = useState('');
    const dispatch = useDispatch();
    const user = useSelector((state) => state.loggedInUser);

    const RegisterUser = (e) =>
    {
        e.preventDefault();
        try
        {
            console.log("user", user);
            if(/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(MailAddress))
            {

                var createUserResponse = createUser(
                    Username,
                    MailAddress,
                    Name,
                    Surname,
                    Password,
                    Username === 'admin' ? 999999999 : 0
                );
     
                if(createUserResponse.Status)
                {
                    dispatch(logToConsole(1));
    
                    Navigate({ to: "/" });
                }
                else
                {
                    if(createUserResponse.ErrorList)
                    {
                        setErrorMessage(createUserResponse.ErrorList.join(','));
                    }
                    else
                    {
                        setErrorMessage("Kayıt işlemi sırasında beklenmedik bir hata yaşandı, daha sonra tekrar deneyiniz.");
                    }
                }
            }
            else
            {
                setErrorMessage("Geçersiz mail adresi.");
            }
        }
        catch(err)
        {
            if(err.code === 'duplicateValue')
            {
                setErrorMessage(err.ErrorList.join(','));
            }
            else
            {
                deleteUser(MailAddress);
                setErrorMessage(err.message);
            }
        }
    }

    return (
        <div className="login-page">
            <div className='container d-flex align-items-center'>
                <div className='form-holder has-shadow'>
                    <div className="row">
                        <div class="col-lg-6">
                            <div class="info d-flex align-items-center">
                                <div class="content">
                                    <div class="logo">
                                        <h1>appName</h1>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div class="col-lg-6 bg-white">
                            <div class="form d-flex align-items-center">
                                <div class="content">
                                    <div className="col-12 text-white ff-arial text-center"><h3>Kaydol</h3></div>

                                {
                                    errorMessage &&
                                    <span className='col-12 text-white pl-0 pb-3 m-0'> { errorMessage } </span>
                                }
                                    <div class="form-group">
                                        <input
                                            id="login-name"
                                            type="text"
                                            name="loginName"
                                            required
                                            data-msg="Lütfen adınızı adresinizi eksiksiz bir şekilde giriniz."
                                            class="input-material"
                                            value={Name}
                                            onChange={(e) => setName(e.target.value)}/>
                                        <label for="login-name" class="label-material">Ad</label>
                                    </div>
                                    <div class="form-group">
                                        <input
                                            id="login-surname"
                                            type="text"
                                            name="loginSurname"
                                            required
                                            data-msg="Lütfen soyadınızı adresinizi eksiksiz bir şekilde giriniz."
                                            class="input-material"
                                            value={Surname}
                                            onChange={(e) => setSurname(e.target.value)}/>
                                        <label for="login-surname" class="label-material">Soyad</label>
                                    </div>
                                    <div class="form-group">
                                        <input
                                            id="login-userName"
                                            type="text"
                                            name="loginUsername"
                                            required
                                            data-msg="Lütfen kullanıcı adınızı adresinizi eksiksiz bir şekilde giriniz."
                                            class="input-material"
                                            value={Username}
                                            onChange={(e) => setUsername(e.target.value)}/>
                                        <label for="login-userName" class="label-material">Kullanıcı adı</label>
                                    </div>
                                    <div class="form-group">
                                        <input
                                            id="login-mail"
                                            type="text"
                                            name="loginMailAddress"
                                            required
                                            data-msg="Lütfen e-mail adresinizi eksiksiz bir şekilde giriniz."
                                            class="input-material"
                                            value={MailAddress}
                                            onChange={(e) => setMailAddress(e.target.value)}/>
                                        <label for="login-mail" class="label-material">Mail Adresi</label>
                                    </div>
                                    <div class="form-group">
                                        <input
                                            id="login-password"
                                            type="password"
                                            name="loginPassword"
                                            required
                                            data-msg="Şifrenizi eksiksiz olarak giriniz."
                                            class="input-material"
                                            value={Password}
                                            onChange={(e) => setPassword(e.target.value)}/>
                                        <label for="login-password" class="label-material">Şifre</label>
                                    </div>
                                    <button className='btn btn-block btn-primary' onClick={(e) => RegisterUser(e)}>Kaydol</button>
                                    <div className="col-12 mt-3 text-center text-white">
                                        <span>veya <Link to="/login" className='ml-1'>Giriş Yap</Link></span>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    )
}

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {
  BrowserRouter,
  Routes,
  Route
} from 'react-router-dom';
import { store } from './redux/store';
import { Provider } from 'react-redux';
import { Register } from './pages/register/register';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <Provider store={store}>
    <BrowserRouter>
        <Routes>
          <Route path='/' element={<App/>}>
            <Route path='/' element={<Index/>}></Route>
          </Route>
          <Route path='register' element={<Register/>} ></Route>
        </Routes>
    </BrowserRouter>
  </Provider>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint.

package.json

{
  "name": "appName",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@reduxjs/toolkit": "^1.8.2",
    "@testing-library/jest-dom": "^5.16.4",
    "@testing-library/react": "^13.2.0",
    "@testing-library/user-event": "^13.5.0",
    "i": "^0.3.7",
    "react-redux": "^8.0.2",
    "react-router": "^6.3.0",
    "react-router-dom": "^6.3.0",
    "react-scripts": "5.0.1",
    "redux": "^4.2.0",
    "web-vitals": "^2.1.4"
  },
  "peerDependencies": {
    "react": "^18.1.0",
    "react-dom": "^18.1.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "eslint-plugin-react-hooks": "^4.5.0"
  }
}

对于register.js文件,当我直接在button.onClick上添加dispatch()方法时,它没有报错:

<button className='btn btn-block btn-primary' onClick={(e) => dispatch(logToConsole(1))}>Kaydol</button>

但我需要在我的自定义函数中使用 dispatch 方法,如他们的快速启动中所示 guide

我试过 但没用。另外,据我所知,我在功能组件中使用它。

我做错了什么?

您的错误是由此造成的

Navigate({ to: "/" });

这是错误的使用方式Navigate。我认为根据您的代码,您想使用挂钩 useNavigate() 而不是

你的 redux 也不正确(不是你的错误,而是......)

const user = useSelector((state) => state.loggedInUser); 

user 总是未定义的,因为在你的 reducer 中你没有一个叫做 loggedInUser 的状态,你只有 user 指向你的 userReducer。

这应该能满足您的需求:

const user = useSelector((state) => state.user.loggedInUser);