2.8 KiB
2.8 KiB
Agent Coding Guide: Laravel + React SPA with SWR & Zustand
Use this for coding guide and use agent-tasks.md for backlog and tracking progress.
1. Project Structure
Backend (Laravel):
- Standard folders:
app/Http/Controllers(thin, validate, call services)app/Services(business logic)routes/api.php(API endpoints, RESTful JSON)app/Models(Eloquent ORM)app/Http/Resources(format API responses)
- Use Laravel Sanctum for SPA authentication.
Frontend (React):
- React app inside
/resources/js/reactfor seamless Laravel integration. - Functional components with hooks.
- React Router for client-side routing.
- Zustand for global state management.
- SWR for all server data fetching and caching.
- Axios as HTTP client used within SWR fetcher function.
2. Code & Style
Backend (Laravel/PHP):
- PSR-12 standard.
- Controllers handle request, validation, call service methods, return API Resources.
- Services contain business logic.
- Use JSON responses throughout.
Frontend (React/JS):
- Functional components with hooks and JSX.
- Manage UI state (modals, forms) locally or via Zustand.
- Use Axios inside SWR for API calls to leverage SWR caching and revalidation.
- Leverage SWR hooks (
useSWR) for data fetching with automatic caching, re-fetching, and error handling—avoid manual state for server data. - Use Zustand only for client state that doesn’t come from API (e.g., UI toggles, theme, user session info).
- Use React Router for SPA navigation, avoiding full page reloads.
3. Example Usage
Zustand Store (Client UI State)
// store/useStore.js
import create from 'zustand';
export const useStore = create(set => ({
isModalOpen: false,
openModal: () => set({ isModalOpen: true }),
closeModal: () => set({ isModalOpen: false }),
editingPlayer: null,
setEditingPlayer: (player) => set({ editingPlayer: player }),
}));
SWR Data Fetching with Axios
// utils/api.js
import axios from 'axios';
export const apiClient = axios.create({
baseURL: '/api',
withCredentials: true,
});
export const fetcher = url => apiClient.get(url).then(res => res.data);
// components/PlayerList.js
import useSWR from 'swr';
import { fetcher } from '../utils/api';
import { useStore } from '../store/useStore';
function PlayerList() {
const { data, error } = useSWR('/players', fetcher);
const { openModal, setEditingPlayer } = useStore();
if (error) return <div>Failed to load</div>;
if (!data) return <div>Loading...</div>;
const handleEdit = (player) => {
setEditingPlayer(player);
openModal();
};
return (
<ul>
{data.players.map(player => (
<li key={player.id}>
{player.name}
<button onClick={() => handleEdit(player)}>Edit</button>
</li>
))}
</ul>
);
}