钩子useEffect中不正确的依赖行为?

时间:2019-10-20 17:34:53

标签: javascript reactjs asynchronous react-hooks

我在useEffect内遇到无限循环问题。我想仅在id依赖项更改时发出新请求。当我不传递数据时,setData就会出现问题。我的数据状态未更新。当我将数据传递给依赖项时,我遇到了无限循环。如何修复它,为什么?

import React, {useEffect, useState} from 'react';
import LeftArrow from './LeftArrow';
import RightArrow from './RightArrow';
import SlideItem from './SlideItem';

const Slide = () => {
    const [id, setId] = useState(0);
    const [data, setData] = useState({currencies:[], isFetching:false});

    const images = [
        "https://images.pexels.com/photos/672532/pexels-photo-672532.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
        "https://images.pexels.com/photos/773471/pexels-photo-773471.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
        "https://images.pexels.com/photos/64271/queen-of-liberty-statue-of-liberty-new-york-liberty-statue-64271.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940"
    ];

    useEffect(()=> {
        const getCurrentCurrency = async () => {
            try{
                setData({currencies: data.currencies, isFetching: true});
                console.log("data", data.isFetching);
                const currencyArr = [];
                const response = await fetch(`https://api.exchangeratesapi.io/latest?base=GBP`);
                const responseData  = await response.json();
                console.log(responseData);
                const {EUR:euro ,CHF:franc, USD: dolar} = responseData.rates;
                currencyArr.push(euro,franc,dolar);
                console.log(currencyArr);
                setData({currencies: currencyArr, isFetching: false});
                console.log("currencies", data);     
            }
            catch (e) {
                console.log(e);
                setData({currencies: data.currencies, isFetching: false});
            }
        };
        getCurrentCurrency();
    }, [id]);



    const goToPrevSlide = () => {
      //  id === 0 ? setId(2) : setId(id-1);
    }
    const goToNextSlide = () =>{
      //  id === 2 ? setId(0) : setId(id+1);
    }


    return(

        <div className="slide">
            <div className="slide-wrapper"
                style={{
                    transform: `translateX(500px)`,
                    transition: 'transform ease-out 0.45s'
                  }}
            >
             {
                 currencies.map((currency, i, images) => (
                  <SlideItem currency={currency} key={i} imageUrl={images[i]} />
             ))
            }
            </div>
            <LeftArrow 
                 goToPrevSlide={goToPrevSlide}
            />
            <RightArrow 
                 goToNextSlide={goToNextSlide}    
            />  
        </div>
    );
}

export default Slide;

2 个答案:

答案 0 :(得分:0)

这是一个示例,您可以使用useDispatch进行操作,它是更多代码,但是您可以将reducer从组件中取出到单独的文件中,并且还应将操作类型和操作创建者放在单独的文件中。

const Slide = () => {
  const [id, setId] = React.useState(0);
  const [data, dispatch] = React.useReducer(
    //you can move this function to a separate file and import it
    (state, action) => {
      const { type } = action;
      if (type === 'LOAD') {//action types should be defined as constants
        return {
          ...state,
          loading: true,
        };
      }
      if (type === 'RECEIVED') {
        const currencies = action.payload;
        return {
          currencies,
          loading: false,
        };
      }
      if (type === 'ERROR') {
        return {
          ...state,
          loading: false,
        };
      }
      return state;
    }
  );

  const images = [
    'https://images.pexels.com/photos/672532/pexels-photo-672532.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940',
    'https://images.pexels.com/photos/773471/pexels-photo-773471.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940',
    'https://images.pexels.com/photos/64271/queen-of-liberty-statue-of-liberty-new-york-liberty-statue-64271.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940',
  ];

  useEffect(() => {
    const getCurrentCurrency = async () => {
      try {
        //should create action creator functions so you can do
        //dispatch(load()) put the action creators in separate file
        //usually called actions.js
        dispatch({ type: 'LOAD' });
        console.log('loading');
        //not sure how this depends on id since you are not using id
        //  in the request or anywhere in the effect
        const response = await fetch(
          `https://api.exchangeratesapi.io/latest?base=GBP`
        );
        const responseData = await response.json();
        console.log(responseData);
        const {
          EUR: euro,
          CHF: franc,
          USD: dollar,
        } = responseData.rates;
        const currencies = [euro, franc, dollar];
        console.log(currencies);
        dispatch({
          type: 'RECEIVED',
          payload: currencies,
        });
        console.log('currencies', currencies);
      } catch (e) {
        console.log(e);
        dispatch({
          type: 'ERROR',
        });
      }
    };
    getCurrentCurrency();
  }, [id]);

  //other stuff and return jsx

};

在分派之前,您可能应该检查组件是否为still mounted,如果该组件不会卸载,那么这不是问题。

答案 1 :(得分:0)

这里,即使您使用自己的值来设置状态,当useEffect完成时它也会认为您已使用新值更新了数据状态。在useEffect期间将设置币种,这会在将其作为依赖。

如果要保持代码不变,可以在前面的行中添加// eslint-disable-next-line

原因:在这种情况下,您知道您不希望在更改data.currencies时运行此useEffect(因为这是此useEffect的重点)

另一种选择,因为实际上您正在尝试将货币更改为加载状态,因此您只需更改以下内容即可:

setData({currencies: data.currencies, isFetching: true});
-and-
setData({currencies: data.currencies, isFetching: false});

成为:

setData({isFetching: true});
-and-
setData({isFetching: false});

(请记住,由于setState进入了javascript队列,因此setState不是立即的,所以最好不要依赖于此)

完整(略作修改的return语句)工作代码:

import React, {useEffect, useState} from 'react';
import ReactDOM from 'react-dom'

const Slide = () => {
    const [id, setId] = useState(0);
    const [data, setData] = useState({currencies:[], isFetching:false});

    const images = [
       "https://images.pexels.com/photos/672532/pexels-photo-672532.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
       "https://images.pexels.com/photos/773471/pexels-photo-773471.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
       "https://images.pexels.com/photos/64271/queen-of-liberty-statue-of-liberty-new-york-liberty-statue-64271.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940"
    ];

    useEffect(()=> {
        const getCurrentCurrency = async () => {
            try{
                setData({isFetching: true});
                const currencyArr = [];
                const response = await fetch(`https://api.exchangeratesapi.io/latest?base=GBP`);
                const responseData  = await response.json();
                const {EUR:euro ,CHF:franc, USD: dolar} = responseData.rates;
                currencyArr.push(euro,franc,dolar);
                setData({currencies: currencyArr, isFetching: false});
            }
            catch (e) {
                setData({isFetching: false});
            }
        };
        getCurrentCurrency();
    }, [id]);



    const goToPrevSlide = () => {
         id === 0 ? setId(2) : setId(id-1);
    }
    const goToNextSlide = () =>{
         id === 2 ? setId(0) : setId(id+1);
    }


    return(
        <div>
            <div className="slide">
                <div>
                    {id}
                </div>
                {JSON.stringify(data)}
                <div>
                    <button onClick={goToPrevSlide}>Prev</button>
                    <button onClick={goToNextSlide}>Next</button>  
                </div>
            </div>
        </div>
    );
}