1

I'm a complete beginner in react and I have written a fetch component which returns data from an API using a usefetch function . In my app I can manually change the input to get different data from the API but what I want is to have an input field and a button that when it is clicked it returns the new data from the API . With my code below I can fetch data only once when the component mounts and if i give input nothing happens .

import React , {useState ,useEffect} from 'react';
import useFetch from './fetch'; //fetch api  code imported 
import SearchIcon from '@material-ui/icons/Search';
import InputBase from '@material-ui/core/InputBase';
import Button from '@material-ui/core/Button';

  function City(){
    
    
    const searchStyle = {
      display:"flex",
      justifyContent:"flex-start",
      position:"absolute",
      top:"400px",
      left:"40%",
    } 

        

    
    const [inputVal , setInputVal]  = useState(''); //store input value 
    const [place,setPlace] = useState('london');  //get london data from api by manually changing value new data is succesfully dislayed 
    const {loading , pics}  = useFetch(place); //fetch data 
    const [images , setImages] = useState([]); //store fetched imgs 

    const removeImage = (id) =>{
      setImages((oldState)=>oldState.filter((item)=> item.id !== id))
    }


    useEffect(()=>{
      setImages(pics);
    } , [pics] ) 
    
    //load and display fetched images 
    return (<div className="city-info">
       
      {
        !loading ? 
        
          (images.length>0 && images.map((pic) =>{
            return  <div className="info" key = {pic.id}>
                     <span className="close" onClick= {()=>removeImage(pic.id)} >
                        <span
                          className="inner-x">
                          &times;
                        </span>
                      </span>
                      <img src = {pic.src.original} alt ="img"/> 
                      <div style = {{position:"absolute" ,margin:"10px"}}> 
                        <strong>From : </strong> 
                         {pic.photographer}  
                      </div>
                    </div>
          })
        
        ):<div> Loading   </div>

      }

        <div  style = {searchStyle} >
            <SearchIcon />
             //when input changes store it 
            <InputBase onChange={(e)=>setInputVal(e.target.value)}   placeholder="Enter input" style= {{backgroundColor:"lightgrey"}}/>
            //new fetch data based on input by clicking on button nothing happens onclick 
            <Button onClick= {()=>setPlace(inputVal)} color="primary" variant = "contained" > Find </Button>
        </div>  

    </div>);
  }

export default City;

fetch.js my code to connect to api :

import { useState, useEffect } from 'react';

function useFetch(url){

  
  const [loading ,setLoading] = useState(false);
  const [query,setQuery] = useState(url);
  const [pics,setPics]  = useState([]);
  
  const getPics = async()=>{
    setLoading(true);
      const response = await fetch(
        `https://api.pexels.com/v1/search?query=${query}&per_page=4`,
        {
          method:"GET",
          headers:{
            Accept:"application/json",
            Authorization:key
          }
        }
      );
    const result = await response.json();
    setPics(result.photos ?? []);
    setLoading(false);
  }
  
  
  useEffect(()=>{
    getPics();
  },[query]);


  return {loading , pics ,query  ,setQuery , getPics};

}

export default useFetch;

I think that my place value changes when my button is clicked but my fetch function is not reloaded and I just change a value . I would really appreciate your help .

3
  • can you show the code that makes a connection between your API and the component, are using a promise-based HTTP Client for the request, if it is then info will be great to solve your problem Commented Oct 12, 2020 at 17:49
  • @nedam Kailash yes one minute Commented Oct 12, 2020 at 17:50
  • i have done some API calls using axios, you can check in github account its there if you want Commented Oct 12, 2020 at 17:55

2 Answers 2

2

You can create a new useEffect and then add the place to the useEffect dependencies to create a side effect to call the API again once the value of the place variable changes:

  // return the read function as well so you can re-fech the data whenever you need it
  const {loading , pics, readData}  = useFetch(place);
  
  useEffect(() => {
    readData(place);
    setImages(pics)
  }, [place]);

This will give you fresh data for each button click.

Sign up to request clarification or add additional context in comments.

Comments

1

The problem is useFetch is storing the initial url passed into useState:

const [query,setQuery] = useState(url);

When place gets updated, useFetch never uses it and the effect is never going to be re-triggered. I think if you remove this state from useFetch completely, it ought to work as you expect:

import { useState, useEffect } from 'react';

function useFetch(url) {
  const [loading, setLoading] = useState(false);
  const [pics, setPics]  = useState([]);
  
  const getPics = async () => {
    setLoading(true);
    const response = await fetch(
      `https://api.pexels.com/v1/search?query=${query}&per_page=4`,
      {
        method: "GET",
        headers: {
          Accept: "application/json",
          Authorization: key
        }
      }
    );
    const result = await response.json();
    setPics(result.photos ?? []);
    setLoading(false);
  }
  
  
  useEffect(()=>{
    getPics();
  }, [url]);


  return { loading, pics, getPics };

}

export default useFetch;

1 Comment

thank you so much for your help . It took me like 2 hours to get through this

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.