Реакция — ошибка: недопустимый вызов перехвата. Перехваты могут вызываться только внутри тела функционального компонента. Что не так с моим синтаксисом?

#javascript #reactjs #material-ui

#javascript #reactjs #материал-пользовательский интерфейс

Вопрос:

Чтобы дать немного предыстории:

Я пытаюсь создать функцию страницы формы входа, которая определяет, зарегистрирован ли пользователь. Если они вошли в систему, появится кнопка, предлагающая пользователю выйти из системы. Вот так: {user ? userLoggedInAlert() : SignIn()} если user True, то появляется кнопка, если user false, то они получают форму имени пользователя и пароля для аутентификации при входе.

Я прибил всю функциональность. Однако, когда я нажимаю кнопку on для выхода пользователя из системы, я получаю эту ошибку:

 ×
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() работает просто отлично, как и my 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>;
};
 

Спасибо за любую помощь, я застрял здесь.

EDIT:

   "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"
  },
 

EDIT EDIT:
I’m struggling to implement the recommended changes, I’m now getting some more errors that are making me confused:

 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.
 

The above error occurred in the <useLogOut> component.

Here is how I’m implementing:
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.
 

Ответ №1:

когда вы вызываете Logout at onClick , вы вызываете перехваты из своей функции 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>
);
 

Комментарии:

1. Привет, Бузатто, большое спасибо за то, что показал мне, как исправить ошибку, и ваши рекомендации по кодированию. Я пытался их реализовать, но столкнулся с некоторыми ошибками. Не могли бы вы, пожалуйста, проверить мою новую ПРАВКУ в моем исходном сообщении?

2. В дополнение к новым ошибкам, приведенным выше, я также получаю это: Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

3. вы должны передать свой компонент <Route path="/logout" component={LogOut} /> , а не перехват. сам перехват не является компонентом

4. вторая ошибка, вероятно handleLogout() , вызывается после history.push . не уверен, почему у вас есть два действия выхода из системы, но я думаю, если вы измените порядок function(){ handleLogout(), logoutUser()} , это решит вашу проблему

5. вы создаете другой файл с помощью перехвата useLogOut и поддерживаете LogOut компонент. вы также можете использовать перехват useLogOut внутри LogOut , как я описал в конце.

Ответ №2:

Компоненты — это в основном ссылки на функции, и вы вызываете их, поэтому вместо того, чтобы делать SignIn() , попробуйте <SignIn />

редактировать: и userLoggedInAlert начать с верхнего регистра, затем все вместе:

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

Комментарии:

1. Спасибо, Деда, за помощь, я внес изменения, которые вы рекомендовали, и спасибо, что поймали ошибку в нижнем регистре. Однако я все еще получаю ту же ошибку: Error: Invalid hook call. Hooks can only be called inside of the body of a function component.

2. Я добавил немного больше деталей к своему исходному сообщению, показал весь импорт, который я использую для текущего файла, и зависимости для моего React.

3.опять же, вы вызываете здесь компонент, onClick={function(){ LogOut(); handleLogout()}} LogOut это компонент

4. Не совсем уверен, что понимаю, по словам Баззато, проблема заключается не только в этом.

5. Хорошо, простой вопрос, почему вы звоните LogOut() ?