簡體   English   中英

React - 錯誤:無效的鈎子調用。 鈎子只能在 function 組件的主體內部調用。 我的語法有什么問題?

[英]React - Error: Invalid hook call. Hooks can only be called inside of the body of a function component. What is wrong my syntax?

提供一點背景:

我正在嘗試構建一個登錄表單頁面 function 來檢測用戶是否已經登錄。 如果他們已登錄,則會出現一個按鈕,提示用戶注銷。 像這樣: {user? userLoggedInAlert(): SignIn()} {user? userLoggedInAlert(): SignIn()}如果user為真,則顯示按鈕,如果user為假,則他們收到用於登錄驗證的用戶名和密碼表單。

我已經確定了所有功能。 但是,當我按下按鈕以注銷用戶時,我收到此錯誤:

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

這是我的代碼,我認為const userLoggedInAlert有問題

import React from 'react';
import { useSelector } from 'react-redux';
import { selectUser } from '../../features/userSlice';
import SignIn from '../auth/Login';
import Container from '@material-ui/core/Container';
import { Button } from '@material-ui/core';
import { logout } from "../../features/userSlice";
import { NavLink } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import LogOut from '../auth/Logout';



function LoginUser()
{
    const user = useSelector(selectUser);

    const dispatch = useDispatch();

    const handleLogout = (e) =>
    {
        if(e) e.preventDefault();

        dispatch(logout());
    };

    const userLoggedInAlert = () =>
    {
        return <Container>
            <span>
                You are already logged in!
            </span>
            <Button
                component={NavLink}
                variant="contained"
                color="primary"
                to="/logout" 
                onClick={function(){ LogOut(); handleLogout()}}
            >
                Do you want to logout?
            </Button>

              </Container>
        };

    return (
        <Container>
            {user ? userLoggedInAlert() : SignIn()}
        </Container>
    );
}
export default LoginUser;
  • SignIn()工作得很好,我的useDispatch和創建上述代碼所需的其他組件也是如此。 我很確定我只是弄亂了上面代碼中某處的語法?

這是我的LogOut()代碼,請注意,我可以使用這個完全相同的組件通過其他方式很好地注銷。

import React, { useEffect } from 'react';
import axiosInstance from '../../axios';
import { useHistory } from 'react-router-dom';

export default function LogOut() {
    
    const history = useHistory();


    useEffect(() => {
        const response = axiosInstance.post('user/logout/blacklist/', {
            refresh_token: localStorage.getItem('refresh_token'),
        });
        localStorage.removeItem('access_token');
        localStorage.removeItem('refresh_token');
        axiosInstance.defaults.headers['Authorization'] = null;
        history.push('/login');
    });
    return <div>Logout</div>;
};

感謝您的幫助,我被困在這里。

編輯:

  "dependencies": {
    "@material-ui/core": "^4.11.2",
    "@material-ui/icons": "^4.11.2",
    "@material-ui/lab": "^4.0.0-alpha.57",
    "@reduxjs/toolkit": "^1.5.0",
    "@testing-library/jest-dom": "^5.11.6",
    "@testing-library/react": "^11.2.2",
    "@testing-library/user-event": "^12.6.0",
    "axios": "^0.21.1",
    "chart.js": "^2.9.4",
    "react": "^17.0.1",
    "react-chartjs-2": "^2.11.1",
    "react-dom": "^17.0.1",
    "react-facebook-login": "^4.1.1",
    "react-hook-form": "^6.14.2",
    "react-redux": "^7.2.2",
    "react-router-dom": "^5.2.0",
    "react-scripts": "4.0.1",
    "redux": "^4.0.5",
    "redux-persist": "^6.0.0",
    "web-vitals": "^0.2.4"
  },

編輯編輯:我正在努力實施建議的更改,我現在遇到了一些讓我感到困惑的錯誤:

Uncaught Error: Objects are not valid as a React child (found: object with keys {logoutUser}). If you meant to render a collection of children, use an array instead.

上述錯誤發生在<useLogOut>組件中。

這是我的實現方式: LoginPortal.js

import React from 'react';
import { useSelector } from 'react-redux';
import { selectUser } from '../../features/userSlice';
import SignIn from '../auth/Login';
import Container from '@material-ui/core/Container';
import { Button } from '@material-ui/core';
import { logout } from "../../features/userSlice";
import { NavLink } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import useLogOut from '../auth/Logout';

function LoginUser()
{
    const user = useSelector(selectUser);
    // use hook at component body extracting logoutUser method
    const { logoutUser } = useLogOut();

    const dispatch = useDispatch();

    const handleLogout = (e) =>
    {
        if(e) e.preventDefault();

        dispatch(logout());
    };

    const UserLoggedInAlert = () =>
    {
        return <Container>
            <span>
                You are already logged in!
            </span>
            <Button
                component={NavLink}
                variant="contained"
                color="primary"
                to="/logout"
                // here now you can save logout user since no hooks are being called
                onClick={function(){ logoutUser(); handleLogout()}}
            >
                Do you want to logout?
            </Button>

              </Container>
        };

    return (
        <Container>
            {user ? <UserLoggedInAlert/> : <SignIn/> }
        </Container>
    );
}
export default LoginUser;

Logout.js組件:

import axiosInstance from '../../axios';
import { useHistory } from 'react-router-dom';

export default function useLogOut() {
    
    const history = useHistory();
  
    // we don't useEffect here, we are only interested in function logoutUser
  
    const logoutUser = () => {
        const response = axiosInstance.post('user/logout/blacklist/', {
            refresh_token: localStorage.getItem('refresh_token'),
        });
        localStorage.removeItem('access_token');
        localStorage.removeItem('refresh_token');
        axiosInstance.defaults.headers['Authorization'] = null;
        history.push('/login');
      }
  
    return { logoutUser }
  };

我的 index.js:

import useLogOut from './components/auth/Logout';


const routing = (
    <Router>
                <Route path="/logout" component={useLogOut} />
    </Router>
);

ReactDOM.render(routing, document.getElementById('root'));

既然我已經改變了它,這將是我的索引路由器的正確路徑嗎?

新錯誤:

Error: Objects are not valid as a React child (found: object with keys {logoutUser}). If you meant to render a collection of children, use an array instead.

當您在onClick調用Logout時,您正在從 function 調用鈎子 在組件主體之外Logout ,這是不允許的。

您可以將 Logout 邏輯抽象為自定義鈎子,在該鈎子中返回logoutUser function:

export default function useLogOut() {
    
  const history = useHistory();

  // we don't useEffect here, we are only interested in function logoutUser

  const logoutUser = () => {
      const response = axiosInstance.post('user/logout/blacklist/', {
          refresh_token: localStorage.getItem('refresh_token'),
      });
      localStorage.removeItem('access_token');
      localStorage.removeItem('refresh_token');
      axiosInstance.defaults.headers['Authorization'] = null;
      history.push('/login');
    }

  return { logoutUser }
};

然后你在你的LoginUser組件主體中使用你的自定義鈎子,提取logoutUser方法:

function LoginUser()
{
    const user = useSelector(selectUser);
    // use hook at component body extracting logoutUser method
    const { logoutUser } = useLogOut();

    const dispatch = useDispatch();

    const handleLogout = (e) =>
    {
        if(e) e.preventDefault();

        dispatch(logout());
    };

    const UserLoggedInAlert = () =>
    {
        return <Container>
            <span>
                You are already logged in!
            </span>
            <Button
                component={NavLink}
                variant="contained"
                color="primary"
                to="/logout"
                // here now you can safely logout user since no hooks are being called
                onClick={function(){  handleLogout(); logoutUser()}}
            >
                Do you want to logout?
            </Button>

              </Container>
        };

    return (
        <Container>
            {user ? <UserLoggedInAlert/> : <SignIn/> }
        </Container>
    );
}
export default LoginUser;

您可以領先一步並簡化您的LogOut組件:

export default function LogOut() {
    
  const { logoutUser } = useLogOut();

  useEffect(() => {
    logoutUser()
  });
  return <div>Logout</div>;
};

編輯

為簡單起見,您的 Logout.js 執行以下操作:

export const useLogOut = () => {
    
  const history = useHistory();

  // we don't useEffect here, we are only interested in function logoutUser

  const logoutUser = () => {
      const response = axiosInstance.post('user/logout/blacklist/', {
          refresh_token: localStorage.getItem('refresh_token'),
      });
      localStorage.removeItem('access_token');
      localStorage.removeItem('refresh_token');
      axiosInstance.defaults.headers['Authorization'] = null;
      history.push('/login');
    }

  return { logoutUser }
};

export default function LogOut() {

  const { logoutUser } = useLogOut();

  useEffect(() => {
    logoutUser()
  });
  return <div>Logout</div>;
};

在您的登錄處,您使用大括號導入鈎子:

import { useLogOut } from '../auth/Logout';

並在沒有大括號的 index.js 處導入組件:

import LogOut from './components/auth/Logout';


const routing = (
    <Router>
                <Route path="/logout" component={LogOut} />
    </Router>
);

組件基本上是 function 引用,並且您正在調用它們,因此不要執行SignIn() ,而是嘗試<SignIn />

編輯:並使userLoggedInAlert以大寫開頭,然后全部一起:

{user? <UserLoggedInAlert />: <SignIn />}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM