I created a NewsContext for my React project for accessting static data:
NewsContext:
"use client";
import { createContext, useContext, useState, useEffect } from "react";
export const NewsContext = createContext<AuthorVisibilityInfo | undefined>(undefined);
export function useNewsContext() {
const info = useContext(NewsContext);
if (info === undefined) {
throw new Error("useNewsContext must be used within a NewsProvider");
}
return info;
}
export interface AuthorVisibilityInfo {
visibilities: Visibility[];
authors: Author[];
}
export interface Visibility {
name: string;
displayName: string;
}
export interface Author {
name: string;
displayName: string;
color: string;
}
export const NewsProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [data, setData] = useState<AuthorVisibilityInfo | undefined>(undefined);
useEffect(() => {
fetch("http://localhost:7070/news/authors-visibility")
.then(response => {
if (!response.ok) {
throw new Error("Network response was not ok");
}
return response.json();
})
.then((json: AuthorVisibilityInfo) => {
console.log("Fetched data:", json);
setData(json);
})
.catch(error => {
console.error("Error fetching data:", error);
});
}, []);
return (
<NewsContext.Provider value={data}>
{children}
</NewsContext.Provider>
);
};
export function getAuthorByName(authors: Author[], name: string): Author | undefined {
return authors.find(author => author.name === name);
}
Root-Layout:
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className="antialiased bg-gray-100 dark:bg-gray-900 dark:text-gray-200 min-h-screen flex flex-col"
>
<Header />
<div className="flex flex-1">
<div className="flex-1 p-6">
<NewsProvider>
{children}
</NewsProvider>
</div>
<Sidebar />
</div>
<Footer />
</body>
</html>
);
}
Now my problem is, always when I access directly a page the following error comes up:
If I access the page via a button it is working. I think the site is in general loaded before the context is loaded.
Are there any ideas?
const [data, setData] = useState<AuthorVisibilityInfo | undefined>(undefined);
Your initial state is set to undefined
, so on until the data loads, that's what you'll be providing.
export function useNewsContext() {
const info = useContext(NewsContext);
if (info === undefined) {
throw new Error("useNewsContext must be used within a NewsProvider");
}
return info;
}
But you also have code that throws an error when it receives undefined. If you want to keep this code that throws on undefined, then you need to set up your provider so it never uses undefined
as its value.
For example, maybe you could change the value it provides to have a loading
variable:
export interface AuthorVisibilityInfo {
loading: boolean;
visibilities: Visibility[];
authors: Author[];
}
And then initialize the state to have loading true:
const [data, setData] = useState<AuthorVisibilityInfo>({
loading: true,
visilibities: [],
authors: []
});
And then once the data is loaded, change it to false so any component that cares when loading completes can tell the difference.
setData({
loading: false,
...json
});