纯组件中的反应本机更新FlatList数据

我对Reaction Native有些陌生,并且一直在使用FlatList时遇到这个问题,我想更新它使用的数据源,并让它更新它自己。我的案例是有一个我想用FlatList显示的项目列表,然后每个项目都是可选择的。我看到的大多数示例都是使用组件,建议已经将state添加到extraData来解决这个问题。然而,我使用的是纯组件,并尝试查看在这种情况下是否有任何解决方案。遵循我创建的一个样例项目。在此示例中,发生了按下操作,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

解决方案

最终输出:

如何实现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;

工作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.
  };

完整工作演示:Expo Snack

相关文章