I'm new to the webdev world and want to learn ReactJS. I followed a tutorial I found on YouTube made by Traversy where he makes a task tracker and now I want to make some changes to it to learn and practice some more.
I want to use context for the appointments (originally named tasks in the tutorial), add a calendar with react-calendar and use react-router-dom. I got stuck for a while trying to make the list render, because it only rendered "empty". Later on found this post with a similar issue to mine: Only run a useEffect fetch after first useEffect fetch has fired and setUser in context
I changed bits of my code based on that post and now it does render the appointment list, but I don't know why it didn't work before and I'm unsure on why it does work now. I don't even know if I'm using context correctly or just prop-drilling. Help would be greatly appreciated. Thank you.
Also, sorry if my code is a mess, I'm new at this.
App.js
import { createContext, useState, useEffect } from "react";
import Dashboard from "./views/Dashboard";
import './App.css';
import { BrowserRouter as Router, Route, Routes} from "react-router-dom";
import AddAppointmentForm from "./views/AddAppointmentForm";
export const AppContext = createContext();
export const AppUpdateContext = createContext();
function App() {
const [appointments, setAppointments] = useState([])
const updateAppointments = (apptList) => {
setAppointments(apptList)
}
return (
<AppContext.Provider value={ appointments }>
<AppUpdateContext.Provider value={ updateAppointments }>
<Router>
<Routes>
<Route path="/" element={<Dashboard appointments={appointments} />} />
{/* <Route path="/add" element={<AddAppointmentForm />} /> TBA */}
</Routes>
</Router>
</AppUpdateContext.Provider>
</AppContext.Provider>
);
}
export default App;
Dashboard.js
import { useEffect, useContext} from "react";
import { AppContext } from "../App";
import { AppUpdateContext } from "../App";
import AppointmentList from "../components/AppointmentList";
import Header from "../components/Header";
// function Dashboard() { // this is how it used to be
function Dashboard(props) {
const appointments = useContext(AppContext)
const setAppointments = useContext(AppUpdateContext)
const fetchAppointmentList = async () => {
const res = await fetch("http://localhost:5000/appointments");
const data = await res.json();
return data;
}
useEffect(() => {
const getAppointments = async () => {
const appointmentsFromServer = await fetchAppointmentList();
setAppointments(appointmentsFromServer);
}
getAppointments();
console.log("ñññññ",appointments)
}, []);
console.log("aagh",appointments)
return (
<div style={dashboardStyle}>
<Header />
{/* {appointments.lenght>0 ? (<AppointmentList />) : <p>empty</p>} this is how it used to be */}
<AppointmentList appointments={props?.appointments}/>
</div>
);
}
const dashboardStyle = {
maxWidth: "31.25rem",
overflow: "auto",
minHeight: "18.75rem",
border: "1px solid steelblue",
margin: "1.875rem auto",
padding: ".5rem",
boxSizing: "border-box",
}
export default Dashboard;
AppointmentList.js
import Appointment from "./Appointment";
import { AppContext } from "../App";
import { useContext } from "react";
function AppointmentList({ appointments }) {
// function AppointmentList() { // this is how it used to be
// const { appointments, setAppointments } = useContext(AppContext)
console.log("appList",appointments) // this is how it used to be
return (
<>
{
appointments.map(appt => (
<Appointment key={appt.id} appointment={appt} />
))
}
</>
);
}
export default AppointmentList;
Why does optional chaining allows rendering when fetching data through useEffect in an app that uses context?
<AppointmentList appointments={props?.appointments}/>
It allows rendering by preventing accidental accesses into potentially null or undefined objects. The only way props
could be undefined though is if you just simply don't declare it, i.e. const Dashboard = () => {....
vs const Dashboard = (props) => {....
.
You are drilling the appointments
state through props. AppointmentList
can use the AppContext
context to access the appointments
state, while Dashboard
can use the AppUpdateContext
context to update the appointments
state.
App
function App() {
const [appointments, setAppointments] = useState([]);
const updateAppointments = (apptList) => {
setAppointments(apptList);
};
return (
<AppContext.Provider value={{ appointments }}> // <-- need object here
<AppUpdateContext.Provider value={{ updateAppointments }}> // <-- and here
<Router>
<Routes>
<Route path="/" element={<Dashboard />} /> // <-- don't pass props
</Routes>
</Router>
</AppUpdateContext.Provider>
</AppContext.Provider>
);
}
Dashboard
function Dashboard() { // <-- no props
const { updateAppointments } = useContext(AppUpdateContext); // <-- access from context
const fetchAppointmentList = async () => {
const res = await fetch("http://localhost:5000/appointments");
const data = await res.json();
return data;
};
useEffect(() => {
const getAppointments = async () => {
const appointmentsFromServer = await fetchAppointmentList();
updateAppointments(appointmentsFromServer);
}
getAppointments();
}, []);
return (
<div style={dashboardStyle}>
<Header />
<AppointmentList /> // <-- don't pass props
</div>
);
}
AppointmentList
function AppointmentList() { // <-- no props
const { appointments } = useContext(AppContext); // <-- access from context
return appointments.map(appt => (
<Appointment key={appt.id} appointment={appt} />
));
}