Created
May 30, 2025 12:32
-
-
Save sunmeat/f8bbe4cdd223f9c66478d1d90d6a7ed0 to your computer and use it in GitHub Desktop.
привязка запроса к ЖЦ компоненты
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, {useState, useEffect, useRef} from 'react'; | |
const styles = { | |
container: { | |
fontFamily: 'Arial, sans-serif', | |
backgroundColor: '#121212', | |
color: '#fff', | |
textAlign: 'center', | |
padding: '2rem', | |
minHeight: '100vh', | |
}, | |
box: { | |
maxWidth: 800, | |
margin: '0 auto', | |
backgroundColor: '#1e1e1e', | |
padding: 20, | |
borderRadius: 10, | |
textAlign: 'left', | |
}, | |
button: { | |
padding: '0.5rem 1rem', | |
margin: '0.5rem', | |
borderRadius: 5, | |
border: 'none', | |
backgroundColor: '#1db954', | |
color: '#fff', | |
cursor: 'pointer', | |
}, | |
buttonHover: { | |
backgroundColor: '#1ed760', | |
}, | |
pre: { | |
backgroundColor: '#2a2a2a', | |
padding: 10, | |
borderRadius: 5, | |
whiteSpace: 'pre-wrap', | |
wordWrap: 'break-word', | |
}, | |
img: { | |
maxWidth: '100%', | |
borderRadius: 10, | |
margin: '1rem 0', | |
}, | |
error: { | |
color: '#ff5555', | |
}, | |
}; | |
const textUrl = | |
'https://gist.githubusercontent.com/sunmeat/5120f241ab0f407138b7c1dc479a2f02/raw/a01e873da40a27bada0192e4e75e632586623329/index.html'; | |
const imageUrl = | |
'https://raw.githubusercontent.com/sunmeat/gallery/main/MyApplication/cats/cat%20(1).jpg'; | |
const jsonObjectUrl = 'https://jsonplaceholder.typicode.com/users/1'; | |
const jsonArrayUrl = 'https://jsonplaceholder.typicode.com/posts'; | |
const postUrl = 'https://petstore.swagger.io/v2/pet'; | |
const FetchExample = () => { | |
// применяем рефы для контроллеров отмены запросов - привязка к ЖЦ компонента | |
const textController = useRef(null); | |
const imageController = useRef(null); | |
const jsonObjectController = useRef(null); | |
const jsonArrayController = useRef(null); | |
const postController = useRef(null); | |
// реф для хранения URL объектов изображений, для их очистки | |
const imageUrlRef = useRef(null); | |
const [textContent, setTextContent] = useState(''); | |
const [textLoading, setTextLoading] = useState(false); | |
const [textError, setTextError] = useState(''); | |
const [imageBlob, setImageBlob] = useState(null); | |
const [imageLoading, setImageLoading] = useState(false); | |
const [imageError, setImageError] = useState(''); | |
const [jsonObject, setJsonObject] = useState(''); | |
const [jsonObjectLoading, setJsonObjectLoading] = useState(false); | |
const [jsonObjectError, setJsonObjectError] = useState(''); | |
const [jsonArray, setJsonArray] = useState(''); | |
const [jsonArrayLoading, setJsonArrayLoading] = useState(false); | |
const [jsonArrayError, setJsonArrayError] = useState(''); | |
const [postResult, setPostResult] = useState(''); | |
const [postLoading, setPostLoading] = useState(false); | |
const [postError, setPostError] = useState(''); | |
// очистка ресурсов при размонтировании компонента | |
useEffect(() => { | |
return () => { | |
// отменяем все активные запросы! | |
textController.current?.abort(); | |
imageController.current?.abort(); | |
jsonObjectController.current?.abort(); | |
jsonArrayController.current?.abort(); | |
postController.current?.abort(); | |
// освобождаем объекты изображений | |
if (imageUrlRef.current) { | |
URL.revokeObjectURL(imageUrlRef.current); | |
} | |
}; | |
}, []); | |
// очистка предыдущего URL объекта при загрузке нового изображения | |
useEffect(() => { | |
return () => { | |
if (imageUrlRef.current) { | |
URL.revokeObjectURL(imageUrlRef.current); | |
} | |
}; | |
}, [imageBlob]); | |
async function loadText() { | |
// отменяем предыдущий запрос если он есть | |
textController.current?.abort(); | |
// создаём новый контроллер для отмены запроса | |
textController.current = new AbortController(); | |
setTextLoading(true); | |
setTextError(''); | |
setTextContent(''); | |
try { | |
const response = await fetch(textUrl, { | |
signal: textController.current.signal // привязываем к контроллеру | |
}); | |
if (!response.ok) | |
throw new Error(`Ошибка: ${response.status} ${response.statusText}`); | |
const content = await response.text(); | |
setTextContent(content); | |
} catch (error) { | |
// игнорируем ошибки отмены запроса | |
if (error.name !== 'AbortError') { | |
setTextError(error.message); | |
} | |
} finally { | |
setTextLoading(false); | |
} | |
} | |
function downloadText() { | |
if (!textContent) return; | |
const blob = new Blob([textContent], {type: 'text/plain'}); | |
const url = URL.createObjectURL(blob); | |
const a = document.createElement('a'); | |
a.href = url; | |
a.download = 'file.txt'; | |
a.click(); | |
URL.revokeObjectURL(url); | |
} | |
async function loadImage() { | |
// отменяем предыдущий запрос если он есть | |
imageController.current?.abort(); | |
// создаём новый контроллер для отмены запроса | |
imageController.current = new AbortController(); | |
setImageLoading(true); | |
setImageError(''); | |
setImageBlob(null); | |
// очищаем предыдущий URL объект | |
if (imageUrlRef.current) { | |
URL.revokeObjectURL(imageUrlRef.current); | |
imageUrlRef.current = null; | |
} | |
try { | |
const response = await fetch(imageUrl, { | |
signal: imageController.current.signal // привязываем к контроллеру | |
}); | |
if (!response.ok) | |
throw new Error(`Ошибка: ${response.status} ${response.statusText}`); | |
const blob = await response.blob(); | |
setImageBlob(blob); | |
} catch (error) { | |
// игнорируем ошибки отмены запроса | |
if (error.name !== 'AbortError') { | |
setImageError(error.message); | |
} | |
} finally { | |
setImageLoading(false); | |
} | |
} | |
function downloadImage() { | |
if (!imageBlob) return; | |
const url = URL.createObjectURL(imageBlob); | |
const a = document.createElement('a'); | |
a.href = url; | |
a.download = 'image.jpg'; | |
a.click(); | |
URL.revokeObjectURL(url); | |
} | |
async function loadJsonObject() { | |
// отменяем предыдущий запрос если он есть | |
jsonObjectController.current?.abort(); | |
// создаём новый контроллер для отмены запроса | |
jsonObjectController.current = new AbortController(); | |
setJsonObjectLoading(true); | |
setJsonObjectError(''); | |
setJsonObject(''); | |
try { | |
const response = await fetch(jsonObjectUrl, { | |
signal: jsonObjectController.current.signal // привязываем к контроллеру | |
}); | |
if (!response.ok) | |
throw new Error(`Ошибка: ${response.status} ${response.statusText}`); | |
const json = await response.json(); | |
setJsonObject(JSON.stringify(json, null, 2)); | |
} catch (error) { | |
// игнорируем ошибки отмены запроса | |
if (error.name !== 'AbortError') { | |
setJsonObjectError(error.message); | |
} | |
} finally { | |
setJsonObjectLoading(false); | |
} | |
} | |
function downloadJsonObject() { | |
if (!jsonObject) return; | |
const blob = new Blob([jsonObject], {type: 'application/json'}); | |
const url = URL.createObjectURL(blob); | |
const a = document.createElement('a'); | |
a.href = url; | |
a.download = 'user.json'; | |
a.click(); | |
URL.revokeObjectURL(url); | |
} | |
async function loadJsonArray() { | |
// отменяем предыдущий запрос если он есть | |
jsonArrayController.current?.abort(); | |
// создаём новый контроллер для отмены запроса | |
jsonArrayController.current = new AbortController(); | |
setJsonArrayLoading(true); | |
setJsonArrayError(''); | |
setJsonArray(''); | |
try { | |
const response = await fetch(jsonArrayUrl, { | |
signal: jsonArrayController.current.signal // привязываем к контроллеру | |
}); | |
if (!response.ok) | |
throw new Error(`Ошибка: ${response.status} ${response.statusText}`); | |
const json = await response.json(); | |
setJsonArray(JSON.stringify(json, null, 2)); | |
} catch (error) { | |
// игнорируем ошибки отмены запроса | |
if (error.name !== 'AbortError') { | |
setJsonArrayError(error.message); | |
} | |
} finally { | |
setJsonArrayLoading(false); | |
} | |
} | |
function downloadJsonArray() { | |
if (!jsonArray) return; | |
const blob = new Blob([jsonArray], {type: 'application/json'}); | |
const url = URL.createObjectURL(blob); | |
const a = document.createElement('a'); | |
a.href = url; | |
a.download = 'posts.json'; | |
a.click(); | |
URL.revokeObjectURL(url); | |
} | |
async function sendPost() { | |
// отменяем предыдущий запрос если он есть | |
postController.current?.abort(); | |
// создаём новый контроллер для отмены запроса | |
postController.current = new AbortController(); | |
setPostLoading(true); | |
setPostError(''); | |
setPostResult(''); | |
try { | |
const response = await fetch(postUrl, { | |
method: 'POST', | |
headers: {'Content-Type': 'application/json'}, | |
body: JSON.stringify({name: 'Fluffy', status: 'available'}), | |
signal: postController.current.signal // привязываем к контроллеру | |
}); | |
if (!response.ok) | |
throw new Error(`Ошибка: ${response.status} ${response.statusText}`); | |
const json = await response.json(); | |
setPostResult(JSON.stringify(json, null, 2)); | |
} catch (error) { | |
// игнорируем ошибки отмены запроса | |
if (error.name !== 'AbortError') { | |
setPostError(error.message); | |
} | |
} finally { | |
setPostLoading(false); | |
} | |
} | |
return ( | |
<div style={styles.container}> | |
<div style={styles.box}> | |
<h2>Текстовый файл</h2> | |
<button style={styles.button} onClick={loadText} disabled={textLoading}> | |
{textLoading ? 'Загрузка...' : 'Загрузить текст'} | |
</button> | |
{textContent && ( | |
<> | |
<p>Текстовый файл загружен:</p> | |
<pre style={styles.pre}>{textContent}</pre> | |
<button style={styles.button} onClick={downloadText}> | |
Скачать файл | |
</button> | |
</> | |
)} | |
{textError && <p style={styles.error}>{textError}</p>} | |
</div> | |
<div style={styles.box}> | |
<h2>Изображение</h2> | |
<button style={styles.button} onClick={loadImage} disabled={imageLoading}> | |
{imageLoading ? 'Загрузка...' : 'Загрузить изображение'} | |
</button> | |
{imageBlob && ( | |
<> | |
<p>Изображение загружено:</p> | |
<img | |
style={styles.img} | |
src={(() => { | |
// создаём и сохраняем URL объект для последующей очистки | |
if (imageUrlRef.current) { | |
URL.revokeObjectURL(imageUrlRef.current); | |
} | |
imageUrlRef.current = URL.createObjectURL(imageBlob); | |
return imageUrlRef.current; | |
})()} | |
alt="Загруженное" | |
/> | |
<button style={styles.button} onClick={downloadImage}> | |
Скачать изображение | |
</button> | |
</> | |
)} | |
{imageError && <p style={styles.error}>{imageError}</p>} | |
</div> | |
<div style={styles.box}> | |
<h2>JSON-объект</h2> | |
<button | |
style={styles.button} | |
onClick={loadJsonObject} | |
disabled={jsonObjectLoading} | |
> | |
{jsonObjectLoading ? 'Загрузка...' : 'Загрузить JSON-объект'} | |
</button> | |
{jsonObject && ( | |
<> | |
<p>JSON-объект загружен:</p> | |
<pre style={styles.pre}>{jsonObject}</pre> | |
<button style={styles.button} onClick={downloadJsonObject}> | |
Скачать JSON | |
</button> | |
</> | |
)} | |
{jsonObjectError && <p style={styles.error}>{jsonObjectError}</p>} | |
</div> | |
<div style={styles.box}> | |
<h2>JSON-массив</h2> | |
<button | |
style={styles.button} | |
onClick={loadJsonArray} | |
disabled={jsonArrayLoading} | |
> | |
{jsonArrayLoading ? 'Загрузка...' : 'Загрузить JSON-массив'} | |
</button> | |
{jsonArray && ( | |
<> | |
<p>JSON-массив загружен:</p> | |
<pre style={styles.pre}>{jsonArray}</pre> | |
<button style={styles.button} onClick={downloadJsonArray}> | |
Скачать JSON | |
</button> | |
</> | |
)} | |
{jsonArrayError && <p style={styles.error}>{jsonArrayError}</p>} | |
</div> | |
<div style={styles.box}> | |
<h2>POST-запрос</h2> | |
<button style={styles.button} onClick={sendPost} disabled={postLoading}> | |
{postLoading ? 'Отправка...' : 'Отправить POST'} | |
</button> | |
{postResult && ( | |
<> | |
<p>Питомец успешно добавлен в магазин:</p> | |
<pre style={styles.pre}>{postResult}</pre> | |
</> | |
)} | |
{postError && <p style={styles.error}>{postError}</p>} | |
</div> | |
</div> | |
); | |
}; | |
export default FetchExample; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment