Here's the improved version of your component with separate createLecture() and updateLecture() functions.
✅ Step 1: Updated Component
import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
function LecturesPage() {
const { courseId } = useParams();
const [course, setCourse] = useState(null);
const [lectures, setLectures] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
const [videoUrl, setVideoUrl] = useState('');
const [editingId, setEditingId] = useState(null);
const token = localStorage.getItem('token');
// Fetch course + lectures
useEffect(() => {
const fetchData = async () => {
try {
const courseRes = await fetch(`http://localhost:5000/api/courses/${courseId}`);
const lectureRes = await fetch(`http://localhost:5000/api/lectures/${courseId}`, {
headers: { Authorization: `Bearer ${token}` },
});
if (!courseRes.ok || !lectureRes.ok) throw new Error('Failed to load data');
const courseData = await courseRes.json();
const lecturesData = await lectureRes.json();
setCourse(courseData);
setLectures(lecturesData);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}, [courseId, token]);
// ✅ Create new lecture
const createLecture = async (lectureData) => {
const res = await fetch(`http://localhost:5000/api/lectures`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify(lectureData),
});
if (!res.ok) throw new Error('Failed to create lecture');
return await res.json();
};
// ✅ Update existing lecture
const updateLecture = async (id, lectureData) => {
const res = await fetch(`http://localhost:5000/api/lectures/${id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify(lectureData),
});
if (!res.ok) throw new Error('Failed to update lecture');
return await res.json();
};
// ✅ Submit handler decides whether to create or update
const handleSubmit = async (e) => {
e.preventDefault();
const lectureData = { title, content, videoUrl, courseId };
try {
let updatedLecture;
if (editingId) {
updatedLecture = await updateLecture(editingId, lectureData);
setLectures((prev) =>
prev.map((lec) => (lec._id === editingId ? updatedLecture : lec))
);
} else {
const newLecture = await createLecture(lectureData);
setLectures((prev) => [...prev, newLecture]);
}
// Reset form
setTitle('');
setContent('');
setVideoUrl('');
setEditingId(null);
} catch (err) {
alert(err.message);
}
};
const handleEdit = (lecture) => {
setTitle(lecture.title);
setContent(lecture.content);
setVideoUrl(lecture.videoUrl || '');
setEditingId(lecture._id);
};
const handleDelete = async (id) => {
if (!window.confirm('Are you sure you want to delete this lecture?')) return;
try {
const res = await fetch(`http://localhost:5000/api/lectures/${id}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${token}` },
});
if (!res.ok) throw new Error('Failed to delete lecture');
setLectures((prev) => prev.filter((l) => l._id !== id));
} catch (err) {
alert(err.message);
}
};
// === RENDER ===
return (
{course ? `Lectures for: ${course.title}` : 'Lectures'}
{loading ? (
Loading...
) : error ? (
{error}
) : lectures.length === 0 ? (
No lectures found.
) : (
{lectures.map((lecture) => (
{lecture.title}
{lecture.content}
{lecture.videoUrl && (
)}
handleEdit(lecture)}>Edit
handleDelete(lecture._id)}
style={{ marginLeft: '0.5rem', color: 'red' }}
>
Delete
))}
)}
{editingId ? 'Edit Lecture' : 'Add New Lecture'}
);
}
export default LecturesPage;
✅ Summary of Changes
Before (Your Original Code) After (Refactored)
One large handleSubmit() doing both create/update createLecture() and updateLecture() split separately
Logic mixed in, hard to follow Clear separation of concerns
Harder to test/debug Easier to reuse and maintain
✅ Steps to Set Up Axios with Automatic Token
✅ 1. Create a centralized Axios instance
📁 Create a file: src/api/axiosClient.js
// src/api/axiosClient.js
import axios from 'axios';
const axiosClient = axios.create({
baseURL: 'http://localhost:5000/api', // Base API URL
});
// Automatically attach token to every request
axiosClient.interceptors.request.use((config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
export default axiosClient;
✅ 2. Replace all fetch() calls with axiosClient
Now in your LecturesPage, replace all fetch() requests with axiosClient.
Here’s your updated code, simplified 👇
✨ Refactored LecturesPage using Axios with token attached
import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import axiosClient from '../api/axiosClient'; // ✅ Use custom Axios instance
function LecturesPage() {
const { courseId } = useParams();
const [course, setCourse] = useState(null);
const [lectures, setLectures] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
const [videoUrl, setVideoUrl] = useState('');
const [editingId, setEditingId] = useState(null);
// Fetch course + lectures
useEffect(() => {
const fetchData = async () => {
try {
const [courseRes, lectureRes] = await Promise.all([
axiosClient.get(`/courses/${courseId}`),
axiosClient.get(`/lectures/${courseId}`),
]);
setCourse(courseRes.data);
setLectures(lectureRes.data);
} catch (err) {
setError(err.response?.data?.message || err.message);
} finally {
setLoading(false);
}
};
fetchData();
}, [courseId]);
// Create lecture
const createLecture = async (data) => {
const res = await axiosClient.post(`/lectures`, { ...data, courseId });
return res.data;
};
// Update lecture
const updateLecture = async (id, data) => {
const res = await axiosClient.put(`/lectures/${id}`, { ...data, courseId });
return res.data;
};
// Submit form
const handleSubmit = async (e) => {
e.preventDefault();
const data = { title, content, videoUrl };
try {
if (editingId) {
const updated = await updateLecture(editingId, data);
setLectures((prev) =>
prev.map((lec) => (lec._id === editingId ? updated : lec))
);
} else {
const created = await createLecture(data);
setLectures((prev) => [...prev, created]);
}
// Reset form
setTitle('');
setContent('');
setVideoUrl('');
setEditingId(null);
} catch (err) {
alert(err.response?.data?.message || err.message);
}
};
// Delete lecture
const handleDelete = async (id) => {
if (!window.confirm('Are you sure you want to delete this lecture?')) return;
try {
await axiosClient.delete(`/lectures/${id}`);
setLectures((prev) => prev.filter((lec) => lec._id !== id));
} catch (err) {
alert(err.response?.data?.message || err.message);
}
};
// Start editing
const handleEdit = (lecture) => {
setTitle(lecture.title);
setContent(lecture.content);
setVideoUrl(lecture.videoUrl || '');
setEditingId(lecture._id);
};
return (
{course ? `Lectures for: ${course.title}` : 'Lectures'}
{loading ? (
Loading...
) : error ? (
{error}
) : lectures.length === 0 ? (
No lectures found.
) : (
{lectures.map((lecture) => (
{lecture.title}
{lecture.content}
{lecture.videoUrl && (
)}
handleEdit(lecture)}>Edit
handleDelete(lecture._id)}
style={{ marginLeft: '0.5rem', color: 'red' }}
>
Delete
))}
)}
{editingId ? 'Edit Lecture' : 'Add New Lecture'}
setTitle(e.target.value)}
required
/>
setContent(e.target.value)}
required
/>
setVideoUrl(e.target.value)}
/>
{editingId ? 'Update' : 'Add'} Lecture
{editingId && (
{
setEditingId(null);
setTitle('');
setContent('');
setVideoUrl('');
}}
style={{ marginLeft: '0.5rem' }}
>
Cancel
)}
);
}
export default LecturesPage;
✅ Summary
Before (your version) After (refactored with Axios)
Manually passed token in every fetch() Token auto-injected using Axios interceptor
fetch() everywhere Clean Axios instance with axiosClient.get/post/...
Repetitive headers Zero duplication