首页 > 解决方案 > Incorrect dependency behavior in hook useEffect?

问题描述

I've got a problem with infinite loop inside useEffect. I want to make new request only when id dependency changes. When I not pass data I've got a problem with setData. My data state is not updated. When I pass data to the dependency I've got an infinite loop. How to fix it and why?

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;

标签: javascriptreactjsasynchronousreact-hooks

解决方案


在这里,即使您使用自己的值设置状态,当 useEffect 完成时它认为您已经使用新值更新了状态,因为 data.currencies 将在 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 队列,所以最好不要依赖它)

完整(略微修改的返回语句)工作代码:

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>
    );
}

推荐阅读