React Native RecyclerListView:如果状态改变,则滚动到顶部

4
如果我更改任何状态或更改文本输入的值,如果它更改了,则将其滚动到顶部并离开文本输入。 或者如果我喜欢一个产品,如果数组已更改,则自动向上滚动。 有人能帮我吗?
import React, { useState, useMemo, useEffect, forwardRef, memo } from 'react';
import { StyleSheet, Text, TextInput, View, TouchableOpacity, Image, Dimensions } from 'react-native';
import { TouchableWithoutFeedback } from 'react-native-gesture-handler';
import { RecyclerListView, LayoutProvider, DataProvider } from 'recyclerlistview';
import { useSelector, useDispatch } from 'react-redux';
import { AntDesign } from '@expo/vector-icons';
import { addAmountOnCartItem } from '../../../redux/slice/product/shopping_cart';
import faker from 'faker';
import ButtonWithoutLoader from '../../ButtonWithoutLoader';

const { width, height } = Dimensions.get('window');

const Shopping_cart_list = ({ datas, onPressSetting }) => {
  const dispatch = useDispatch();

  const provider = useMemo(() => {
    return new DataProvider(
      (r1, r2) => {
        return r1 !== r2;
      },
      index => {
        return 'id:' + index;
      }
    )
  }, []);

  const dataProvider = useMemo(() => {
    return provider.cloneWithRows(datas);
  }, [datas, provider]);

  const [update, updateRecycler] = useState({
    update: false
  });

  const handleChange = (e, product_id) => {
    dispatch(addAmountOnCartItem({ value: e, product_id }));
    updateRecycler(prevState => {
      return {
        update: !prevState
      }
    });
  };

  const layoutProvider = new LayoutProvider((i) => {
    return dataProvider.getDataForIndex(i).type;
  }, (type, dim) => {
    switch(type) {
      case 'NORMAL':
        dim.height = 250;
        dim.width = width * 0.9;
      break;
      default:
        dim.height = 0;
        dim.width = 0;
      break;
    }
  });

  const RenderData = memo(({ product_id, product_name, price, product_image, amount, username }) => {
    return (
      <TouchableWithoutFeedback style={{height: 250, backgroundColor: '#fff', marginBottom: 16}}>
        <View style={styles.header}>
          <TouchableOpacity style={styles.profile_info}>
            <Image source={{uri: faker.image.avatar()}} resizeMode="contain" style={styles.profile_image} />
            <Text style={styles.username}>{ username }</Text>
          </TouchableOpacity>
          <TouchableOpacity onPress={() => onPressSetting(product_id)}>
            <AntDesign name="setting" size={24} color="#444" />
          </TouchableOpacity>
        </View>
        <View style={styles.mainContainer}>
          <Image source={{uri: product_image}} style={styles.image} />
          <View>
            <Text style={styles.text}>{product_name}</Text>
            <Text style={styles.text}>Preis: {price}</Text>
            <View style={{flexDirection: 'row', alignItems: 'center'}}>
              <Text style={styles.text}>Anzahl: </Text>
              <AntDesign name="minussquareo" style={{marginRight: 6}} size={24} color="black" />
              <TextInput onBlur={() => handleChange(1, product_id)} value={ isNaN(amount) ? '' : amount.toString() } onChangeText={e => handleChange(e, product_id)} style={{height: 28, width: 28, borderRadius: 4, textAlign: 'center', backgroundColor: '#eee'}} />
              <AntDesign name="plussquareo" style={{marginLeft: 6}} size={24} color="black" />
            </View>
          </View>
        </View>
        <View style={[styles.header, { marginTop: 4 }]}>
          <ButtonWithoutLoader onPress={() => updateRecycler(prevState => !prevState)} title="Jetzt Kaufen!" width={width * 0.9} />
        </View>
      </TouchableWithoutFeedback>
    )
  });

  const rowRenderer = (type, data) => {
    const { product_id, product_name, price, product_image, amount, username } = data.item;
    return <RenderData product_id={product_id} product_name={product_name} price={price} product_image={product_image} amount={amount} username={username} />
  };

  return (
    <View style={{flex: 1, paddingBottom: 85}}>
      <RecyclerListView
        dataProvider={dataProvider}
        layoutProvider={layoutProvider}
        forceNonDeterministicRendering={true}
        rowRenderer={rowRenderer}
        style={{marginLeft: width * 0.05, marginRight: width * 0.05}}
        extendedState={update}
        scrollViewProps={{showsVerticalScrollIndicator: false}}
      />
    </View>
  )
};

Flatlist:

import React, { useState, useRef, memo, useMemo } from 'react';
import { StyleSheet, Animated, FlatList, Text, TextInput, View, TouchableOpacity, Image, Dimensions } from 'react-native';
import { TouchableWithoutFeedback } from 'react-native-gesture-handler';
import { useSelector, useDispatch } from 'react-redux';
import { useNavigation } from '@react-navigation/core';
import { AntDesign } from '@expo/vector-icons';
import { addAmountOnCartItem } from '../../../redux/slice/product/shopping_cart';
import faker from 'faker';
import ButtonWithoutLoader from '../../ButtonWithoutLoader';

const { width } = Dimensions.get('window');

