Как я могу ограничить свою длину data.length с помощью инвариантных размеров данных, чтобы никогда не превысить последний индекс?

У меня есть два компонента списка или стопки карточек. 5 колод и несколько карт в каждой колоде. Я перечисляю 5 колод и передаю их содержимое дочернему компоненту, когда нажимаю на него в состоянии (selectedItem). Затем я беру это в дочернем компоненте и сопоставляю другой плоский список с карточками внутри.

Моя проблема в том, что когда я нажимаю на индекс, выходящий за пределы меньшей колоды. Например,

Deck1 = 23 cards
Deck2 = 15 cards

Когда я индексирую +1 на Deck1 где-то выше 15 и пытаюсь нажать на Deck2, он говорит, что он вне диапазона (очевидно, лол), я бы хотел, чтобы он сбрасывался до index(0) при смене колод. Я не могу просто добавить больше карт и сделать их одинаковым количеством, потому что позже я устанавливаю новую функцию, где пользователь может создать свою собственную колоду, и это может быть любое количество карт.

Вот мой компонент Decks

const Decks = () => {
  const {theme} = useContext(ThemeContext)
  let activeColors = colors[theme.mode]

  const allbelts = [yellowbelt, orangebelt, greenbelt, brownbeltthree, brownbelttwo, brownbeltone]

  const ref = useRef(null);
  const [selectedItem, setSelectedItem] = useState(null);
  const [deckIndex, setDeckIndex] = useState(0);

  const handleItemPress = (item) => {
    setSelectedItem(item);
    console.info("deckIndex:", deckIndex);
  };

  return (
    <View style = {{flex:1}}>
      <View style = {{flex:1, marginTop: 40}}>
        <Text style = {[{color:activeColors.primary}, styles.sectionheading]}>
          Testing Card Decks
        </Text>
          <Animated.FlatList
            ref = {ref}
            data = {allbelts}
            horizontal
            keyExtractor = {(item, index) => index.toString()}
            showsHorizontalScrollIndicator = {false}
            snapToInterval = {ITEM_SIZE}
            decelerationRate = {0}
            bounces = {false}
            scrollEventThrottle = {16}
            renderItem = {({item, deckIndex}) => (
              <View 
                style = {{}}>
                <TouchableOpacity 
                  key = {deckIndex}
                  onPress = {() => {
                    handleItemPress(item)
                  }}>    
                  <Animated.View 
                    style = {[{
                      backgroundColor: item.color,
                      shadowColor: item.color,
                      borderColor: activeColors.bgalt}, 
                      styles.card]}>
                      <Text 
                        numberOfLines = {2} 
                        style = {[{color: item.color === "#F5D143" ? activeColors.black : activeColors.white}, styles.cardtitle]}>
                        {item.rank_japanese}
                      </Text>
                      <Text 
                        numberOfLines = {2} 
                        style = {[{color: item.color === "#F5D143" ? activeColors.black : activeColors.white}, styles.cardtitle]}>
                        {item.rank_english}
                      </Text>
                  </Animated.View>
                </TouchableOpacity>
              </View>
            )}
          />   
        <Cards selectedItem = {selectedItem} deckIndex = {deckIndex}  />
      </View>
    </View>
  )
}

export default Decks

а вот компонент «Карты»

const Card = ({info}) => {
  const {theme} = useContext(ThemeContext)
  let activeColors = colors[theme.mode]

  const rotate = useSharedValue(0);
  
  const frontAnimatedStyle = useAnimatedStyle(() => {
    const rotateValue = interpolate(
      rotate.value, 
      [0, 1], 
      [0, 180]
    );
    return {
      transform: [
        {rotateY: withTiming(`${rotateValue}deg`, {duration: 500})}
      ]
    }
  })

  const backAnimatedStyle = useAnimatedStyle(() => {
    const rotateValue = interpolate(
      rotate.value, 
      [0, 1], 
      [180, 360]
    );
    return {
      transform: [
        {rotateY: withTiming(`${rotateValue}deg`, {duration: 500})}
      ]
    }
  })
  return (
    <View style = {[styles.cardcontainer]}>
      <View style = {styles.iconcnt}>
        <Pressable 
          onPress = {() => {
            rotate.value = rotate.value ? 0 : 1;  
          }} 
          style = {styles.icon}>
          <Icon name = "swap-horizontal" size = {24} color = {activeColors.textcolor} />
        </Pressable>
      </View>
      <View style = {styles.cardContent}>
        <Animated.View style = {[ {backgroundColor: activeColors.bgalt, position:"absolute"}, styles.card, frontAnimatedStyle]}>
          <View>
            <Text 
              style = {[{color: activeColors.textcolor}, styles.cardtext]}>
              {info.q}
            </Text>
          </View>
        </Animated.View>

        <Animated.View style = {[ {backgroundColor: activeColors.primary}, styles.card, backAnimatedStyle]}>
          <View>
            <Text 
              style = {[{color: activeColors.textcolor}, styles.cardtext]}>
              {info.a}
            </Text>
          </View>          
        </Animated.View>
      </View>
    </View>
    
  )
}

const Cards = ({selectedItem, deckIndex}) => {
  const {theme} = useContext(ThemeContext)
  let activeColors = colors[theme.mode]

  const [index, setIndex] = useState(0);
  const [lastIndex, setLastIndex] = useState(null);
  const [viewPosition, setViewPosition] = useState(0.5);

  const flatlistRef = useRef(null)

  useEffect(() => {
    setLastIndex(selectedItem?.questions?.length - 1);

    // console.info("selectedItem:", selectedItem);
    console.info("lastIndex at TestingCards:", lastIndex);

    flatlistRef.current?.scrollToIndex({
      index: index, 
      animated: true,
      viewPosition,
    })
  } , [index, viewPosition, lastIndex, selectedItem])

  if (!selectedItem) {
    return null;
  }
  return (
    <View style = {[styles.container]}>
      <Animated.FlatList
        data = {selectedItem.questions}   
        ref = {flatlistRef}
        index = {deckIndex}           
        keyExtractor = {(item, index) => index.toString()}
        initialScrollIndex = {0}
        horizontal
        showsHorizontalScrollIndicator = {false}
        snapToInterval = "ITEM_SIZE"
        decelerationRate = {0}
        bounces = {false}
        scrollEventThrottle = {16}
        initialNumToRender = {10} 
        onScrollToIndexFailed = {index => {
            setIndex(0) 
          const wait = new Promise(resolve => setTimeout(resolve, 0));
          wait.then(() => {
            flatlistRef.current?.scrollToIndex({ index: index, animated: true });
          });
        }}
        renderItem = {({item, index}) => <Card info = {item} index = {index} totalLength = {lastIndex} seefront = {true} />}
      />
      <View style = {styles.btnGroup}>
        <TouchableOpacity 
          onPress = {()=>{
            setIndex(0);
            setViewPosition(0.5);
          }}>
          <LinearGradient
            colors = {['rgba(1,102,175,1)', 'rgba(155,24,213,1)']}
            start = {{ x: 0, y: 1 }}
            end = {{ x: 1, y: 1 }}
            style = {styles.btnPrimary}>
            <Icon name = "chevron-double-left" size = {24} color = {activeColors.white} />
          </LinearGradient>
        </TouchableOpacity>      
        <TouchableOpacity 
          onPress = {()=>{
            if (index === 0) {
              return;
            }
            setIndex(index - 1);
            setViewPosition(0.5);
          }}>
          <LinearGradient
            colors = {['rgba(1,102,175,1)', 'rgba(155,24,213,1)']}
            start = {{ x: 0, y: 1 }}
            end = {{ x: 1, y: 1 }}
            style = {styles.btnPrimary}>
            <Icon name = "chevron-left" size = {24} color = {activeColors.white} />
          </LinearGradient>
        </TouchableOpacity>               
        <TouchableOpacity 
          onPress = {()=>{
            if (index === lastIndex) {
              return;
            }
            setIndex(index + 1);
            setViewPosition(0.5);
          }}>
          <LinearGradient
            colors = {['rgba(1,102,175,1)', 'rgba(155,24,213,1)']}
            start = {{ x: 0, y: 1 }}
            end = {{ x: 1, y: 1 }}
            style = {styles.btnPrimary}>
            <Icon name = "chevron-right" size = {24} color = {activeColors.white} />
          </LinearGradient>
        </TouchableOpacity>   
        <TouchableOpacity 
          onPress = {()=>{

            if (index === lastIndex) {
              return;
            }
            setIndex(lastIndex - 1);
            setViewPosition(0.5); 
          }}>
          <LinearGradient
            colors = {['rgba(1,102,175,1)', 'rgba(155,24,213,1)']}
            start = {{ x: 0, y: 1 }}
            end = {{ x: 1, y: 1 }}
            style = {styles.btnPrimary}>
            <Icon name = "chevron-double-right" size = {24} color = {activeColors.white} />
          </LinearGradient>
        </TouchableOpacity>            
      </View>
    </View>
  )
}

export default Cards

где используются FlashCards?

