¶Membuat Custom Context Authentikasi React Native
Pada artikel kali ini kita akan membuat sebuah context untuk autentikasi user, silahkan teman-teman buat folder baru dengan nama context
, kemudian di dalam folder tersebut buat file baru dengan nama AuthContext.tsx
. Kemudian masukkan kode berikut ini:
AuthContext.tsx
import api from "@/api";
import { User } from "@/types/user";
import AsyncStorage from '@react-native-async-storage/async-storage';
import { router } from "expo-router";
import { createContext, useEffect, useState } from "react";
// define type auth context
type AuthContextType = {
user: User | null;
isLoading: boolean;
fetchUser: () => Promise<void>;
handleRegister: (name: string, email: string, password: string) => Promise<void>;
handleLogin: (email: string, password: string) => Promise<void>;
handleLogout: () => Promise<void>;
};
// define context object
export const AuthContext = createContext<AuthContextType>({
user: null,
isLoading: false,
fetchUser: async () => {},
handleRegister: async () => {},
handleLogin: async () => {},
handleLogout: async () => {},
})
export const AuthProvider = ({ children } : {children: React.ReactNode}) => {
// define state user
const [user, setUser] = useState<User | null>(null);
// define state loading
const [isLoading, setIsLoading] = useState(true);
// define method fetchUser
const fetchUser = async () => {
try{
// hit api profile
const response = await api.get('/profile');
// update state user with response api
setUser(response.data.user);
}catch{
// update state user to null
setUser(null);
}finally{
// update state is loading to false
setIsLoading(false);
}
}
// define method register
const handleRegister = async (name: string, email: string, password: string) => {
// hit api register
const response = await api.post('/register', { name, email, password});
// get token user
const token = response.data.access_token;
// save token
await AsyncStorage.setItem('access_token', token);
// set token to headers authorization
api.defaults.headers.common['Authorization'] = `Bearer ${token}`;
// call method fetch user
await fetchUser();
// push to dashboard screen
router.replace('/(home)/dashboard')
}
// define method login
const handleLogin = async (email: string, password: string) => {
try{
// hit api login
const response = await api.post('/login', {email, password})
// get token user
const token = response.data.access_token;
// save token
await AsyncStorage.setItem('access_token', token);
// set token to headers authorization
api.defaults.headers.common['Authorization'] = `Bearer ${token}`;
// call method fetch user
await fetchUser();
// push to dashboard screen
router.replace('/(home)/dashboard')
}catch(error){
throw error;
}
}
// define method logout
const handleLogout = async () => {
// hit api logout
await api.post('/logout');
// remove token user
await AsyncStorage.removeItem('access_token');
// update state user to null
setUser(null);
// push to login screen
router.replace('/(auth)/login')
}
// call method fetch user using useEffect hooks
useEffect(() => {
// define method to check authentication
const checkAuth = async () => {
try{
// get access_token user
const token = await AsyncStorage.getItem('access_token');
if(token){
api.defaults.headers.common['Authorization'] = `Bearer ${token}`;
await fetchUser();
}else{
setIsLoading(false);
}
}catch{
setIsLoading(false);
}
}
checkAuth();
}, [])
return (
<AuthContext.Provider value={{ fetchUser, user, isLoading, handleRegister, handleLogin, handleLogout }}>
{children}
</AuthContext.Provider>
);
}
Pertama-tama, kita melakukan import beberapa module yang kita butuhkan, seperti:
-
api
: konfigurasi axios untuk mengakses API kita.
-
User
: tipe data user.
-
AsyncStorage
: digunakan untuk menyimpan token autentikasi secara lokal.
-
router
: untuk melakukan navigasi antar halaman menggunakan expo-router
.
- Beberapa hooks bawaan dari React seperti
createContext
, useEffect
, dan useState
.
¶Mendefinisikan Tipe Context
Selanjutnya kita mendefinisikan struktur tipe data yang akan digunakan oleh AuthContext
.
AuthContext.tsx
type AuthContextType = {
user: User | null;
isLoading: boolean;
fetchUser: () => Promise<void>;
handleRegister: (name: string, email: string, password: string) => Promise<void>;
handleLogin: (email: string, password: string) => Promise<void>;
handleLogout: () => Promise<void>;
};
Pada kode diatas, kita mendefinisikan beberapa tipe data context yang akan menyimpan data user
, status isLoading
, serta beberapa method yang akan kita buat didalam context seperti fetchUser
, handleRegister
, handleLogin
dan handleLogout
.
¶Membuat dan Mengekspor Context
Lalu kita buat context-nya menggunakan createContext
, dan langsung beri nilai default agar tidak terjadi error saat digunakan sebelum AuthProvider
terpasang.
AuthContext.tsx
export const AuthContext = createContext<AuthContextType>({
user: null,
isLoading: false,
fetchUser: async () => {},
handleRegister: async () => {},
handleLogin: async () => {},
handleLogout: async () => {},
});
¶Membuat Komponen AuthProvider
Disini kita akan membuat sebuah komponen baru bernama AuthProvider
yang akan digunakan untuk membungkus seluruh aplikasi kita. Komponen ini akan menyediakan data user dan fungsi autentikasi ke seluruh component anak di dalamnya.
AuthContext.tsx
export const AuthProvider = ({ children } : {children: React.ReactNode}) => {
¶State User dan Loading
Dalam AuthProvider
, kita mendefinisikan dua state:
-
user
: menyimpan data user yang sedang login.
-
isLoading
: penanda bahwa proses autentikasi sedang berlangsung.
AuthContext.tsx
const [user, setUser] = useState<User | null>(null);
const [isLoading, setIsLoading] = useState(true);
¶Method fetchUser
Method ini digunakan untuk mengambil data profil user yang sedang login.
AuthContext.tsx
const fetchUser = async () => {
try {
const response = await api.get('/profile');
setUser(response.data.user);
} catch {
setUser(null);
} finally {
setIsLoading(false);
}
}
Jika API berhasil diakses, maka data user akan disimpan. Jika gagal (misalnya token tidak valid), maka user akan diset ke null
. Terakhir, kita pastikan isLoading
akan di-set false
.
¶Method handleRegister
Digunakan untuk mendaftarkan user baru.
AuthContext.tsx
const handleRegister = async (name: string, email: string, password: string) => {
const response = await api.post('/register', { name, email, password });
const token = response.data.access_token;
await AsyncStorage.setItem('access_token', token);
api.defaults.headers.common['Authorization'] = `Bearer ${token}`;
await fetchUser();
router.replace('/(home)/dashboard');
}
Setelah berhasil register:
- Token disimpan ke
AsyncStorage
- Token diset ke header
Authorization
- Data user diambil ulang menggunakan
fetchUser
- User diarahkan ke halaman dashboard
¶Method handleLogin
Digunakan untuk login user yang sudah terdaftar.
AuthContext.tsx
const handleLogin = async (email: string, password: string) => {
try {
const response = await api.post('/login', { email, password });
const token = response.data.access_token;
await AsyncStorage.setItem('access_token', token);
api.defaults.headers.common['Authorization'] = `Bearer ${token}`;
await fetchUser();
router.replace('/(home)/dashboard');
} catch (error) {
throw error;
}
}
Alurnya hampir sama seperti handleRegister
, namun di sini kita menangani error yang dilempar dari API jika login gagal.
¶Method handleLogout
Method ini digunakan untuk logout user yang sedang aktif.
AuthContext.tsx
const handleLogout = async () => {
await api.post('/logout');
await AsyncStorage.removeItem('access_token');
setUser(null);
router.replace('/(auth)/login');
}
Saat logout:
- Token dihapus dari penyimpanan lokal
- User dihapus dari state
- Aplikasi diarahkan kembali ke halaman login
¶Mengecek Autentikasi Saat Aplikasi Dimuat
Kita ingin memastikan bahwa jika user memiliki token tersimpan, maka data user akan langsung dimuat saat aplikasi dibuka, disini kita menggunakan hooks useEffect
.
AuthContext.tsx
useEffect(() => {
const checkAuth = async () => {
try {
const token = await AsyncStorage.getItem('access_token');
if (token) {
api.defaults.headers.common['Authorization'] = `Bearer ${token}`;
await fetchUser();
} else {
setIsLoading(false);
}
} catch {
setIsLoading(false);
}
}
checkAuth();
}, []);
Terakhir, kita kembalikan provider dan berikan semua method dan data yang sudah kita buat.
AuthContext.tsx
return (
<AuthContext.Provider
value={{ fetchUser, user, isLoading, handleRegister, handleLogin, handleLogout }}
>
{children}
</AuthContext.Provider>
);
¶Membuat Custom Hook untuk Mengakses Context
Agar kita lebih mudah dalam menggunakan context autentikasi yang kita buat sebelumnya di seluruh komponen aplikasi, kita bisa membuat sebuah custom hook bernama useAuth
. Silakan teman-teman buat folder baru dengan nama hooks
, lalu di dalam folder tersebut buat file baru dengan nama useAuth.ts
, dan masukkan kode berikut:
useAuth.ts
import { AuthContext } from '@/context/AuthContext';
import { useContext } from 'react';
export const useAuth = () => useContext(AuthContext);
Pada kode di atas :
- Kita mengimpor
AuthContext
dari file AuthContext.tsx
yang sebelumnya telah kita buat.
- Kita juga menggunakan
useContext
dari React, yang berfungsi untuk mengakses isi dari context.
useAuth.ts
import { AuthContext } from '@/context/AuthContext';
import { useContext } from 'react';
Kemudian kita membuat sebuah fungsi useAuth
, yaitu custom hook yang akan mengembalikan isi dari AuthContext
.
useAuth.ts
export const useAuth = () => useContext(AuthContext);
Dengan adanya custom hook ini, kita tidak perlu lagi mengimpor dan menggunakan useContext(AuthContext)
secara manual di setiap komponen. Cukup panggil useAuth()
saja, dan kita sudah bisa mengakses:
-
user
-
isLoading
-
handleLogin()
-
handleRegister()
-
handleLogout()
-
fetchUser()