Refactor context dependencies

This commit is contained in:
IRBorisov 2023-08-11 17:36:09 +03:00
parent df4dc7bf23
commit b9d0dd4c20
4 changed files with 43 additions and 46 deletions

View File

@ -2,14 +2,15 @@ import { createContext, useCallback, useContext, useLayoutEffect, useState } fro
import { type ErrorInfo } from '../components/BackendError'; import { type ErrorInfo } from '../components/BackendError';
import useLocalStorage from '../hooks/useLocalStorage'; import useLocalStorage from '../hooks/useLocalStorage';
import { type DataCallback, getAuth, postLogin, postLogout, postSignup } from '../utils/backendAPI'; import { type DataCallback, getAuth, patchPassword,postLogin, postLogout, postSignup } from '../utils/backendAPI';
import { ICurrentUser, IUserLoginData, IUserProfile, IUserSignupData } from '../utils/models'; import { ICurrentUser, IUserLoginData, IUserProfile, IUserSignupData, IUserUpdatePassword } from '../utils/models';
interface IAuthContext { interface IAuthContext {
user: ICurrentUser | undefined user: ICurrentUser | undefined
login: (data: IUserLoginData, callback?: DataCallback) => void login: (data: IUserLoginData, callback?: DataCallback) => void
logout: (callback?: DataCallback) => void logout: (callback?: DataCallback) => void
signup: (data: IUserSignupData, callback?: DataCallback<IUserProfile>) => void signup: (data: IUserSignupData, callback?: DataCallback<IUserProfile>) => void
updatePassword: (data: IUserUpdatePassword, callback?: () => void) => void
reload: (callback?: () => void) => void reload: (callback?: () => void) => void
loading: boolean loading: boolean
error: ErrorInfo error: ErrorInfo
@ -85,13 +86,28 @@ export const AuthState = ({ children }: AuthStateProps) => {
}); });
} }
const updatePassword = useCallback(
(data: IUserUpdatePassword, callback?: () => void) => {
setError(undefined);
patchPassword({
data: data,
showError: true,
setLoading: setLoading,
onError: error => { setError(error); },
onSuccess: () => {
setUser(undefined);
reload();
if (callback) callback();
}});
}, [setUser, reload]);
useLayoutEffect(() => { useLayoutEffect(() => {
reload(); reload();
}, [reload]) }, [reload])
return ( return (
<AuthContext.Provider <AuthContext.Provider
value={{ user, login, logout, signup, loading, error, reload, setError }} value={{ user, login, logout, signup, loading, error, reload, setError, updatePassword }}
> >
{children} {children}
</AuthContext.Provider> </AuthContext.Provider>

View File

@ -1,9 +1,8 @@
import { createContext, useCallback, useContext, useEffect, useState } from 'react'; import { createContext, useCallback, useContext, useEffect, useState } from 'react';
import { ErrorInfo } from '../components/BackendError'; import { ErrorInfo } from '../components/BackendError';
import { DataCallback, getProfile, patchPassword,patchProfile } from '../utils/backendAPI'; import { DataCallback, getProfile, patchProfile } from '../utils/backendAPI';
import { IUserProfile, IUserUpdateData, IUserUpdatePassword } from '../utils/models'; import { IUserProfile, IUserUpdateData } from '../utils/models';
import { useAuth } from './AuthContext';
interface IUserProfileContext { interface IUserProfileContext {
user: IUserProfile | undefined user: IUserProfile | undefined
@ -12,7 +11,6 @@ interface IUserProfileContext {
error: ErrorInfo error: ErrorInfo
setError: (error: ErrorInfo) => void setError: (error: ErrorInfo) => void
updateUser: (data: IUserUpdateData, callback?: DataCallback<IUserProfile>) => void updateUser: (data: IUserUpdateData, callback?: DataCallback<IUserProfile>) => void
updatePassword: (data: IUserUpdatePassword, callback?: () => void) => void
} }
const ProfileContext = createContext<IUserProfileContext | null>(null); const ProfileContext = createContext<IUserProfileContext | null>(null);
@ -36,7 +34,6 @@ export const UserProfileState = ({ children }: UserProfileStateProps) => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [processing, setProcessing] = useState(false); const [processing, setProcessing] = useState(false);
const [error, setError] = useState<ErrorInfo>(undefined); const [error, setError] = useState<ErrorInfo>(undefined);
const auth = useAuth()
const reload = useCallback( const reload = useCallback(
() => { () => {
@ -67,29 +64,13 @@ export const UserProfileState = ({ children }: UserProfileStateProps) => {
}, [setUser] }, [setUser]
); );
const updatePassword = useCallback(
(data: IUserUpdatePassword, callback?: () => void) => {
setError(undefined);
patchPassword({
data: data,
showError: true,
setLoading: setProcessing,
onError: error => { setError(error); },
onSuccess: () => {
setUser(undefined);
auth.reload();
if (callback) callback();
}});
}, [setUser, auth]
);
useEffect(() => { useEffect(() => {
reload(); reload();
}, [reload]); }, [reload]);
return ( return (
<ProfileContext.Provider <ProfileContext.Provider
value={{user, updateUser, updatePassword, error, loading, setError, processing}} value={{user, updateUser, error, loading, setError, processing}}
> >
{children} {children}
</ProfileContext.Provider> </ProfileContext.Provider>

View File

@ -1,31 +1,34 @@
import { useState } from 'react'; import { useMemo, useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import BackendError from '../../components/BackendError';
import TextInput from '../../components/Common/TextInput'; import TextInput from '../../components/Common/TextInput';
import { useUserProfile } from '../../context/UserProfileContext'; import { useAuth } from '../../context/AuthContext';
import { IUserUpdatePassword } from '../../utils/models'; import { IUserUpdatePassword } from '../../utils/models';
export function ChangePassword() { export function ChangePassword() {
const { updatePassword, processing } = useUserProfile(); const { updatePassword, error, loading } = useAuth();
const [old_password, setOldPassword] = useState(''); const [oldPassword, setOldPassword] = useState('');
const [new_password, setNewPassword] = useState(''); const [newPassword, setNewPassword] = useState('');
const [new_password_repeat, setNewPasswordRepeat] = useState(''); const [newPasswordRepeat, setNewPasswordRepeat] = useState('');
const [new_pass_color, setNewPassColor] = useState('');
const navigate = useNavigate(); const navigate = useNavigate();
const colorClass = useMemo(() => {
return !!newPassword && !!newPasswordRepeat && newPassword !== newPasswordRepeat ? 'bg-red-500' : '';
}, [newPassword, newPasswordRepeat]);
function handleSubmit(event: React.FormEvent<HTMLFormElement>) { function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault(); event.preventDefault();
if (new_password !== new_password_repeat) { if (newPassword !== newPasswordRepeat) {
setNewPassColor('bg-red-500');
toast.error('Пароли не совпадают'); toast.error('Пароли не совпадают');
} }
else { else {
const data: IUserUpdatePassword = { const data: IUserUpdatePassword = {
old_password: old_password, old_password: oldPassword,
new_password: new_password, new_password: newPassword,
}; };
updatePassword(data, () => {toast.success('Изменения сохранены'); navigate('/login')}); updatePassword(data, () => {toast.success('Изменения сохранены'); navigate('/login')});
} }
@ -37,36 +40,34 @@ export function ChangePassword() {
<TextInput id='old_password' <TextInput id='old_password'
type='password' type='password'
label='Введите старый пароль:' label='Введите старый пароль:'
value={old_password} value={oldPassword}
onChange={event => setOldPassword(event.target.value)} onChange={event => setOldPassword(event.target.value)}
/> />
<TextInput id='new_password' <TextInput id='new_password'
colorClass={new_pass_color} colorClass={colorClass}
label="Введите новый пароль:" label="Введите новый пароль:"
value={new_password} value={newPassword}
onChange={event => { onChange={event => {
setNewPassword(event.target.value); setNewPassword(event.target.value);
setNewPassColor('');
}} }}
/> />
<TextInput id='new_password_repeat' <TextInput id='new_password_repeat'
colorClass={new_pass_color} colorClass={colorClass}
label="Повторите новый пароль:" label="Повторите новый пароль:"
value={new_password_repeat} value={newPasswordRepeat}
onChange={event => { onChange={event => {
setNewPasswordRepeat(event.target.value); setNewPasswordRepeat(event.target.value);
setNewPassColor('');
}} }}
/> />
<div className='relative flex justify-center my-4 border'> <div className='relative flex justify-center my-4 border'>
<button <button
type='submit' type='submit'
className='absolute bottom-0 px-2 py-1 bg-blue-500 border' className='absolute bottom-0 px-2 py-1 bg-blue-500 border'
disabled={processing}> disabled={loading}>
<span>Сменить пароль</span> <span>Сменить пароль</span>
</button> </button>
</div> </div>
{ error && <BackendError error={error} />}
</form> </form>
</div> </div>
)} )}

View File

@ -17,7 +17,6 @@ export function initBackend() {
axios.defaults.xsrfCookieName = 'csrftoken'; axios.defaults.xsrfCookieName = 'csrftoken';
axios.defaults.xsrfHeaderName = 'x-csrftoken'; axios.defaults.xsrfHeaderName = 'x-csrftoken';
axios.defaults.baseURL = `${config.backend}`; axios.defaults.baseURL = `${config.backend}`;
} }
// ================ Data transfer types ================ // ================ Data transfer types ================