Home > Enterprise >  Call function inside of a react functional component
Call function inside of a react functional component

Time:09-22

I had my component like that before modifying and it worked.

ForecastButtons.js

export const ForecastButtons = ({ city }) => {
  const [payload, setPayload] = useState(null)
  const [error, setError] = useState(null)
  const [loading, setLoading] = useState(true)
  
  const fetchCityData = () => {
    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))
    
  }

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

  return(
    <div className="sm:col-span-2">
      <p className="block text-sm font-medium text-gray-700">Select forecast</p>
        <button onClick={fetchCityData} className="mt-1 bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded" type='button'>
          Today
        </button>
        <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>
  )
}

For the purpose of running a unit test for the fetchCityData function only, I understand, that I need to extract this function and then somehow use in my ForecastButtons component. So I tried:

ForecastButtons.js

export const FetchCityData = () => {
  const [payload, setPayload] = useState(null)
  const [error, setError] = useState(null)
  const [loading, setLoading] = useState(true)
  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))
}


export const ForecastButtons = ({ city, payload, setPayload }) => {

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

  return(
    <div className="sm:col-span-2">
      <p className="block text-sm font-medium text-gray-700">Select forecast</p>
        <button onClick={FetchCityData} className="mt-1 bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded" type='button'>
          Today
        </button>
        <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>
  )
}

This throws following:

Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:

  1. You might have mismatching versions of React and the renderer (such as React DOM)
  2. You might be breaking the Rules of Hooks
  3. You might have more than one copy of React in the same app

I am just learning react, so I am stuck with this issue.

CodePudding user response:

You cannot use hooks outside the body of your functional component or a custom hook

There are multiple ways to fix that, like using a custom hook or passing the callbacks to change your state to your function

export const FetchCityData = (setPayload, setError, setLoading) => {
    const options = {
      method: `POST`,
    };
    fetch(`/api/weather?city=${city}`, options)
    .then((response) => {
      if(response.ok){
        return response.json().then(data => setPayload(data))
      }
        throw new Error('Api is not available') 
      })
    .catch(error => {
      console.error('Error fetching data: ', error)
      setError(error)
    })
    .finally(() => setLoading(false))
  }
  
  
  export const ForecastButtons = ({ city, payload, setPayload }) => {
    const [payload, setPayload] = useState(null)
    const [error, setError] = useState(null)
    const [loading, setLoading] = useState(true)
  
    const location = payload?.location?.name;
    const currentTemp = payload?.current?.temp_c;
  
    return(
      <div className="sm:col-span-2">
        <p className="block text-sm font-medium text-gray-700">Select forecast</p>
          <button onClick={() => FetchCityData(setPayload, setError, setLoading)} className="mt-1 bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded" type='button'>
            Today
          </button>
          <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>
    )
  }

You could also declare the function inside your component

  export const ForecastButtons = ({ city, payload, setPayload }) => {
    const [payload, setPayload] = useState(null)
    const [error, setError] = useState(null)
    const [loading, setLoading] = useState(true)
  
    const location = payload?.location?.name;
    const currentTemp = payload?.current?.temp_c;

    const FetchCityData = () => {
        const options = {
          method: `POST`,
        };
        fetch(`/api/weather?city=${city}`, options)
        .then((response) => {
          if(response.ok){
            return response.json().then(data => setPayload(data))
          }
            throw new Error('Api is not available') 
          })
        .catch(error => {
          console.error('Error fetching data: ', error)
          setError(error)
        })
        .finally(() => setLoading(false))
    }
  
return(
  <div className="sm:col-span-2">
    <p className="block text-sm font-medium text-gray-700">Select forecast</p>
      <button onClick={FetchCityData} className="mt-1 bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded" type='button'>
        Today
      </button>
      <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>
)
  }

No matter how you do just don't use useState or any other hook outside a functional component

Take a look at https://reactjs.org/docs/hooks-rules.html

CodePudding user response:

In this FetchCityData(), what you will return?

  • Related