Home > database >  React Redux-Toolkit: Invalid hook call - Hooks can only be called inside of the body of a function c
React Redux-Toolkit: Invalid hook call - Hooks can only be called inside of the body of a function c

Time:05-29

I'm trying to build my first react app, and I'm following some standart libraries and their quick start quides but for the react-redux and @reduxjs/toolkit, there's this error that I couldn't figure out why it shows in the first place:

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.

Relevant files:

../../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 >
                            <div >
                                <div >
                                    <div >
                                        <h1>appName</h1>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div >
                            <div >
                                <div >
                                    <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 >
                                        <input
                                            id="login-name"
                                            type="text"
                                            name="loginName"
                                            required
                                            data-msg="Lütfen adınızı adresinizi eksiksiz bir şekilde giriniz."
                                            
                                            value={Name}
                                            onChange={(e) => setName(e.target.value)}/>
                                        <label for="login-name" >Ad</label>
                                    </div>
                                    <div >
                                        <input
                                            id="login-surname"
                                            type="text"
                                            name="loginSurname"
                                            required
                                            data-msg="Lütfen soyadınızı adresinizi eksiksiz bir şekilde giriniz."
                                            
                                            value={Surname}
                                            onChange={(e) => setSurname(e.target.value)}/>
                                        <label for="login-surname" >Soyad</label>
                                    </div>
                                    <div >
                                        <input
                                            id="login-userName"
                                            type="text"
                                            name="loginUsername"
                                            required
                                            data-msg="Lütfen kullanıcı adınızı adresinizi eksiksiz bir şekilde giriniz."
                                            
                                            value={Username}
                                            onChange={(e) => setUsername(e.target.value)}/>
                                        <label for="login-userName" >Kullanıcı adı</label>
                                    </div>
                                    <div >
                                        <input
                                            id="login-mail"
                                            type="text"
                                            name="loginMailAddress"
                                            required
                                            data-msg="Lütfen e-mail adresinizi eksiksiz bir şekilde giriniz."
                                            
                                            value={MailAddress}
                                            onChange={(e) => setMailAddress(e.target.value)}/>
                                        <label for="login-mail" >Mail Adresi</label>
                                    </div>
                                    <div >
                                        <input
                                            id="login-password"
                                            type="password"
                                            name="loginPassword"
                                            required
                                            data-msg="Şifrenizi eksiksiz olarak giriniz."
                                            
                                            value={Password}
                                            onChange={(e) => setPassword(e.target.value)}/>
                                        <label for="login-password" >Ş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"
  }
}

For the register.js file, when I add the dispatch() method on button.onClick directly like this, it doesn't give the error:

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

But I need to use the dispatch method inside my custom function, as can be seen on their quick start guide.

I tried this but it didn't work. Also, as far as I know so far, I am using it inside a functional component.

What am I doing wrong here?

CodePudding user response:

Your error is caused by this

Navigate({ to: "/" });

This is the incorrect way to use Navigate. I think based on your code you want to use the hook useNavigate() instead

Also your redux is incorrect (not causing your error but...)

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

user would always be undefined because in your reducer you don't have a state called loggedInUser you only have user that points to your userReducer.

This should give you what you want:

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