diff --git a/05_react/agenda.md b/05_react/agenda.md index ecaa71e..81184ef 100644 --- a/05_react/agenda.md +++ b/05_react/agenda.md @@ -614,7 +614,6 @@ offene Übungen. --- - ### Tag 41 **Inhalt:** diff --git a/05_react/unterricht/tag44/01_react-api-with-hooks/src/App.jsx b/05_react/unterricht/tag44/01_react-api-with-hooks/src/App.jsx index 0fde633..1077d57 100644 --- a/05_react/unterricht/tag44/01_react-api-with-hooks/src/App.jsx +++ b/05_react/unterricht/tag44/01_react-api-with-hooks/src/App.jsx @@ -1,4 +1,4 @@ -import ChuckNorrisJokes from './components/jokes/ChuckNorrisJokes'; +import ChuckNorrisJokes from './components/jokes/ChuckNorrisJokesAdvanced'; import ListTodo from './components/lists/ListTodo'; import ListUser from './components/lists/ListUser'; @@ -8,9 +8,9 @@ function App(props) { return ( <>
- {/*
+
-
*/} +

API auslesen in React


diff --git a/05_react/unterricht/tag44/01_react-api-with-hooks/src/components/jokes/ChuckNorrisJokesAdvanced.jsx b/05_react/unterricht/tag44/01_react-api-with-hooks/src/components/jokes/ChuckNorrisJokesAdvanced.jsx new file mode 100644 index 0000000..f670f61 --- /dev/null +++ b/05_react/unterricht/tag44/01_react-api-with-hooks/src/components/jokes/ChuckNorrisJokesAdvanced.jsx @@ -0,0 +1,58 @@ +import { useEffect, useState } from 'react'; + +const ChuckNorrisJokes = (props) => { + // const { } = props; + + const [joke, setJoke] = useState(''); + + const getJoke = async (signal) => { + await fetch('https://api.chucknorris.io/jokes/random', { + signal, + }) + .then((res) => { + if (!res.ok) { + throw new Error('HTTP-Response error'); + } + return res.json(); + }) + .then((data) => { + setJoke(data.value); + console.log('1'); + }) + .catch((err) => { + if (err.name === 'AbortError') { + console.warn('Fetch wurde abgebrochen'); + } else if (err instanceof Error) { + console.error('Error', err.message); + } + }); + }; + + useEffect(() => { + const controller = new AbortController(); + + getJoke(controller.signal); + + return () => { + controller.abort(); + }; + }, []); + + // Event-Handler =========== + + // hier wird signal nicht abgebrochen, da kein Abbruch erwünscht + const onClickJoke = () => { + getJoke(); + }; + + return ( +
+
{joke}
+ +
+ ); +}; + +export default ChuckNorrisJokes; diff --git a/05_react/unterricht/tag44/01_react-api-with-hooks/src/components/lists/ListTodo.jsx b/05_react/unterricht/tag44/01_react-api-with-hooks/src/components/lists/ListTodo.jsx index ff72067..07b526c 100644 --- a/05_react/unterricht/tag44/01_react-api-with-hooks/src/components/lists/ListTodo.jsx +++ b/05_react/unterricht/tag44/01_react-api-with-hooks/src/components/lists/ListTodo.jsx @@ -4,29 +4,63 @@ const ListTodo = (props) => { // const { } = props; const [todos, setTodos] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); useEffect(() => { - fetch('https://dummyjson.com/todos/random/6') - .then((res) => res.json()) + // AbortController instanziieren (seit 2019) + const controller = new AbortController(); + + setIsLoading(true); + setError(null); + + fetch('https://dummyjson.com/todos/random/6', { + signal: controller.signal, // signal mitsenden + }) + .then((res) => { + if (!res.ok) { + throw new Error('Http-Request went wrong'); + } + return res.json(); + }) .then((data) => { setTodos(data); }) .catch((err) => { console.error(err); + if (err.name === 'AbortError') { + console.error('Fetch wurde abgebrochen'); + } else if (err instanceof Error) { + console.error(err.message); + setError(err); + } + }) + .finally(() => { + if (!controller.signal.aborted) { + setIsLoading(false); + } }); - return () => {}; + return () => { + // unmount + controller.abort(); // The abort() method of the AbortController interface aborts an asynchronous operation before it has completed. + }; }, []); return ( - + <> + {!isLoading && ( + + )} + {error &&
{error?.message}
} + ); }; export default ListTodo; diff --git a/05_react/unterricht/tag44/01_react-api-with-hooks/src/components/lists/ListUser.jsx b/05_react/unterricht/tag44/01_react-api-with-hooks/src/components/lists/ListUser.jsx index 289377a..216dfe2 100644 --- a/05_react/unterricht/tag44/01_react-api-with-hooks/src/components/lists/ListUser.jsx +++ b/05_react/unterricht/tag44/01_react-api-with-hooks/src/components/lists/ListUser.jsx @@ -3,23 +3,48 @@ import { useEffect, useState } from 'react'; const ListUser = (props) => { // const { } = props; - const [users, setUsers] = useState([]); + const [isLoading, setIsLoading] = useState(false); + // const [error, setError] = useState(null); useEffect(() => { + const controller = new AbortController(); // 2019 + + setIsLoading(true); + axios - .get('https://dummyjson.com/users?limit=6') + .get('https://dummyjson.com/users?limit=100', { + signal: controller.signal, + }) .then(({ data }) => { console.log(data); setUsers(data?.users); }) .catch((err) => { - console.error(err); + if (axios.isCancel(err)) { + console.error('Request abgebrochen:', err.message); + } else { + console.error('Error:', err); + } + }) + .finally(() => { + // Nur wenn die Komponente nicht abgebrochen/unmounted wurde, + // den Lade-Zustand auf false setzen + if (!controller.signal.aborted) { + setIsLoading(false); + } }); - return () => {}; + return () => { + // umount + controller.abort(); + }; }, []); + if (isLoading) { + return

is loading users...

; + } + return (