#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()
работает просто отлично, как и myuseDispatch
, и другие компоненты, необходимые для создания кода выше. Я почти уверен, что я просто перепутал синтаксис где-то в приведенном выше коде?
Вот мой код 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()
?