简体   繁体   English

React Native 在纯组件中更新 FlatList 数据

[英]React Native update FlatList data in pure component

I'm kinda new to React Native and have been having this issue with FlatList that I want to update the data source it uses and get it to update itself.我对 React Native 有点陌生,并且一直遇到 FlatList 的这个问题,我想更新它使用的数据源并让它自我更新。 My case is having a list of items that I want to show with FlatList and then each item is selectable.我的情况是有一个我想用 FlatList 显示的项目列表,然后每个项目都是可选的。 Most of the examples I've seen are using component and suggestion has been adding state to extraData to solve this issue.我见过的大多数示例都是使用组件,建议将state添加到extraData以解决此问题。 However, I'm using pure component and trying to see if there's any solution in this case.但是,我正在使用纯组件并尝试查看在这种情况下是否有任何解决方案。 Following a sample project I've created.按照我创建的示例项目。 In this example, the press happens, data is updated, but FlatList doesn't reflect the data changes.在这个例子中,新闻发生了, data被更新了,但是 FlatList 没有反映data的变化。

import { findIndex } from 'lodash'
import React, { useState } from 'react'
import {
  SafeAreaView,
  StyleSheet,
  View,
  Text,
  StatusBar,
  FlatList,
  Pressable,
} from 'react-native'

import {
  Colors,
} from 'react-native/Libraries/NewAppScreen'

var array = require('lodash/array')

const App = () => {
  const [data, setData] = useState([
    {
      name: "Foo",
      id: '1',
      isSelected: false
    },
    {
      name: "Boo",
      id: '2',
      isSelected: false
    },
    {
      name: "Koo",
      id: '3',
      isSelected: false
    },
    {
      name: "Poo",
      id: '4',
      isSelected: false
    },
    {
      name: "Too",
      id: '5',
      isSelected: false
    },
    {
      name: "Qoo",
      id: '6',
      isSelected: false
    },
  ])

  const tappedItem = (item) => {
    const itemIndex = data.findIndex((i) => i.id == item.id)
    const newItem = data[itemIndex]
    newItem.isSelected = !newItem.isSelected
    data[itemIndex] = newItem
    setData(data)
  }

  const renderItem = (item) => {
    return (
      <Pressable onPress={() => tappedItem(item.item)}>
        <View style={[styles.sectionContainer, { backgroundColor: item.item.isSelected ? 'pink' : 'lightgray' }]}>
          <Text style={styles.sectionTitle}>{item.item.name}</Text>
        </View>
      </Pressable>
    )
  }

  return (
    <>
      <StatusBar barStyle="dark-content" />
      <SafeAreaView>
        <View style={styles.body}>
          <FlatList
            data={data}
            keyExtractor={item => item.id}
            renderItem={renderItem}
            extraData={data}
          />
        </View>
      </SafeAreaView>
    </>
  )
}

const styles = StyleSheet.create({
  body: {
    backgroundColor: Colors.white,
  },
  sectionContainer: {
    marginTop: 32,
    paddingHorizontal: 24,
  },
  sectionTitle: {
    fontSize: 24,
    fontWeight: '600',
    color: Colors.black,
  },
})

export default App

Final Output:最终 Output:

在此处输入图像描述

Here is how you can implement the selection and deselection of FlatList items:以下是如何实现 FlatList 项的选择和取消选择:

import React, { useState } from 'react';
import {
  SafeAreaView,
  StyleSheet,
  View,
  Text,
  StatusBar,
  FlatList,
  Pressable,
} from 'react-native';


const App = () => {
  const [data, setData] = useState([
    {
      name: 'Foo',
      id: '1',
      isSelected: false,
    },
    {
      name: 'Boo',
      id: '2',
      isSelected: false,
    },
    {
      name: 'Koo',
      id: '3',
      isSelected: false,
    },
    {
      name: 'Poo',
      id: '4',
      isSelected: false,
    },
    {
      name: 'Too',
      id: '5',
      isSelected: false,
    },
    {
      name: 'Qoo',
      id: '6',
      isSelected: false,
    },
  ]);

  const tappedItem = (item) => {
    console.log(item);
    const modifiedList= data.map((element) => {
      if (element.id === item.id) {
        return { ...element, isSelected: !element.isSelected };
      }
      return element;
    });
    setData(modifiedList);
  };

  const renderItem = (item) => {
    return (
      <Pressable onPress={() => tappedItem(item.item)}>
        <View
          style={[
            styles.sectionContainer,
            { backgroundColor: item.item.isSelected ? 'pink' : 'lightgray' },
          ]}>
          <Text style={styles.sectionTitle}>{item.item.name}</Text>
        </View>
      </Pressable>
    );
  };

  return (
    <>
      <StatusBar barStyle="dark-content" />
      <SafeAreaView>
        <View style={styles.body}>
          <FlatList
            data={data}
            keyExtractor={(item) => item.id}
            renderItem={renderItem}
            extraData={data}
          />
        </View>
      </SafeAreaView>
    </>
  );
};

const styles = StyleSheet.create({
  body: {
    backgroundColor: 'white',
  },
  sectionContainer: {
    marginTop: 32,
    paddingHorizontal: 24,
  },
  sectionTitle: {
    fontSize: 24,
    fontWeight: '600',
    color: 'black',
  },
});

export default App;

Working of tappedItem() : tappedItem()的工作:

/* 
Lets assume we pressed first item in the FlatList, the item that we will be passing 
is: {
      name: 'Foo',
      id: '1',
      isSelected: false,
    }

After that is done most important and also probably the easiest part, 
the modification of the existing state, i.e. toggling the selected property. 

*/
const tappedItem = (item) => {
    console.log(item);
/*
In the following map operation, we iterate through the existing state, i.e. data.
and when we find the matching id, 1 in the case of 
=> {
      name: 'Foo',
      id: '1',
      isSelected: false,
    }

we return the mutated object where we change only the `isSelected` property

You can see this happening here: 
=> if (element.id === item.id) {
        return { ...element, isSelected: !element.isSelected };
   }
in above if statement, we spread the `element` object, toggle the `isSelected` property and return it. 

If the id does not matches then we can simply return the original `element` object without any changes. 

So after that, we will get the modifiedList which will have the `isSelected` toggled as we intend to. 

Then we simply set the data state with that of the newly modified list. 

=> setData(modifiedList);

*/
    const modifiedList= data.map((element) => {
      if (element.id === item.id) {
        return { ...element, isSelected: !element.isSelected };
      }
      return element;
    });
    setData(modifiedList);
  };

/*
this variation of your existing tappedItem function will work fine too, 

just be careful not to mutate the states directly as you did in the original case

=>  data[itemIndex] = newItem [❌]
=> let temp = [...data] 
   temp[itemIndex] = newItem; [✔]
*/


 const tappedItem = (item) => {
    let temp = [...data] //<= create a copy of current state
    const itemIndex = data.findIndex((i) => i.id == item.id);
    const newItem = data[itemIndex];
    newItem.isSelected = !newItem.isSelected;
    temp[itemIndex] = newItem; //<= modify that copy
    setData(temp); //<= use it to set the state.
  };

Full Working demo: Expo Snack完整的工作演示: Expo Snack

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM