0

I am making an application using the Upsplash API. Upon rendering I want to display 30 images, witch works correctly.

import React, { useState, useEffect } from "react"

    const ContextProvider =({ children }) =>{
        const [allPhotos, setAllPhotos] = useState([])
        const [cartItems, setCartItems] = useState([])
        const [imageQuery, setImageQuery] = useState('')
      
        useEffect(() => {
            const url = `https://api.unsplash.com/photos?page=5&per_page=30&client_id=${process.env.REACT_APP_UNSPLASH_KEY}`
    
            async function getPhotos() {
                const photosPromise = await fetch(url)
                const photos = await photosPromise.json()
                setAllPhotos(photos)
            }
            getPhotos()
           
        },[])

I then pass AllPhotos to my Photos.js using my context, and map over allPhotos, passing the photo to my Image component to display information about the image.

 import React, {useContext} from "react"
    import {Context} from "../Context"
    
    
    function Photos(){
        const {allPhotos} = useContext(Context)
    
        const imageElements = allPhotos.map((photo,index) =>(
            <Image key={photo.id}  photo={photo}/>
        ))
        return(
          <>
          <main>
            {imageElements}
          </main>
          </>
        )
    }
    export default Photos




    const Image = ({  photo }) => {
  return (
    <div
      <img src={photo.urls.thumb} className="image-grid" alt="" />
    </div>
    
  )
}

From here the images from the API display and everything is working correctly.

What I want to do now is add a search query, where the users can search for certain images. I made a component for the input value

import React, { useContext } from "react"
import {Context} from "../../Context"

const QueryInput = () =>{

    const {imageQuery, setImageQuery, SearchImage} = useContext(Context)
    return(
        <form onSubmit={SearchImage} >
            <label>
                Search Photos
                <input
                type="text"
                className="query-input"
                placeholder="Search Images"
                value={imageQuery}
                onChange={(e) => setImageQuery(e.target.value) }
                />
            </label>
            <button type="submit">Search Image</button>
        </form>
    )
}
export default QueryInput

I made a searchQuery function in my context

  const SearchImage  = async (e) =>{
           e.preventDefault()
    
        const queryUrl = `https://api.unsplash.com/search/photos? 
         age=5&per_page=30&query=${imageQuery}&client_id=${APP_KEY}`
    
         const response = await fetch(queryUrl)
         const queryPhotos = await response.json();
        
        
        setAllPhotos(prevState => [...prevState, ...queryPhotos])
     
        }

Everything works so far, I can console.log(queryPhotos) and get the users images of the query they searched for. If I search for "stars" I will get a bunch of images with stars.

What im having trouble doing is mapping through allPhotos again and displaying the query search images.

The error im having is

TypeError: queryPhotos is not iterable

I have been at this for awhile. Any information/advice would be greatly appreciated. Any questions about the code or need additional information I can provide it. THANK YOU.

meeshaa
  • 67
  • 6

1 Answers1

0

In short. queryPhotos is not an array.

unsplash api response for api /photos and /search/photos is a bit different. One return an array, while the other is an object, you need to access photos in results

enter image description here

So, change this line from

setAllPhotos(prevState => [...prevState, ...queryPhotos])

to

setAllPhotos(prevState => [...prevState, ...queryPhotos.results])

Should fix your problem.

Doppio
  • 1,576
  • 10
  • 11
  • Doppio, Thanks!! That ended up working! I must have missed that in the documentation, I appreciate you taking the time to help me out. Now when I search for "stars" I have images of stars displaying. Now what I want to do is the images that display upon the first render to be removed, and only have my star images displaying. Any hints to achieving this? – meeshaa Apr 15 '21 at 03:08
  • Doppio, I believe this can be achieved with: setAllPhotos(prevState => [...prevState.slice(15,15), ...queryPhotos.results]) But is there a more efficient way? Again, thanks for your help. – meeshaa Apr 15 '21 at 03:33
  • Just replace allPhotos with new result. `setAllPhotos([...queryPhotos.results])` – Doppio Apr 15 '21 at 05:41
  • I tried this initially, may have had a typo. Everything is working now! Time to add pagination. Thanks again! – meeshaa Apr 15 '21 at 14:01
  • You're welcome :) – Doppio Apr 15 '21 at 14:28