React
Vue d'ensemble
React est une bibliothèque JavaScript pour construire des interfaces utilisateur interactives, basée sur des composants réutilisables et un rendu efficace via le Virtual DOM.
Avantages clés
- Component-based : Architecture modulaire
- Virtual DOM : Performance optimale
- Hooks : Gestion d'état moderne
- Ecosystem : Next.js, Gatsby, Create React App
Composants modernes
// Functional Component avec Hooks
import { useState, useEffect } from 'react';
const UserList = () => {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetchUsers();
}, []);
const fetchUsers = async () => {
try {
setLoading(true);
const response = await fetch('/api/users');
if (!response.ok) throw new Error('Failed to fetch');
const data = await response.json();
setUsers(data.data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return (
<div className="user-list">
{users.map(user => (
<UserCard key={user.id} user={user} />
))}
</div>
);
};
const UserCard = ({ user }) => (
<div className="user-card">
<h3>{user.name}</h3>
<p>{user.email}</p>
<span>{user.posts_count} posts</span>
</div>
);
Custom Hooks
// Custom hook pour API
const useApi = (url) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch(url);
const result = await response.json();
setData(result);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
};
// Utilisation
const Dashboard = () => {
const { data: users, loading } = useApi('/api/users');
return (
<div>
{loading ? <Spinner /> : <UserList users={users} />}
</div>
);
};
Integration avec Laravel
// Service API pour Laravel backend
class ApiService {
constructor(baseURL = '/api') {
this.baseURL = baseURL;
this.token = localStorage.getItem('auth_token');
}
async request(endpoint, options = {}) {
const url = `${this.baseURL}${endpoint}`;
const config = {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
...(this.token && { 'Authorization': `Bearer ${this.token}` }),
},
...options,
};
const response = await fetch(url, config);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
}
// CRUD operations
async getUsers(page = 1) {
return this.request(`/users?page=${page}`);
}
async createUser(userData) {
return this.request('/users', {
method: 'POST',
body: JSON.stringify(userData),
});
}
async updateUser(id, userData) {
return this.request(`/users/${id}`, {
method: 'PUT',
body: JSON.stringify(userData),
});
}
async deleteUser(id) {
return this.request(`/users/${id}`, {
method: 'DELETE',
});
}
}
export default new ApiService();
Testing avec Jest
// UserCard.test.jsx
import { render, screen } from '@testing-library/react';
import UserCard from './UserCard';
describe('UserCard', () => {
const mockUser = {
id: 1,
name: 'John Doe',
email: 'john@example.com',
posts_count: 5
};
it('renders user information correctly', () => {
render(<UserCard user={mockUser} />);
expect(screen.getByText('John Doe')).toBeInTheDocument();
expect(screen.getByText('john@example.com')).toBeInTheDocument();
expect(screen.getByText('5 posts')).toBeInTheDocument();
});
it('handles missing posts count', () => {
const userWithoutPosts = { ...mockUser, posts_count: 0 };
render(<UserCard user={userWithoutPosts} />);
expect(screen.getByText('0 posts')).toBeInTheDocument();
});
});
Ressources
- Documentation : reactjs.org
- Next.js : nextjs.org