繁体   English   中英

在反应功能组件内部调用 function

[英]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 组件的内部调用。 这可能由于以下原因之一而发生:

  1. 您可能有不匹配的 React 版本和渲染器(例如 React DOM)
  2. 你可能违反了 Hooks 规则
  3. 你可能在同一个应用程序中有多个 React 副本

我只是在学习反应,所以我被这个问题困住了。

我将 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或任何其他挂钩

看看https://reactjs.org/docs/hooks-rules.html

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM