I'll write the code for a Pokemon listing app using the specified stack, merged into a single `index.js` file.
```ja vascript
import React, { useEffect } from 'react';
import ReactDOM from 'react-dom/client';
import { configureStore, createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import { Provider, useSelector, useDispatch } from 'react-redux';
import axios from 'axios';
import { List, Card, A vatar, Row, Col, Input, Spin, Alert } from 'antd';
import { SearchOutlined } from '@ant-design/icons';
const { Search } = Input;
// API configuration
const POKEAPI_BASE = 'https://pokeapi.co/api/v2';
const POKEMON_LIMIT = 50;
// Redux slice for pokemons
const pokemonSlice = createSlice({
name: 'pokemons',
initialState: {
items: [],
filteredItems: [],
loading: false,
error: null,
searchQuery: ''
},
reducers: {
setSearchQuery: (state, action) => {
state.searchQuery = action.payload;
if (action.payload.trim() === '') {
state.filteredItems = state.items;
} else {
const query = action.payload.toLowerCase();
state.filteredItems = state.items.filter(pokemon =>
pokemon.name.toLowerCase().includes(query)
);
}
}
},
extraReducers: (builder) => {
builder
.addCase(fetchPokemons.pending, (state) => {
state.loading = true;
state.error = null;
})
.addCase(fetchPokemons.fulfilled, (state, action) => {
state.loading = false;
state.items = action.payload;
state.filteredItems = action.payload;
})
.addCase(fetchPokemons.rejected, (state, action) => {
state.loading = false;
state.error = action.error.message;
});
}
});
// Thunk for fetching pokemons
const fetchPokemons = createAsyncThunk(
'pokemons/fetchPokemons',
async () => {
const response = await axios.get(`${POKEAPI_BASE}/pokemon?limit=${POKEMON_LIMIT}`);
const pokemons = await Promise.all(
response.data.results.map(async (pokemon, index) => {
try {
const details = await axios.get(pokemon.url);
return {
id: details.data.id,
name: details.data.name,
image: details.data.sprites.front_default,
types: details.data.types.map(t => t.type.name)
};
} catch (error) {
return {
id: index + 1,
name: pokemon.name,
image: null,
types: []
};
}
})
);
return pokemons;
}
);
const { setSearchQuery } = pokemonSlice.actions;
// Store configuration
const store = configureStore({
reducer: {
pokemons: pokemonSlice.reducer
}
});
// Main App Component
const PokemonApp = () => {
const dispatch = useDispatch();
const { items, filteredItems, loading, error, searchQuery } = useSelector(state => state.pokemons);
useEffect(() => {
dispatch(fetchPokemons());
}, [dispatch]);
const handleSearch = (value) => {
dispatch(setSearchQuery(value));
};
if (error) {
return (
);
}
return (
Pokémon Directory
}
onChange={(e) => handleSearch(e.target.value)}
onSearch={handleSearch}
style={{ marginBottom: '30px' }}
value={searchQuery}
/>
{loading ? (
) : (
(
) : (
No Image
)
}
>
{pokemon.name}
}
description={
ID: #{pokemon.id.toString().padStart(3, '0')}
{pokemon.types.length > 0 && (
Type: {pokemon.types.map(type => (
{type}
))}
)}
}
/>
)}
locale={{ emptyText: searchQuery ? 'No matching Pokémon found' : 'No Pokémon a vailable' }}
/>
)}
);
};
// Render the app
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
);
```
This single-file Pokemon app includes:
1. **Redux Toolkit** implementation with `createSlice` for state management
2. **Thunk middleware** for async API calls to PokeAPI
3. **Axios** for HTTP requests to fetch Pokemon data and sprites
4. **Ant Design** components for UI (List, Card, A vatar, Search, Spin, Alert)
5. **Search functionality** to filter Pokemon by name
6. **Responsive grid layout** with different columns for different screen sizes
7. **Error handling** with user-friendly error messages
8. **Loading states** with spinner animation
9. **Pokemon details** including ID, name, image from sprites endpoint, and types
10. **Card-based display** with hover effects
The app fetches 50 Pokemon from the PokeAPI, retrieves their details including sprites, and displays them in a responsive grid with search functionality.
这就是Senior Frontend Developer的全部内容了,希望以上内容对小伙伴们有所帮助,更多详情可以关注我们的菜鸟游戏和软件相关专区,更多攻略和教程等你发现!