反应 - 错误:无效的钩子调用。钩子只能在函数组件的主体内部调用。我的语法有什么问题?

时间:2021-02-05 01:55:42

标签: javascript reactjs material-ui

提供一点背景:

我正在尝试构建一个登录表单页面功能来检测用户是否已经登录。如果他们已登录,则会出现一个按钮,提示用户注销。像这样:user 如果 user 为 True,则按钮出现,如果 × 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. 为 false,则他们收到用于登录身份验证的用户名和密码表单。

我已经确定了所有功能。 但是,当我按下按钮以注销用户退出时,我收到此错误:

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 } }; 组件:

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


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

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

我的 index.js:

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.

既然我已经更改了它,这是否是我的索引路由器的正确路径?

新错误:

cols <- c('FinalSalary','FinalDCBalance','FinalAge','FinalSSLow', 'FinalSSHigh')
new_cols <- paste(cols, trial, sep = '_')
ptptdata[new_cols] <- ptptdata[cols]

2 个答案:

答案 0 :(得分:1)

组件基本上是函数引用,您正在调用它们,因此与其执行 SignIn(),不如尝试 <SignIn />

编辑:并使 userLoggedInAlert 以大写开头,然后全部放在一起:

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

答案 1 :(得分:1)

当您在 Logout 处调用 onClick 时,您正在从组件主体外部的函数 Logout 调用钩子,这是不允许的。

您可以将 Logout 逻辑抽象为返回 logoutUser 函数的自定义钩子:

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>
);