import { createContext, useSyncExternalStore, useContext, useReducer, Suspense } from 'react';
function assertNever(state) {
throw new Error(`Unexpected state: ${state}`);
}
function createFetchContext() {
return {
cache: {},
};
}
const FetchContext = createContext(createFetchContext());
function createSlice(url) {
var debugUrl = new URL(url).pathname
console.log(debugUrl + ': Created slice')
let state = {
state: 'idle',
}
let listeners = [];
const entry = {
getState: () => state,
dispatch: (action) => {
console.log(debugUrl + ': dispatch: ' + action.type)
switch (action.type) {
case 'reset':
state = {
state: 'idle',
}
break;
case 'success':
state = {
state: 'success',
data: action.payload,
}
break;
case 'error':
state = {
state: 'error',
data: action.payload,
}
break;
default:
return assertNever(action);
}
listeners.forEach(listener => listener());
},
subscribe: (listener) => {
listeners = listeners.toSpliced(-1, 0, listener);
console.log(debugUrl + ': subscribe: ' + listeners.length + " listeners")
return () => {
const index = listeners.indexOf(listener);
if (index>= 0) {
// We make a copy of the listeners to deal with the case of the listeners changing as we loop over them
listeners = listeners.toSpliced(index, 1);
}
console.log(debugUrl + ': unsubscribe: ' + listeners.length + " listeners")
};
},
getStateAndDispatch: () => {
if (state.state === 'idle') {
// The idle state is a phantom state that is never returned to the user
// It is used to indicate that the fetch is in progress
console.log(debugUrl + ': getStateAndDispatch: Started fetching')
const promise = fetch(url).then(response => response.json());
state = {
state: 'loading',
data: promise,
}
promise.then(
data => entry.dispatch({ type: 'success', payload: data }),
error => entry.dispatch({ type: 'error', payload: error }),
);
}
return state;
},
};
return entry;
}
function useFetch(url) {
var context = useContext(FetchContext);
var entry = context.cache[url] ??= createSlice(url);
var state = useSyncExternalStore(entry.subscribe, entry.getStateAndDispatch);
switch (state.state) {
case 'loading':
throw state.data;
case 'success':
return state.data;
case 'error':
throw state.data;
default:
return assertNever(state);
}
}
function Expander({ children, summary }) {
const [opened, onClick] = useReducer((state) => !state, false);
return
User: {data.name}
Email: {data.email}
Phone: {data.phone}
Post: {data.title}
Body: {data.body}
Comment: {data.name}
Email: {data.email}
Body: {data.body}
Todo: {data.title}
Completed: {data.completed ? 'Yes' : 'No'}
Album: {data.title}
Photo: {data.title}
URL: {data.url}
; } export function App () { return (