const Shopping_cart = ({ datas, onPressSetting }) => {
  const dispatch = useDispatch();
  const navigation = useNavigation();

  const [update, updateRecycler] = useState({
    update: false
  });

  const handleChange = (e, product_id) => {
    dispatch(addAmountOnCartItem({ value: e, product_id }));
    updateRecycler(prevState => {
      return {
        update: !prevState
      }
    });
  };

  const RenderItem = memo(({ item }) => {
    const { product_id, product_name, price, product_image, amount, username, size, colors, desc, selectedColor, selectedSize } = item.item;
    return (
      <TouchableWithoutFeedback style={{height: 300, backgroundColor: '#fff', marginBottom: 16}}>
        <View style={styles.header}>
          <TouchableOpacity style={styles.profile_info}>
            <Image source={{uri: faker.image.avatar()}} resizeMode="contain" style={styles.profile_image} />
            <Text style={styles.username}>{ username }</Text>
          </TouchableOpacity>
          <TouchableOpacity onPress={() => onPressSetting(product_id)}>
            <AntDesign name="setting" size={24} color="#444" />
          </TouchableOpacity>
        </View>
        <TouchableOpacity onPress={() => navigation.navigate('ProductStack', {
                product_id,
                desc,
                price,
                product_image,
                username,
                user_profil_image: faker.image.avatar(),
                size,
                colors,
              })} style={styles.mainContainer}>
          <Image source={{uri: product_image}} style={styles.image} />
          <View>
            <Text style={styles.text}>{product_name}</Text>
            <Text style={styles.text}>Preis: {price}</Text>

            <Text style={styles.text}>Farbe: {selectedColor}</Text>
            <Text style={styles.text}>Größe: {selectedSize}</Text>
            <View style={{flexDirection: 'row', alignItems: 'center'}}>
                <Text style={styles.text}>Anzahl: </Text>
                <AntDesign name="minussquareo" style={{marginRight: 6}} size={24} color="black" />
                <TextInput onBlur={() => handleChange(1, product_id)} value={ isNaN(amount) ? '' : amount.toString() } onChangeText={e => handleChange(e, product_id)} style={{height: 28, width: 28, borderRadius: 4, textAlign: 'center', backgroundColor: '#eee'}} />
                <AntDesign name="plussquareo" style={{marginLeft: 6}} size={24} color="black" />
            </View> 
          </View>
        </TouchableOpacity>
        <View style={[styles.header, { marginTop: 4 }]}>
          <ButtonWithoutLoader onPress={() => updateRecycler(prevState => !prevState)} title="Jetzt Kaufen!" width={width * 0.9} />
        </View>
      </TouchableWithoutFeedback>
    );
  });
  return (
    <FlatList 
      data={datas}
      keyExtractor={item => item.item.product_id + Math.random(100)}
      renderItem={({ item }) => <RenderItem item={item}/>}
      contentContainerStyle={{justifyContent: 'center', alignItems: 'center'}}
      removeClippedSubviews={true}
      initialNumToRender={2}
      maxToRenderPerBatch={1}
      extraData={update}
      updateCellsBatchingPeriod={100}
    />
  );
};

........................................................................................................................................................................................

2个回答

0
如果您查看RecyclerView的文档,您会发现extendedState属性的说明:

在某些情况下,传递给行级别的数据可能不包含项目所依赖的所有信息,您可以将所有其他信息保留在外部,并通过此属性传递它。更改此对象将导致重新渲染所有内容。确保您不经常更改它以确保性能。重新渲染很耗费资源。

根据您的代码,您正在按钮单击和文本更改处理程序中更新此属性,这将根据描述最终重新渲染屏幕。因此,在这里,您需要修改您的逻辑以避免不必要的重新渲染。

嗨,谢谢你的回答。我为了测试已经删除了extendedState并且它再次渲染了。所以这不是extendedState的问题。 - locklock123

0

我最近在自己的项目中解决了这个问题,所以很乐意帮忙。

主要问题是您不能在列表项中具有任何与UI相关的状态。 没有。 您必须通过props将所有信息移动到要传输的位置。

我看到你唯一的实例是通过TextInput。 这可能很困难。 所以我的第一个问题是:你真的需要RecyclerListView吗? 除非您的列表有数千个项目,并且项目具有复杂/图像密集的UI,否则我建议在此项目中使用FlatList。

如果您继续需要RecyclerListView,则建议使其在完成文本输入后(“完成”指可能是在3秒计时器之后或按下“提交”等按钮后),更改dataProvider的数据对象。 更改您需要的索引处的项目,然后使用cloneWithRows()重新提交它


嘿,谢谢你的回答。我想我会使用flatlist,因为它只用于购物车。我不认为用户会有1000个购物车商品:D。但是我使用recyclerlistview是因为性能更好。在购物车屏幕上,我还有一个模态框,所以如果我使用flatlist并打开它,那么它会很慢。这就是选择recyclerlistview的原因。但是有一个问题。如何在recyclerlistview中更改索引处的项目?你有任何链接或教程吗? - locklock123
是的,对于购物车,我强烈建议使用FlatList。它也更容易实现。我唯一需要使用RecyclerListView来获得高性能的情况是在一个包含大量图像和视频的无限社交媒体动态中。我很惊讶你报告模态框速度慢的问题。请随时展示你的代码,我们可以看看那里出了什么问题。一个简单的模态框不应该降低性能。 - Maxwell
我将使用FlatList和Modal再次测试它,如果问题出现缓慢,我会发布代码,感谢你们两个的回答,我非常感激。 - locklock123
你好我的朋友,我已经在FlatList中使用了它,但是如果我在输入框中输入任何内容,它会自动聚焦。为什么?我编辑了上面的FlatList代码。 - locklock123
现在我已经有时间仔细看了,我可以说TextInput组件本身有一些问题。而且既然我们现在正在使用FlatList,你可以像在任何地方使用TextInput组件一样使用标准方法。在RenderItem中,添加状态。像这样:const [text, updateText] = React.useState(amount)TextInput也可以使用标准方法:value={amount.toString} onChangeText={e => updateText(e)}您当前的handleChange函数包含太多不应包含在TextInput的onChangeText中的内容,应将其移至其他位置。 - Maxwell
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接