React渲染Unsplash搜索图片时遇image.map is not a function错误
image.map is not a function Error & Implementing Unsplash Image Search in React Hey there! Let's break down what's going wrong and get your image search feature working smoothly.
Why You're Seeing That Error
The core issue here is that the data you're storing in your image state isn't an array—it's an object. When you call Unsplash's /search/photos API, the response structure looks like this:
{ "results": [...], // This is the actual array of images you need "total": 123, "total_pages": 6 }
Right now, you're setting image to data.data (the entire response object), which doesn't have a map method. That's why you're getting the error!
Step-by-Step Fix & Full Implementation
1. Correctly Extract the Image Array from the API Response
Update your fetchImages function to grab the results array from the API response instead of the whole object. Also, fix the Authorization header format—Unsplash requires it to be Client-ID YOUR_ACCESS_KEY:
const fetchImages = () => { // Don't make a request if the search input is empty if (!searchText.trim()) { setImage([]); return; } setIsLoading(true); axios.get('https://api.unsplash.com/search/photos', { params: { query: searchText }, headers: { Authorization: `Client-ID ${API_KEY}` }, // Fixed auth format }) .then((response) => { // Store the actual image array (response.data.results) setImage(response.data.results); }) .catch((err) => { console.error('Failed to load images:', err); setImage([]); // Reset to empty array on error to avoid future issues }) .finally(() => { setIsLoading(false); }); };
2. Clean Up the ImageList Component
Add a default empty array to the image prop to avoid null/undefined checks, and add basic styling to make your images look organized:
/* eslint-disable jsx-a11y/alt-text */ /* eslint-disable react/prop-types */ import React from 'react'; const ImageList = ({ image = [] }) => { const imgs = image.map(img => ( <img key={img.id} src={img.urls.regular} style={{ width: '100%', height: '200px', objectFit: 'cover', borderRadius: '8px' }} /> )); return ( <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(250px, 1fr))', gap: '15px', marginTop: '20px' }}> {imgs} </div> ); }; export default ImageList;
3. Add Quality-of-Life Improvements (Optional)
Loading State: Let users know when images are being fetched by adding an
isLoadingstate:// Add this state to your SearchPage component const [isLoading, setIsLoading] = useState(false);Then render a loading message in your JSX:
{isLoading && <p style={{ textAlign: 'center', fontSize: '1.2rem' }}>Loading images...</p>}Debounced Search: Prevent excessive API calls by adding a delay before fetching images as the user types. You can use Lodash's
debounceor implement your own:import { useCallback } from 'react'; import { debounce } from 'lodash'; // Inside your SearchPage component const debouncedFetch = useCallback(debounce((query) => { if (query.trim()) { // Reuse your fetch logic here, or adjust fetchImages to accept a query parameter axios.get('https://api.unsplash.com/search/photos', { params: { query }, headers: { Authorization: `Client-ID ${API_KEY}` }, }) .then(res => setImage(res.data.results)) .catch(err => console.error(err)); } else { setImage([]); } }, 500), []); // Update onInputChange to use the debounced function const onInputChange = (e) => { const value = e.target.value; setSearchTerm(value); debouncedFetch(value); };
Full Updated SearchPage Component
import React, { useState } from 'react'; import axios from 'axios'; import SearchBar from '../SearchBar/SearchBar'; import ImageList from '../ImageList/ImageList'; const API_KEY = process.env.REACT_APP_ACCESS_KEY; const SearchPage = () => { const [searchText, setSearchTerm] = useState(''); const [image, setImage] = useState([]); const [isLoading, setIsLoading] = useState(false); const onInputChange = (e) => { setSearchTerm(e.target.value); }; const fetchImages = () => { if (!searchText.trim()) { setImage([]); return; } setIsLoading(true); axios.get('https://api.unsplash.com/search/photos', { params: { query: searchText }, headers: { Authorization: `Client-ID ${API_KEY}` }, }) .then((response) => { setImage(response.data.results); }) .catch((err) => { console.error('Failed to load images:', err); setImage([]); }) .finally(() => { setIsLoading(false); }); }; const onSubmitHandler = (e) => { e.preventDefault(); fetchImages(); }; return ( <div style={{ maxWidth: 1200, margin: '0 auto', padding: '20px' }}> <SearchBar onSubmitHandler={onSubmitHandler} onInputChange={onInputChange} searchText={searchText} /> {isLoading && <p style={{ textAlign: 'center', fontSize: '1.2rem' }}>Loading images...</p>} <ImageList image={image} /> </div> ); }; export default SearchPage;
That's it! This fixes the map error, implements the search functionality correctly, and adds some nice touches to improve the user experience.
内容的提问来源于stack exchange,提问作者SImon Haddad

