Неправильное поведение зависимости в ловушке useEffect? - PullRequest
0 голосов
/ 20 октября 2019

У меня проблема с бесконечным циклом внутри 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 голосов
/ 20 октября 2019

Здесь, даже если вы устанавливаете состояние с его собственным значением, когда useEffect завершает работу, оно думает, что вы обновили состояние с новым значением, так как data.currencies будут установлены во время использования useEffect, это вызывает цикл при добавлении его какзависимость.

Если вы хотите сохранить код как есть, вы можете добавить // eslint-disable-next-line к строке ранее.

Причина: в этом случае вы знаете, что не хотите запускать этот useEffect при изменении data.currencies (так как в этом смысл этого 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>
    );
}
0 голосов
/ 20 октября 2019

Вот пример того, как вы могли бы сделать это с помощью useDispatch, это больше кода, но вы можете вынуть редуктор из компонента в отдельный файл и также должны поместить типы действий и создателей действий в отдельный файл.

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

};

Перед отправкой вам, вероятно, следует проверить, установлен ли компонент все еще установлен , если компонент не будет демонтирован, то это не проблема.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...