Last active
          October 27, 2025 18:58 
        
      - 
      
- 
        Save sunmeat/dbf4b63ef9875b5de38758b5cc0dcacc to your computer and use it in GitHub Desktop. 
    useContext - автентифікація
  
        
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
  | App.jsx: | |
| import React, {createContext, useContext, useState} from 'react'; | |
| import './App.css'; | |
| // контекст автентифікації | |
| const AuthContext = createContext(); | |
| // провайдер контексту автентифікації | |
| function AuthProvider({children}) { | |
| const [auth, setAuth] = useState({ | |
| isAuthenticated: false, | |
| user: null, | |
| role: 'гість' | |
| }); | |
| // функція входу в систему | |
| const login = (userData) => { | |
| setAuth({ | |
| isAuthenticated: true, | |
| user: userData, | |
| role: userData.role || 'користувач' | |
| }); | |
| }; | |
| // функція реєстрації | |
| const register = (userData) => { | |
| setAuth({ | |
| isAuthenticated: true, | |
| user: userData, | |
| role: userData.role || 'користувач' | |
| }); | |
| }; | |
| // функція входу як гість | |
| const guestLogin = () => { | |
| setAuth({ | |
| isAuthenticated: false, | |
| user: {name: 'Гість'}, | |
| role: 'гість' | |
| }); | |
| }; | |
| // функція виходу | |
| const logout = () => { | |
| setAuth({ | |
| isAuthenticated: false, | |
| user: null, | |
| role: 'гість' | |
| }); | |
| }; | |
| return ( | |
| <AuthContext.Provider value={{auth, login, register, guestLogin, logout}}> | |
| {children} | |
| </AuthContext.Provider> | |
| ); | |
| } | |
| // кастомний хук useAuth спрощує доступ до контексту AuthContext, надаючи компонентам зручний спосіб | |
| // отримання даних і методів автентифікації (auth, login, register, guestLogin, logout) | |
| // замість повторюваного виклику useContext(AuthContext) у кожному компоненті, useAuth робить код чистішим, | |
| // читабельнішим і підтримує єдиний інтерфейс для роботи з контекстом | |
| const useAuth = () => useContext(AuthContext); | |
| // головний компонент додатка | |
| function App() { | |
| const [screen, setScreen] = useState('auth'); | |
| const [theme, setTheme] = useState('light'); | |
| // перемикання теми | |
| const toggleTheme = () => { | |
| setTheme(theme === 'light' ? 'dark' : 'light'); | |
| }; | |
| // перехід на екран контенту | |
| const goToContent = () => { | |
| setScreen('content'); | |
| }; | |
| // перехід на екран авторизації | |
| const goToAuth = () => { | |
| setScreen('auth'); | |
| }; | |
| // зміна екранів залежить від стану screen | |
| return ( | |
| <AuthProvider> | |
| <div className={`app ${theme}`}> | |
| {screen === 'auth' ? ( | |
| <AuthScreen goToContent={goToContent} toggleTheme={toggleTheme}/> | |
| ) : ( | |
| <ContentScreen goToAuth={goToAuth} toggleTheme={toggleTheme}/> | |
| )} | |
| </div> | |
| </AuthProvider> | |
| ); | |
| } | |
| // екран автентифікації | |
| function AuthScreen({goToContent, toggleTheme}) { | |
| return ( | |
| <div className="auth-container"> | |
| <ThemeToggle toggleTheme={toggleTheme}/> | |
| <AuthForm goToContent={goToContent}/> | |
| <GuestButton goToContent={goToContent}/> | |
| </div> | |
| ); | |
| } | |
| // компонент кнопки перемикання теми | |
| function ThemeToggle({toggleTheme}) { | |
| return ( | |
| <button className="auth-button" onClick={toggleTheme}> | |
| Змінити тему | |
| </button> | |
| ); | |
| } | |
| // форма входу та реєстрації | |
| function AuthForm({goToContent}) { | |
| const {login, register} = useAuth(); | |
| const [loginInput, setLoginInput] = useState('Alex'); | |
| const [password, setPassword] = useState('1234'); | |
| // обробник входу | |
| const handleLogin = (e) => { | |
| e.preventDefault(); | |
| login({name: loginInput, role: 'користувач'}); | |
| goToContent(); | |
| }; | |
| // обробник реєстрації | |
| const handleRegister = (e) => { | |
| e.preventDefault(); | |
| register({name: loginInput, role: 'користувач'}); | |
| goToContent(); | |
| }; | |
| return ( | |
| <form className="auth-form"> | |
| <h2>Авторизація</h2> | |
| <input | |
| type="text" | |
| placeholder="Логін" | |
| value={loginInput} | |
| onChange={(e) => setLoginInput(e.target.value)} | |
| required | |
| /> | |
| <input | |
| type="password" | |
| placeholder="Пароль" | |
| value={password} | |
| onChange={(e) => setPassword(e.target.value)} | |
| required | |
| /> | |
| <div className="button-group"> | |
| <button className="auth-button" onClick={handleLogin}> | |
| Вхід | |
| </button> | |
| <button className="auth-button" onClick={handleRegister}> | |
| Реєстрація | |
| </button> | |
| </div> | |
| </form> | |
| ); | |
| } | |
| // кнопка входу як гість | |
| function GuestButton({goToContent}) { | |
| const {guestLogin} = useAuth(); | |
| // обробник входу як гість | |
| const handleGuestLogin = () => { | |
| guestLogin(); | |
| goToContent(); | |
| }; | |
| return ( | |
| <button className="auth-button guest" onClick={handleGuestLogin}> | |
| Увійти як гість | |
| </button> | |
| ); | |
| } | |
| // екран контенту | |
| function ContentScreen({goToAuth, toggleTheme}) { | |
| return ( | |
| <div className="content-container"> | |
| <ThemeToggle toggleTheme={toggleTheme}/> | |
| <UserInfo/> | |
| <Content/> | |
| <Settings/> | |
| <LogoutButton goToAuth={goToAuth}/> | |
| </div> | |
| ); | |
| } | |
| // інформація про користувача | |
| function UserInfo() { | |
| const {auth} = useAuth(); | |
| return ( | |
| <div className="content-section"> | |
| <h2>Користувач</h2> | |
| <p>Ім’я: {auth.user?.name || 'Гість'}</p> | |
| <p>Роль: {auth.role}</p> | |
| <p>Статус: {auth.isAuthenticated ? 'Автентифікація успішна' : 'Не автентифікований'}</p> | |
| </div> | |
| ); | |
| } | |
| // контент | |
| function Content() { | |
| const {auth} = useAuth(); | |
| return ( | |
| <div className="content-section"> | |
| <h2>Контент</h2> | |
| <p>Вітаємо, {auth.user?.name || 'Гість'}!</p> | |
| </div> | |
| ); | |
| } | |
| // налаштування | |
| function Settings() { | |
| const {auth} = useAuth(); | |
| return ( | |
| <div className="content-section"> | |
| <h2>Налаштування</h2> | |
| <p>{auth.isAuthenticated ? 'Налаштування користувача.' : 'Увійдіть для налаштувань.'}</p> | |
| </div> | |
| ); | |
| } | |
| // кнопка виходу | |
| function LogoutButton({goToAuth}) { | |
| const {logout} = useAuth(); | |
| // обробник виходу | |
| const handleLogout = () => { | |
| logout(); | |
| goToAuth(); | |
| }; | |
| return ( | |
| <button className="auth-button" onClick={handleLogout}> | |
| Вийти | |
| </button> | |
| ); | |
| } | |
| export default App; | |
| ================================================================================================================ | |
| App.css: | |
| @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&family=Poppins:wght@300;400;600&display=swap'); | |
| :root { | |
| --light-bg: linear-gradient(135deg, #ff00cc, #3333ff); | |
| --light-text: #f0f0f0; | |
| --light-accent: #ff00ff; | |
| --light-glow: 0 0 10px rgba(255, 0, 204, 0.6); | |
| --dark-bg: linear-gradient(135deg, #1a0033, #003366); | |
| --dark-text: #e0e0ff; | |
| --dark-accent: #ff33cc; | |
| --dark-glow: 0 0 10px rgba(255, 51, 204, 0.7); | |
| --transition: all 0.3s ease-in-out; | |
| } | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| html, body, #root { | |
| height: 100%; | |
| width: 100%; | |
| } | |
| body { | |
| font-family: 'Poppins', sans-serif; | |
| } | |
| .app { | |
| min-height: 100vh; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| background-size: 200% 200%; | |
| animation: gradientShift 10s ease infinite; | |
| } | |
| @keyframes gradientShift { | |
| 0% { | |
| background-position: 0 50%; | |
| } | |
| 50% { | |
| background-position: 100% 50%; | |
| } | |
| 100% { | |
| background-position: 0 50%; | |
| } | |
| } | |
| .light { | |
| background: var(--light-bg); | |
| color: var(--light-text); | |
| } | |
| .dark { | |
| background: var(--dark-bg); | |
| color: var(--dark-text); | |
| } | |
| .auth-container { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 1rem; | |
| padding: 1.5rem; | |
| max-width: 400px; | |
| width: 100%; | |
| background: rgba(255, 255, 255, 0.15); | |
| border-radius: 15px; | |
| backdrop-filter: blur(10px); | |
| box-shadow: var(--light-glow); | |
| } | |
| .dark .auth-container { | |
| box-shadow: var(--dark-glow); | |
| } | |
| .auth-form { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0.8rem; | |
| } | |
| .auth-form h2 { | |
| font-family: 'Orbitron', sans-serif; | |
| font-size: 1.8rem; | |
| font-weight: 700; | |
| text-transform: uppercase; | |
| text-shadow: var(--light-glow); | |
| } | |
| .dark .auth-form h2 { | |
| text-shadow: var(--dark-glow); | |
| } | |
| .auth-form input { | |
| padding: 10px; | |
| border: none; | |
| border-radius: 8px; | |
| background: rgba(255, 255, 255, 0.2); | |
| color: var(--light-text); | |
| font-size: 0.9rem; | |
| } | |
| .dark .auth-form input { | |
| color: var(--dark-text); | |
| } | |
| .auth-form input:focus { | |
| outline: none; | |
| box-shadow: var(--light-glow); | |
| } | |
| .dark .auth-form input:focus { | |
| box-shadow: var(--dark-glow); | |
| } | |
| .button-group { | |
| display: flex; | |
| gap: 0.5rem; | |
| } | |
| .auth-button { | |
| padding: 10px; | |
| border: none; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| background: linear-gradient(45deg, #ff0066, #ffcc00); | |
| color: #fff; | |
| font-family: 'Poppins', sans-serif; | |
| font-size: 0.9rem; | |
| font-weight: 600; | |
| transition: var(--transition); | |
| box-shadow: var(--light-glow); | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .dark .auth-button { | |
| background: linear-gradient(45deg, #cc00ff, #00ccff); | |
| box-shadow: var(--dark-glow); | |
| } | |
| .auth-button:hover { | |
| transform: scale(1.05); | |
| box-shadow: 0 6px 15px rgba(255, 0, 204, 0.5); | |
| } | |
| .dark .auth-button:hover { | |
| box-shadow: 0 6px 15px rgba(255, 51, 204, 0.6); | |
| } | |
| .auth-button::after { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: -100%; | |
| width: 100%; | |
| height: 100%; | |
| background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); | |
| transition: 0.4s; | |
| } | |
| .auth-button:hover::after { | |
| left: 100%; | |
| } | |
| .auth-button.guest { | |
| background: linear-gradient(45deg, #00ccff, #33ff33); | |
| } | |
| .dark .auth-button.guest { | |
| background: linear-gradient(45deg, #33ffcc, #66ff33); | |
| } | |
| .content-container { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 1rem; | |
| padding: 1.5rem; | |
| max-width: 600px; | |
| width: 100%; | |
| background: rgba(255, 255, 255, 0.15); | |
| border-radius: 15px; | |
| backdrop-filter: blur(10px); | |
| box-shadow: var(--light-glow); | |
| } | |
| .dark .content-container { | |
| box-shadow: var(--dark-glow); | |
| } | |
| .content-section { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0.5rem; | |
| } | |
| .content-section h2 { | |
| font-family: 'Orbitron', sans-serif; | |
| font-size: 1.6rem; | |
| font-weight: 700; | |
| text-shadow: var(--light-glow); | |
| } | |
| .dark .content-section h2 { | |
| text-shadow: var(--dark-glow); | |
| } | |
| .content-section p { | |
| font-size: 1rem; | |
| line-height: 1.6; | |
| font-weight: 300; | |
| } | |
| @media (max-width: 768px) { | |
| .auth-container, .content-container { | |
| padding: 1rem; | |
| max-width: 90%; | |
| } | |
| .auth-form h2, .content-section h2 { | |
| font-size: 1.5rem; | |
| } | |
| .auth-button { | |
| padding: 8px; | |
| font-size: 0.8rem; | |
| } | |
| } | |
| @media (max-width: 480px) { | |
| .auth-container, .content-container { | |
| padding: 0.8rem; | |
| } | |
| .auth-form h2, .content-section h2 { | |
| font-size: 1.4rem; | |
| } | |
| .auth-form input { | |
| padding: 8px; | |
| font-size: 0.8rem; | |
| } | |
| .auth-button { | |
| padding: 6px; | |
| font-size: 0.7rem; | |
| } | |
| .button-group { | |
| flex-direction: column; | |
| gap: 0.3rem; | |
| } | |
| } | 
  
    Sign up for free
    to join this conversation on GitHub.
    Already have an account?
    Sign in to comment