Jonas Beck 27.03.2024 18:04
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
1
71
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий
useEffect(() => {
  flatlistRef.current?.scrollToIndex({
    index: index, 
    animated: true,
    viewPosition,
  })
} , [index])  
useEffect(()=>{
  setIndex(0)
  setLastIndex(selectedItem?.questions?.length - 1);
  setViewPosition(0.5)
}, [selectedItem])

Он сбрасывает индекс в 0 всякий раз, когда изменяется DeckIndex, и, с другой стороны, всякий раз, когда вы выбираете другую колоду. Вы можете добавить несколько кодов для сброса других вещей внутри useEffect.

Обновлять:

initialNumToRender = {selectedItem.questions.length}

Это понадобится в FlatList, чтобы избежать ошибки ScrollToIndex.

Это не помогло. Я не был уверен, в каком компоненте это будет установлено, поэтому я сделал это в компоненте карточек, в котором находится setIndex, и он просто переключался между первыми двумя картами в выбранной колоде вперед и назад. Очень странно. Затем я попробовал это на родительском компоненте, и это ничего не изменило.

LOTUSMS 27.03.2024 19:50

Чтобы ответить на ваш вопрос выше, компонент FlashCards был переименован, когда я копировал код, извините. Я отредактировал свой ОП. По сути, компонент Decks — это то, что заполняет маршрут. Он принимает компонент Cards, а компонент Cards отображает коллекцию компонента Card.

LOTUSMS 27.03.2024 19:56

Да, его следует поместить в компонент карточек. Я обновил условие useEffect.

Jonas Beck 27.03.2024 20:08

Оооо, почти получилось!!!! Когда я сменил колоду, он вернулся к индексу 0, но ошибка возникает до того, как он устанавливает индекс Invariant Violation: scrollToIndex out of range: requested index 23 is out of 0 to 22

LOTUSMS 27.03.2024 20:18

на самом деле нет смысла в viewPosition и LastIndex, поскольку нет изменения viewPosition и нет использования LastIndex. поэтому вам не нужно объявлять их как состояние.

Jonas Beck 27.03.2024 20:18

Да, это все еще не работает. Я могу щелкнуть другую колоду, и она вернется к индексу 0, но если состояние имеет более высокий индекс, чем тот, который я нажимаю в данный момент, поскольку я запрашиваю индекс + 1, он терпит неудачу, если не находит это. Скажем, например, я пришел из индекса 23. Когда я вызываю индекс +1, он ожидает индекс 24. Я чувствую, что знаю это, но не могу заставить свой код делать то, что у меня в голове.

LOTUSMS 27.03.2024 20:29

Если вы хотите, чтобы ваш код работал полностью, вам потребуется отладить ваш код с использованием реальных данных. Надеюсь, вы понимаете, как использовать хук и состояние useEffect.

Jonas Beck 27.03.2024 20:41

Да, это гоночное состояние. Но я посмотрю, смогу ли я реализовать это в каком-нибудь онлайн-редакторе.

LOTUSMS 27.03.2024 20:43
Snack.expo.dev/@lotusms/deck Я сделал эту упрощенную версию, но, конечно, мне повезло, Scrolltoindex не поддерживается в редакторе закусок. Может быть, вы сможете понять это. Все в порядке, если ты не хочешь утруждать себя
LOTUSMS 27.03.2024 21:37

Было бы лучше, если бы можно было поделиться полным кодом.

Jonas Beck 28.03.2024 15:53

Код заполнения находится в этом вопросе. Я не могу добавить его в закуску, потому что он не запускается из-за других зависимостей.

LOTUSMS 28.03.2024 18:34

Есть ли репозиторий, загруженный на git?

Jonas Beck 28.03.2024 18:43

да. Я могу добавить вас в него по вашему желанию. Какой у тебя дескриптор на GitHub? Я отправлю приглашение. Я не могу отблагодарить тебя, чувак

LOTUSMS 28.03.2024 18:46

Я повышу свой вопрос до награды и дам вам ответ, чтобы вы получили еще большую шишку

LOTUSMS 28.03.2024 18:47

Идентификатор GitHub: jonasbeckdev

Jonas Beck 28.03.2024 18:53

Приглашение отправлено....

LOTUSMS 28.03.2024 19:22

Он работает на эмуляторе. но я не могу найти подходящие компоненты, скорее всего, нет компонентов с названиями «Колоды» и «Карта».

Jonas Beck 28.03.2024 20:03

Я только что объединил свой последний код. Я забыл, что не делал этого. Извини. Оформить заказ снова и git pull

LOTUSMS 28.03.2024 20:08

Давайте продолжим обсуждение в чате.

LOTUSMS 28.03.2024 20:08

Другие вопросы по теме