Въведение
В този урок ще научим как да управляваме глобалното състояние с помощта на контекстния API на React и куката useReducer. Ще изградим система за удостоверяване, която съхранява потребителски данни и статус на удостоверяване, което ги прави достъпни в цялото приложение.
Предпоставки
Преди да започнем, уверете се, че сте инсталирали следното:
- Node.js и npm
- Редактор на код (Код на Visual Studio, Sublime Text и др.)
- Основни познания за React и функционални компоненти
Настройвам
Създайте нов React проект, като използвате Create React App или предпочитания от вас метод:
npx create-react-app react-auth-context-demo --template typescript cd react-auth-context-demo npm start
Част 1: Внедряване на интерфейси за контекст на удостоверяване
Нека започнем със създаването на необходимите интерфейси за нашия Auth Context.
// Import necessary dependencies import { createContext, useEffect, useReducer } from 'react'; import type { FC, ReactNode } from 'react'; import PropTypes from 'prop-types'; import type { User } from '../types/user'; import { authApi } from '../__fakeApi__/authApi'; // Define the structure of the state object interface State { isInitialized: boolean; isAuthenticated: boolean; user: User | null; } // Define the AuthContextValue interface extending the State interface with additional methods interface AuthContextValue extends State { platform: 'JWT'; login: (email: string, password: string) => Promise<void>; logout: () => Promise<void>; register: (email: string, name: string, password: string) => Promise<void>; } // Define the AuthProviderProps interface for specifying props of AuthProvider interface AuthProviderProps { children: ReactNode; } // Rest of the code will be added in subsequent parts.
Част 2: Дефиниране на типове действия и начално състояние
ще дефинираме типовете действия и първоначалното състояние за нашия контекст на удостоверяване.
// ... (Previous code) // Define action types to be used by the reducer function type InitializeAction = { type: 'INITIALIZE'; payload: { isAuthenticated: boolean; user: User | null; }; }; type LoginAction = { type: 'LOGIN'; payload: { user: User; }; }; type LogoutAction = { type: 'LOGOUT'; }; type RegisterAction = { type: 'REGISTER'; payload: { user: User; }; }; type Action = | InitializeAction | LoginAction | LogoutAction | RegisterAction; // Define the initial state of the authentication context const initialState: State = { isAuthenticated: false, isInitialized: false, user: null }; // ... (Rest of the code will be added in the next part.)
Част 3: Създаване на редуктор и манипулатори
В тази част ще създадем редуцираща функция и манипулатори за актуализиране на състоянието въз основа на различни типове действия.
// ... (Previous code) // Define handlers for each action type to update the state const handlers: Record<string, (state: State, action: Action) => State> = { INITIALIZE: (state: State, action: InitializeAction): State => { const { isAuthenticated, user } = action.payload; return { ...state, isAuthenticated, isInitialized: true, user }; }, LOGIN: (state: State, action: LoginAction): State => { const { user } = action.payload; return { ...state, isAuthenticated: true, user }; }, LOGOUT: (state: State): State => ({ ...state, isAuthenticated: false, user: null }), REGISTER: (state: State, action: RegisterAction): State => { const { user } = action.payload; return { ...state, isAuthenticated: true, user }; } }; // Reducer function to manage state updates based on actions const reducer = (state: State, action: Action): State => ( handlers[action.type] ? handlers[action.type](state, action) : state ); // ... (Rest of the code will be added in the next part.)
Част 4: Създаване на AuthContext и AuthProvider
ще създадем AuthContext с помощта на createContext и компонента AuthProvider с помощта на useReducer и useEffect.
// ... (Previous code) // Create the AuthContext using createContext const AuthContext = createContext<AuthContextValue>({ ...initialState, platform: 'JWT', login: () => Promise.resolve(), logout: () => Promise.resolve(), register: () => Promise.resolve() }); // Create the AuthProvider component using useReducer and useEffect export const AuthProvider: FC<AuthProviderProps> = (props) => { const { children } = props; const [state, dispatch] = useReducer(reducer, initialState); // useEffect to initialize the authentication state useEffect(() => { const initialize = async (): Promise<void> => { try { const accessToken = window.localStorage.getItem('accessToken'); if (accessToken) { const user = await authApi.me(accessToken); // Dispatch INITIALIZE action with user data if accessToken is present dispatch({ type: 'INITIALIZE', payload: { isAuthenticated: true, user } }); } else { // Dispatch INITIALIZE action with default data if no accessToken is present dispatch({ type: 'INITIALIZE', payload: { isAuthenticated: false, user: null } }); } } catch (err) { console.error(err); // Dispatch INITIALIZE action with default data in case of any error dispatch({ type: 'INITIALIZE', payload: { isAuthenticated: false, user: null } }); } }; // Call the initialize function on component mount initialize(); }, []); // Define login, logout, and register functions to handle authentication actions const login = async (email: string, password: string): Promise<void> => { // ... (Rest of the code will be added in the next part.) }; // ... (Rest of the code will be added in the next part.) }; // ... (Rest of the code will be added in the next part.)
Част 5: Завършване на AuthProvider и PropTypes
ще завършим компонента AuthProvider и ще добавим проверка на PropTypes.
// ... (Previous code) export const AuthProvider: FC<AuthProviderProps> = (props) => { const { children } = props; const [state, dispatch] = useReducer(reducer, initialState); // ... (Previous code) // Define login, logout, and register functions to handle authentication actions const login = async (email: string, password: string): Promise<void> => { const accessToken = await authApi.login({ email, password }); const user = await authApi.me(accessToken); localStorage.setItem('accessToken', accessToken); dispatch({ type: 'LOGIN', payload: { user } }); }; const logout = async (): Promise<void> => { localStorage.removeItem('accessToken'); dispatch({ type: 'LOGOUT' }); }; const register = async ( email: string, name: string, password: string ): Promise<void> => { const accessToken = await authApi.register({ email, name, password }); const user = await authApi.me(accessToken); localStorage.setItem('accessToken', accessToken); dispatch({ type: 'REGISTER', payload: { user } }); }; // Render the AuthContext.Provider with the state and methods provided return ( <AuthContext.Provider value={{ ...state, platform: 'JWT', login, logout, register }} > {children} </AuthContext.Provider> ); }; // Validate the props for AuthProvider AuthProvider.propTypes = { children: PropTypes.node.isRequired }; // Export the AuthContext as default export default AuthContext;
Част 6: Създаване на useAuth Hook за използване на AuthContext
import { useContext } from 'react'; import AuthContext from '../contexts/AuthContext'; const useAuth = () => useContext(AuthContext); export default useAuth;
Част 7: Обвиване в index.js
// src/index.js import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; import { AuthProvider } from './context/AuthContext'; ReactDOM.render( <React.StrictMode> {/* Wrap your App component with the AuthProvider */} <AuthProvider> <App /> </AuthProvider> </React.StrictMode>, document.getElementById('root') );
Използване на useAuth Hook в компонент
import React from 'react'; import useAuth from '../hooks/useAuth'; const SomeComponent: React.FC = () => { // Use the custom hook to get access to the AuthContext const { isAuthenticated, user, login, logout, register } = useAuth(); // Now, you can use the state and methods wherever needed return ( <div> {isAuthenticated ? ( <p>Hello, {user?.name}!</p> ) : ( <p>Please log in or register.</p> )} {isAuthenticated ? ( <button onClick={logout}>Logout</button> ) : ( <button onClick={() => login('[email protected]', 'password')}> Login </button> )} </div> ); }; export default SomeComponent;
Заключение
В тази последна част създадохме персонализирана кука, наречена useAuth
, за да използва AuthContext
. Използвайки тази кука, можете лесно да получите достъп до глобалното състояние и методите за удостоверяване във всеки компонент във вашето приложение.
Чрез комбиниране на AuthContext, AuthProvider и useAuth hook, вие вече успешно внедрихте глобално управление на състоянието за удостоверяване на потребителя във вашето приложение React. Вече можете да създавате сигурни и богати на функции приложения, които разчитат на това състояние на глобално удостоверяване.
Чувствайте се свободни да персонализирате и разширите тази реализация, за да отговаря на специфичните изисквания на вашия проект. Приятно кодиране!
Полезни ресурси:
- Github repo:
https://github.com/cupid-dust/use-imperative