0

I am new to react and I kind of did smth, which I don't really understand. I am fetching weather data from api (using next.js so, the route is in api folder). Before using Autoselect I used normal input with a ref and the function took the input value and posted it to my endpoint, the weather data came back on button click. Now since I integrated Autoselect, the data gets fetched on select. Here is the code

import { useState, useEffect } from 'react';
import { ReactSearchAutocomplete } from 'react-search-autocomplete'

export const SearchBar = () => {
  const [city, setCity] = useState('')
  const [payload, setPayload] = useState(null)
  const [error, setError] = useState(null)
  const [loading, setLoading] = useState(true)

  const items = [
    {
      id: 0,
      name: 'London'
    },
    {
      id: 1,
      name: 'Long Beach'
    },
    {
      id: 2,
      name: 'Moscow'
    },
    {
      id: 3,
      name: 'Mosul'
    },
    {
      id: 4,
      name: 'Hamburg'
    }
  ]


 
  const handleOnSelect = (item) => {
    // the item selected
    const city = item?.name
    setCity(city)   
  }

  useEffect(() => {
    city && fetchCityData(city);
  }, [city])


  const fetchCityData = (city) => {
    const options = {
      method: `POST`,
    };
    fetch(`/api/weather?city=${city}`, options)
    .then((response) => {
      if(response.ok){
        return response.json().then(setPayload)
      }
        throw new Error('Api is not available') 
      })
    .catch(error => {
      console.error('Error fetching data: ', error)
      setError(error)
    })
    .finally(setLoading(false))
  }

  console.log(payload)

  const location = payload?.location?.name;
  const currentTemp = payload?.current?.temp_c;

  return(
    <div >
      <label htmlFor="email" className="block text-sm font-medium text-gray-700">
        Search city
      </label>
      <div className="grid grid-cols-5 gap-4">
        <div className="col-span-3">
          <div className="mt-1">
            <ReactSearchAutocomplete
              id="city"
              name="city"
              type="text"
              items={items}
              onSelect={handleOnSelect}
              autoFocus
              autoComplete="off"
              className="py-3 px-4 block w-full shadow-sm focus:ring-blue-500 focus:border-blue-500 border-gray-300 rounded-md"
            />
          </div>
        </div>
        <div className="col-span-2 mt-1">
          <button className="mt-1 bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" type='button' onClick={fetchCityData}>
            Check weather
          </button>
        </div>
      </div>
      <p key={city?.location?.id} className='my-5'>
        { location ? `Current weather in ${location} is ${currentTemp} degrees ` : 'Please search for city to see current weather'}
      </p>
    </div>
  )
}

So, my first question is, how is it even possible, as I do a post request with the function fetchCityData, which is not actually attached to onSelect. And second question, of course, how to correct this. First fetch and show weather data on button click. At the moment, the [object Object] gets posted to my endpoint, so, of course I get an error. I would really like to understand what is happening.

SearchBar.js?2362:50 POST http://localhost:3000/api/weather?city=[object%20Object] 400 (Bad Request)

1 Answer 1

1

Check this sandbox: https://codesandbox.io/s/quizzical-joana-0wdes?file=/src/App.js About Question 1

It's happening because you have:

  const handleOnSelect = (item) => {
    // the item selected
    const city = item?.name
    setCity(city)   
  }

  useEffect(() => {
    city && fetchCityData(city);
  }, [city])

Basically, you're setting city value on handleSelect (which is needed, for sure). But also, you have defined an effect after any change on city with useEffect and that effect is fetchCityData.

About Question 2

So, you have to delete

  useEffect(() => {
    city && fetchCityData(city);
  }, [city])

And it won't be triggered on select.

About [object Object] error

~Also, based on the error of the object object you have 2 options: set city as item?.name or fetch by city.name (It's because now your cities are objects with id and name, previously they were just one string) It will affect other occurrences of the city in your code.~

Was because the button was sending the event as a parameter and fetchCityData was using that instead of city. So, we have to write fetchCityData to get the city from the state and not from the event.

At the end your code should looks like:

  const handleOnSelect = (item) => {
    // the item selected
    const city = item?.name
    setCity(city)   
  }

  const fetchCityData = () => {
    const options = {
      method: `POST`,
    };
    fetch(`/api/weather?city=${city.name}`, options)
    .then((response) => {
      if(response.ok){
        return response.json().then(setPayload)
      }
        throw new Error('Api is not available') 
      })
    .catch(error => {
      console.error('Error fetching data: ', error)
      setError(error)
    })
    .finally(setLoading(false))
  }
Sign up to request clarification or add additional context in comments.

6 Comments

I thought city is already set and this is an item.name, so basically I was hoping, that this kinda stands for input value. Trying city.name in request gives me city=undefined, trying item?.name throws error, cause obviously the function doesn't know what item ist
and, what is really crazy console.log(city) gives back SyntheticBaseEvent {_reactName: "onClick", _targetInst: null, type: "click", nativeEvent: PointerEvent, target: button.mt-1.bg-blue-500.hover:bg-blue-700.text-white.font-bold.py-2.px-4.rounded, …}altKey: falsebubbles: truebutton: 0buttons: 0cancelable: trueclientX: 965clientY: 237ctrlKey: falsecurrentTarget: nulldefaultPrevented: falsedetail: 1eventPhase: 3getModifierState: ƒ modifierStateGetter(keyArg)isDefaultPrevented: ƒ functionThatReturnsFalse()isPropagationStopped: ƒ functionThatReturnsFalse()isTrusted: truemetaKey ........
I found one more issue. You have on the button onClick={fetchCityData} it will trigger data but will give value of the event to that function. So, you have to redefine your function as ` const fetchCityData = () => {`
Just exactly was saying that!
|

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.