Skip to content

Instantly share code, notes, and snippets.

@sunmeat
Created May 30, 2025 12:05
Show Gist options
  • Save sunmeat/0bd5d4a5fcf82b0344564029d521cd59 to your computer and use it in GitHub Desktop.
Save sunmeat/0bd5d4a5fcf82b0344564029d521cd59 to your computer and use it in GitHub Desktop.
Jokes API + fetch REACT
import React, {useState} from 'react';
// CSS-in-JS стили
const styles = {
body: {
fontFamily: "'Courier New', monospace",
backgroundColor: '#121212',
color: '#ffffff',
padding: '2rem',
margin: 0,
minHeight: '100vh',
},
container: {
maxWidth: '800px',
margin: '0 auto',
backgroundColor: '#1e1e1e',
padding: '20px',
borderRadius: '10px',
},
button: {
padding: '0.5rem 1rem',
margin: '10px',
borderRadius: '5px',
border: 'none',
backgroundColor: '#1db954',
color: '#ffffff',
cursor: 'pointer',
fontSize: '16px',
},
jokeBox: (frameColor, textColor) => ({
margin: '20px 0',
padding: '10px',
border: `2px solid ${frameColor}`,
borderRadius: '5px',
width: 'fit-content',
maxWidth: '100%',
color: textColor,
boxSizing: 'border-box',
}),
jokeHeader: {
textAlign: 'center',
marginBottom: '10px',
fontWeight: 'bold',
},
jokeContent: {
whiteSpace: 'pre-wrap',
wordWrap: 'break-word',
padding: '0 10px',
},
error: {
color: '#ff5555',
margin: '10px 0',
},
};
const colorPairs = [
{frame: '#8B0000', text: '#FF0000'},
{frame: '#006400', text: '#00FF00'},
{frame: '#00008B', text: '#0000FF'},
{frame: '#BDB76B', text: '#FFFF00'},
{frame: '#008B8B', text: '#00FFFF'},
{frame: '#8B008B', text: '#FF00FF'},
];
const getFrameAndTextColors = () => {
const random = Math.floor(Math.random() * colorPairs.length);
return colorPairs[random];
};
const wrapText = (text, lineWidth) => {
const lines = [];
const splitByNewLine = text.split('\n');
for (const part of splitByNewLine) {
let remaining = part;
while (remaining.length > lineWidth) {
let splitIndex = remaining.lastIndexOf(' ', lineWidth);
if (splitIndex <= 0) splitIndex = lineWidth;
lines.push(remaining.substring(0, splitIndex));
remaining = remaining.substring(splitIndex).trimStart();
}
lines.push(remaining);
}
return lines;
};
const getJokeContent = (joke) => {
return joke.type === 'single' ? joke.joke : `${joke.setup}\n${joke.delivery}`;
};
const JokeBox = ({joke, index}) => {
const {frame, text} = getFrameAndTextColors();
const wrapped = wrapText(getJokeContent(joke), 48); // boxWidth - 4
return (
<div style={styles.jokeBox(frame, text)}>
<div style={styles.jokeHeader}>Шутка #{index}</div>
<div style={styles.jokeContent}>{wrapped.join('\n')}</div>
</div>
);
};
const JokeApp = () => {
const [jokes, setJokes] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const getJoke = async () => {
try {
const url = new URL('https://v2.jokeapi.dev/joke/Programming');
const res = await fetch(url, {
method: 'GET',
headers: {Accept: 'application/json'},
});
if (!res.ok) throw new Error(`Ошибка HTTP: ${res.status}`);
return await res.json();
} catch (err) {
console.error(err);
return null;
}
};
const fetchJokes = async () => {
setLoading(true);
setError('');
setJokes([]);
const results = [];
let attempts = 0;
while (results.length < 50 && attempts < 70) {
const joke = await getJoke();
if (joke) results.push(joke);
attempts++;
}
if (results.length === 0) {
setError('Не удалось загрузить шутки.');
} else {
setJokes(results);
}
setLoading(false);
};
return (
<div style={styles.body}>
<div style={styles.container}>
<h2>Шутки на fetch API</h2>
<button style={styles.button} onClick={fetchJokes}>
Загрузить 50 шуток
</button>
{loading && <p>Загрузка шуток...</p>}
{error && <p style={styles.error}>{error}</p>}
{jokes.map((joke, index) => (
<JokeBox joke={joke} key={index} index={index + 1}/>
))}
{!loading && jokes.length > 0 && <p>Загрузка завершена!</p>}
</div>
</div>
);
};
export default JokeApp;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment