-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SOK-16] #8
[SOK-16] #8
Conversation
fcac281
to
4110c13
Compare
ab29ccb
to
80fb9c2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Крутая работа.
Оставил пару моментов, которые меня волнуют.
}, [user]) | ||
|
||
// if the user is full, show page | ||
return user === null ? <h1>Загрузка...</h1> : <Outlet /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Выглядит так, что если запрос не пройдет, то будет бесконечно "Загрузка...". Возможно стоит при ошибке в backendApi перебрасывать на <Error>
, чтобы хотя бы понятно было, что дальше нет смысла ждать.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Он кидает на авторизацию при неудачном запросе, сделаю компонент прелоадера для таких целей.
import { AppDispatch, InferAppActions } from '@/store' | ||
import backendApi from '@/api/backendApi' | ||
|
||
export type UserType = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Может такую штуку использовать?
type Nullable<T> = T | null
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Не стал использовать. Не понимаю в чем преимущество. Поясни
if (data) { | ||
dispatch(actions.setUser(null)) | ||
|
||
window.location.href = '/' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
А нельзя с помощью роутера перебросить, чтобы не ломать spa?
if (data) { | ||
dispatch(actions.setUser(null)) | ||
|
||
window.location.href = '/' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
И еще момент, я не разбираюсь в реакте. Нормально ли, что redux управляет редиректами? Я просто думал, что его задача исключительно управлять состоянием приложения. А тут получается что он начинает реализовывать это состояние.
Или я тут заблуждаюсь?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Наверно не нормально. Переделал, логика в компоненте
if (error.response?.status === 401) { | ||
localStorage.removeItem('user') | ||
// save page where we get 401 and redirect after login | ||
window.location.href = '/sign-in?redirectUrl=' + window.location.pathname |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Может быть можно роутером перебросить?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Переделал на роутер
window.location.href = '/sign-in?redirectUrl=' + window.location.pathname | ||
} | ||
|
||
if (axios.isCancel(error)) return Promise.reject(error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
А есть смысл в этом условии, если все равно возвращается тоже самое?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Убрал
function (error: AxiosError) { | ||
// if app get response code 401 (died token), redirect user to sign-in form | ||
if (error.response?.status === 401) { | ||
localStorage.removeItem('user') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Возможно не стоит давать backendApi возможность манипулировать localStorage`ем? Начинаем как будто смешивать логику запросов и управления состоянием.
Возможно стоит сделать какой-то errorHandler, который опознает ошибку и вызовет нужный контролер.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Манипуляцию localstorage уберу
if (error.response?.status === 401) { | ||
localStorage.removeItem('user') | ||
// save page where we get 401 and redirect after login | ||
window.location.href = '/sign-in?redirectUrl=' + window.location.pathname |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
И возможно тоже смешивается логика. Теперь получается, что backendApi управляет роутами и локал сторажем. Как будто много на себя берет, не?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Он берет на себя одну функцию - отловить 401 (что говорит о том что авторизация слетела) и что-то сделать. Подумаю как оформить
href?: string | ||
className?: string | undefined | ||
useFixWidth?: boolean | undefined | ||
href?: string | undefined |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Не будет чуть путацины с href`ом для кнопки? Вроде ожидается, что кнопка делает экшены, а ссылки перебрасывают.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Тут ты наверно прав. Уберу href оставлю только событие
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Хороший поинт от @k0ndratov 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Привет! Отличная работа! Сейчас попробую затянуть к себе и сделать кнопку "профиль" в шапке, с именем пользователя ))
У меня пара комментов:
- Давай запретим посещение страниц, авторизации и регистрации авторизованным пользователям.
- И я бы убрал ненужные комментарии в коде, пример ниже.
// if user is empty call backend
if (user === null) { dispatch(getUser()) }
useEffect(() => { | ||
// if user is empty call backend | ||
if (user === null) { | ||
dispatch(getUser()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Дважды улетает только в dev из-за strict mode
https://legacy.reactjs.org/docs/strict-mode.html
}, [user]) | ||
|
||
// if the user is full, show page | ||
return user === null ? <h1>Загрузка...</h1> : <Outlet /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Он кидает на авторизацию при неудачном запросе, сделаю компонент прелоадера для таких целей.
if (error.response?.status === 401) { | ||
localStorage.removeItem('user') | ||
// save page where we get 401 and redirect after login | ||
window.location.href = '/sign-in?redirectUrl=' + window.location.pathname |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Чуть смущает camelCase в гет параметрах. Это не правка, просто коммент.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Убрал вообще этот код. Сделал иначе
bc559fc
to
9960d7c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ты молодец. Большая работа и это действительно сложная задачка. Описал всякие нюансы, как можно сделать лучше, но критических замечаний нет.
Вообще глобальная рекомендация:
- Из реакт компонента кидать один экшн
dispatch(login())
, например, а в санке уже ловить этот экшн и отправлять запросы на сервер там. Так логика будет чище. - Ну и меньше копипасты и больше обобщений!
package-lock.json
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Интересно, что у вас в проекте есть и package-lock, и yarn.lock. Обычно используется что-то одно из двух. Возможно, у вас в команде кто-то устанавливает зависимости через npm install
, а кто-то через yarn
. Нужно разобраться с этим придти к единообразию.
import axios from 'axios' | ||
|
||
const instance = axios.create({ | ||
baseURL: import.meta.env.VITE_AUTH_URL, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Круто, что такие штуки унесены в переменные окружения 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Отдельный лайк за абстракцию в виде backendApi
, а не использование чистого axios
во всех местах
/* eslint-disable */ | ||
import App from './App' | ||
import { render, screen } from '@testing-library/react' | ||
// import App from './App' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
А что тут такое? Почему ты закоментил? Этот тест сейчас не запускается никогда?
href?: string | ||
className?: string | undefined | ||
useFixWidth?: boolean | undefined | ||
href?: string | undefined |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Хороший поинт от @k0ndratov 👍
useFixWidth?: boolean | undefined | ||
href?: string | undefined | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
onClick?: (() => Promise<void>) | (() => any) | undefined |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ESlint не зря ругается. Лучше вместо any использовать unknown
, а ещё лучше чётко прописывать нужный тип.
Пытаюсь понять, а зачем три типа подаёшь? Смотри, так как у тебя после onClick
идёт вопросительный знак, значит тип undefined
можно смело удалить, останется два, но всё равно не пойму, зачем тут промис.
Ты просто кидаешь в button функцию onClick. Нативная кнопка промис не возвращает.
Нужно исправить это Вообще любой такой onClick обычно имеет тип (): void
или такой же, только с параметром event
внутри
</label> | ||
<Button | ||
text={'зарегистрироваться'} | ||
useFixWidth={true} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
useFixWidth={true} | |
useFixWidth |
Можно просто без true
packages/client/src/store/index.ts
Outdated
import { combineReducers } from '@reduxjs/toolkit' | ||
|
||
const rootReducer = combineReducers({ | ||
AuthReducer, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Можно лучше: обычно пишут с маленькой буквы:
AuthReducer, | |
authReducer: AuthReducer, |
export type RootState = ReturnType<typeof store.getState> | ||
// Inferred type: {posts: PostsState, comment: CommentsState, users: UsersState} | ||
export type AppDispatch = typeof store.dispatch | ||
export const useAppDispatch = useDispatch.withTypes<AppDispatch>() | ||
export type InferAppActions<T> = T extends { | ||
[keys: string]: (...args: unknown[]) => infer U | ||
} | ||
? U | ||
: never |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Воу! прикольно! 👍
} | ||
|
||
const initialState = { | ||
user: null as unknown as UserType, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
так обычно плохо делать. Лучше создать тип:
type InitialState = { user: UserType | null }
const initialState: InitialState = { ... }
) | ||
|
||
export type ActionsType = InferAppActions<typeof actions> | ||
export default AuthReducer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Можно лучше: Все санки похожи между собой. Рекомендую написать базовый санк, который ба содержал в себе обработку ошибок, и потом его использовать во всех местах.
+add backendApi
+create axios interceptors
+add userReducer and create function for use yandex api +add env
*update SignIn.tsx for use backendApi
*update SignUp.tsx for use backendApi
*format and lint
Какую задачу решаем