[英]Call function inside of a react functional component
在修改之前我有这样的组件并且它有效。
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>
)
}
我明白,为了仅为fetchCityData
function 运行单元测试,我需要提取此 function,然后以某种方式在我的 ForecastButtons 组件中使用。 所以我尝试了:
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>
)
}
这会抛出以下内容:
错误:无效挂钩调用。 钩子只能在 function 组件的内部调用。 这可能由于以下原因之一而发生:
我只是在学习反应,所以我被这个问题困住了。
我将 fetchCityData 外包给另一个文件,我只是在其中进行 API 调用:
const fetch = require('cross-fetch');
const dev = process.env.NODE_ENV !== 'production';
const server = dev ? 'http://localhost:3000' : 'https://your_deployment.server.com';
const fetchCityData = (city) => {
const options = {
method: `POST`,
};
return fetch(`${server}/api/weather?city=${city}`, options)
.then((response) => {
if(response.ok){
return response.json()
}
throw new Error('Api is not available')
})
.catch(error => {
console.error('Error fetching data in city data: ', error)
})
}
并像这样在我的组件中使用 fetchCityData :
import React, { useState, useEffect } from 'react';
import { fetchCityData } from '../lib/cityData'
export const ForecastButtons = ({ city }) => {
const [payload, setPayload] = useState(null)
const [error, setError] = useState(null)
const [loading, setLoading] = useState(true)
const getData = () => {
fetchCityData(city).then((payload) => setPayload(payload));
console.log(payload)
}
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={getData} 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>
)
}
您不能在功能组件的主体或自定义钩子之外使用钩子
有多种方法可以解决这个问题,例如使用自定义挂钩或传递回调以将您的 state 更改为您的 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>
)
}
您还可以在组件内部声明 function
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>
)
}
无论您怎么做,都不要在功能组件之外使用useState
或任何其他挂钩
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.