0

我面临的问题是,当我快速刷卡时,我的 useEffect 被无限调用,这弄乱了我的牌组。而我的 useEffect 应该只在当前卡索引发生变化但它每次都发生时才被调用。尝试了很多都无法弄清楚“我在 react/react-native 中是菜鸟”。我正在为我的代码粘贴代码,请帮助我,任何帮助将不胜感激

import React, {useState, useEffect} from 'react';
import {StyleSheet, Text, View} from 'react-native';
import styled from 'styled-components';
import Swiper from 'react-native-deck-swiper';
import ReactionCTAs from '../reactionCTAs/ReactionCTAs';
import MovieDetails from '../cardMovieDetails/MovieDetails';

const Recommendations = () => {
  const [currentCardIndex, setCount] = useState(0);
  const [loading, setLoading] = useState(true);
  const [cardsState, updateState] = useState({
    cards: [],
    swipedAllCards: false,
    swipeDirection: '',
    cardIndex: 0,
    pageNumber: 1,
  });

  useEffect(() => {
    console.log('Use effect being called');

    fetch(
      `https://abcde`,
      {
        method: 'GET',
        headers: {
          Accept: 'application/json',
          Authorization: 'Token ',
        },
      },
    )
      .then((response) => response.json())
      .then((json) => {
        setLoading(false);
        updateState({
          ...cardsState,
          cards: [...cardsState.cards, ...json.data],
        });
      })
      .catch((error) => {
        console.error(error);
      });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [currentCardIndex]);

  const renderCard = (card, index) => {
    return (
      <Card>
        {cardsState.cards[index] !== undefined ? (
          <CardImage
            source={{
              uri: cardsState.cards[index].media.posters[0],
            }}>
            <MovieDetails movie={cardsState.cards[index]} />
            <ReactionCTAs
              movieId={cardsState.cards[index].id}
              swipeLeft={swipeLeft}
              swipeRight={swipeRight}
              swipeTop={swipeTop}
              swipeBottom={swipeBottom}
            />
          </CardImage>
        ) : (
          <Text>Undefined</Text>
        )}
      </Card>
    );
  };

  const onSwiped = (type) => {
    setCount((prevCurrentCardIndex) => prevCurrentCardIndex + 1);
    updateState((prevState) => ({
      ...prevState,
      pageNumber: prevState.pageNumber + 1,
    }));
    if (type === 'right') {
      fetch('https://abcde', {
        method: 'POST',
        body: `{"movie":${cardsState.cards[currentCardIndex].id}}`,
        headers: {
          'Content-type': 'application/json',
          Authorization: 'Token',
        },
      });
    }
  };

  const onSwipedAllCards = () => {
    updateState({
      ...cardsState,
      swipedAllCards: true,
    });
  };

  const swipeLeft = () => {
    cardsState.swiper.swipeLeft();
  };

  const swipeRight = () => {
    cardsState.swiper.swipeRight();
  };
  const swipeTop = () => {
    cardsState.swiper.swipeTop();
  };
  const swipeBottom = () => {
    cardsState.swiper.swipeBottom();
  };

  return loading ? (
    <ProgressBar animating={true} color="red" size="large" />
  ) : (
    <Container>
      <Swiper
        ref={(swiper) => {
          cardsState.swiper = swiper;
        }}
        backgroundColor={'#20242b'}
        onSwiped={() => onSwiped('general')}
        onSwipedLeft={() => onSwiped('left')}
        onSwipedRight={() => onSwiped('right')}
        onSwipedTop={() => onSwiped('top')}
        onSwipedBottom={() => onSwiped('bottom')}
        onTapCard={swipeLeft}
        cards={cardsState.cards}
        cardIndex={cardsState.cardIndex}
        cardVerticalMargin={80}
        renderCard={renderCard}
        children={true}
        stackScale={4.5}
        onSwipedAll={onSwipedAllCards}
        stackSize={3}
        stackSeparation={-25}
        overlayLabels={{
          bottom: {
            title: 'BLEAH',
            style: {
              label: {
                backgroundColor: 'black',
                borderColor: 'black',
                color: 'white',
                borderWidth: 1,
              },
              wrapper: {
                flexDirection: 'column',
                alignItems: 'center',
                justifyContent: 'center',
              },
            },
          },
          left: {
            title: 'NOPE',
            style: {
              label: {
                backgroundColor: 'black',
                borderColor: 'black',
                color: 'white',
                borderWidth: 1,
              },
              wrapper: {
                flexDirection: 'column',
                alignItems: 'flex-end',
                justifyContent: 'flex-start',
                marginTop: 30,
                marginLeft: -30,
              },
            },
          },
          right: {
            title: 'LIKE',
            style: {
              label: {
                backgroundColor: 'black',
                borderColor: 'black',
                color: 'white',
                borderWidth: 1,
              },
              wrapper: {
                flexDirection: 'column',
                alignItems: 'flex-start',
                justifyContent: 'flex-start',
                marginTop: 30,
                marginLeft: 30,
              },
            },
          },
          top: {
            title: 'SUPER LIKE',
            style: {
              label: {
                backgroundColor: 'black',
                borderColor: 'black',
                color: 'white',
                borderWidth: 1,
              },
              wrapper: {
                flexDirection: 'column',
                alignItems: 'center',
                justifyContent: 'center',
              },
            },
          },
        }}
        animateOverlayLabelsOpacity
        animateCardOpacity
        swipeBackCard
      />
    </Container>
  );
};

const Container = styled.View`
  flex: 1;
`;

const CardImage = styled.ImageBackground`
  height: 100%;
  width: 372px;
`;

const Card = styled.View`
  flex: 1;
  border-radius: 4px;
  justify-content: center;
  background-color: #20242b;
`;

const ProgressBar = styled.ActivityIndicator`
  flex: 1;
  justify-content: center;
  align-items: center;
`;

export default Recommendations;

4

1 回答 1

1

目前尚不清楚您的代码应该做什么,但基于用户操作获取数据存在一些缺陷。

  1. Debounce fetching 因此仅在用户处于非活动状态一段时间后才会触发 fetch。
  2. 仅在最后一次用户操作时才解决,此处解释了为什么要这样做

所以这里有一些你可以使用的辅助函数:

//helper to make sure promise only resolves when
//  it was the last user action
const last = (fn) => {
  const check = {};
  return (...args) => {
    const last = {};
    check.last = last;
    return Promise.resolve(fn(...args)).then((resolve) =>
      check.last === last
        ? resolve
        : Promise.reject('replaced by newer request')
    );
  };
};
const debounce = (fn, time = 300) => {
  let timeout;
  return (...args) => {
    clearTimeout(timeout);
    timeout = setTimeout(() => fn(...args), time);
  };
};
// only resolve if it was the last user action
const lastFetch = debounce(last(fetch));
// the debounced effect (only run when inactive for 300ms)
const effect = debounce(
  (pageNumber, setLoading, updateState) => {
    //only resolve when it was the last user action
    lastFetch(`https://abcde/?page=${pageNumber}`, {
      method: 'GET',
      headers: {
        Accept: 'application/json',
        Authorization: 'Token',
      },
    })
      .then((response) => response.json())
      .then((json) => {
        setLoading(false);
        //pass callback to upateState so cardsState is not
        //  a dependency of the effect
        updateState((cardsState) => ({
          ...cardsState,
          cardIndex: 0,
          cards: [...cardsState.cards, ...json.data],
        }));
      })
      .catch((error) => {
        console.error(error);
      });
  }
);

这是运行效果的方法:

useEffect(() => {
  //debounced and resolve last
  effect(cardsState.pageNumber, setLoading, updateState);
  //if you don't add pageNumber then pageNumber will be
  //  a stale closure and request with new pageNumber
  //  will never be made
}, [cardsState.pageNumber, cardsState.currentCardIndex]);
于 2020-07-16T08:24:50.810 回答