React-native 搜索栏和 flatList 问题

标签 react-native search react-native-flatlist

有人可以帮我解决问题吗?

我正在尝试搜索本地 JSON 文件并返回相关数据。我的 JSON 文件包含餐厅名称和餐厅类型。

我不明白问题可能来自哪里。当我注释掉ListHeadComponent={this.renderHeader}时,没有错误;但是,如果我将其注释掉,则不会显示任何内容。

我遇到的问题是:

Invariant Violation: Invariant Violation: Tried to get frame for out of range index NaN

This error is located at:
    in VirtualizedList (at FlatList.js:662)
    in FlatList (at SearchScreen.js:75)
    in RCTView (at View.js:44)
    in SearchScreen (created by SceneView)
    in SceneView (at StackViewLayout.js:795)
    in RCTView (at View.js:44)
    in AnimatedComponent (at StackViewCard.js:69)
    in RCTView (at View.js:44)
    in AnimatedComponent (at screens.native.js:59)
    in Screen (at StackViewCard.js:57)
    in Card (at createPointerEventsContainer.js:27)
    in Container (at StackViewLayout.js:860)
    in RCTView (at View.js:44)
    in ScreenContainer (at StackViewLayout.js:311)
    in RCTView (at View.js:44)
    in AnimatedComponent (at StackViewLayout.js:307)
    in Handler (at StackViewLayout.js:300)
    in StackViewLayout (at withOrientation.js:30)
    in withOrientation (at StackView.js:79)
    in RCTView (at View.js:44)
    in Transitioner (at StackView.js:22)
    in StackView (created by Navigator)
    in Navigator (at createKeyboardAwareNavigator.js:12)
    in KeyboardAwareNavigator (created by SceneView)
    in SceneView (at createTabNavigator.js:39)
    in RCTView (at View.js:44)
    in RCTView (at View.js:44)
    in ResourceSavingScene (at createBottomTabNavigator.js:113)
    in RCTView (at View.js:44)
    in ScreenContainer (at createBottomTabNavigator.js:103)
    in RCTView (at View.js:44)
    in TabNavigationView (at createTabNavigator.js:197)
    in NavigationView (created by Navigator)
    in Navigator (created by SceneView)
    in SceneView (created by SwitchView)
    in SwitchView (created by Navigator)
    in Navigator (at createAppContainer.js:388)
    in NavigationContainer (at App.js:24)
    in RCTView (at View.js:44)
    in App (at withExpoRoot.js:22)
    in RootErrorBoundary (at withExpoRoot.js:21)
    in ExpoRootComponent (at renderApplication.js:34)
    in RCTView (at View.js:44)
    in RCTView (at View.js:44)
    in AppContainer (at renderApplication.js:33)

This error is located at:
    in NavigationContainer (at App.js:24)
    in RCTView (at View.js:44)
    in App (at withExpoRoot.js:22)
    in RootErrorBoundary (at withExpoRoot.js:21)
    in ExpoRootComponent (at renderApplication.js:34)
    in RCTView (at View.js:44)
    in RCTView (at View.js:44)
    in AppContainer (at renderApplication.js:33)

我的 .js 文件中的代码是:

import React from 'react';
import { ExpoConfigView } from '@expo/samples';
import { View, Text, FlatList, ActivityIndicator} from 'react-native';
import { SearchBar } from 'react-native-elements';
import restaurantList from '../assets/files/search.json';

export default class SearchScreen extends React.Component {
static navigationOptions = {
    title: 'Search for Restaurants',
};
constructor() {
    super();
    this.state = {
        loading: false,
        data: {restaurantList},
        error: null,
    };
    this.arrayholder = [];
}

renderSeparator = () => {
    return (
      <View
        style={{
          height: 1,
          width: '86%',
          backgroundColor: '#CED0CE',
          marginLeft: '14%',
        }}
      />
    );
  };    

searchFilterFunction = text =>{
    this.setState({
        value: text,
    });

    const newData = this.arrayholder.filter(item => {
        const itemData = `${item.name.toUpperCase()} 
        ${item.type.toUpperCase()}`;
        const textData = text.toUpperCase();

        return itemData.indexOf(textData) > -1;
    })
    this.setState({
        data: newData,
    });
};

renderHeader = () => {
    return (
        <SearchBar
            placeholder="Type..."
            value={this.state.value}
            onChange={text => this.searchFilterFunction(text)}
            />
    );
};

render() {
    if (this.state.loading) {
        return (
          <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
            <ActivityIndicator />
          </View>
        );
      }
      return (
        <View>
          <FlatList
            data={this.state.data}
            renderItem={({ item }) => (
              <Text>{item.name} {item.type}</Text>
            )}
            ItemSeparatorComponent={this.renderSeparator}
            ListHeaderComponent={this.renderHeader}
          />
        </View>
      );
}
}

这是我的 json 文件

[
{ 
    "name": "Shake Shack",
    "type":"american"
},
{
    "name": "BonChon Chicken",
    "type": "korean"
},
{
    "name": "Starbucks",
    "type:": "cafe"
}
]

最佳答案

这是一个非常好的开始。但是,您的代码存在一些问题。因此,让我们仔细检查一下,看看我们可以在哪些方面进行改进。

构造函数

首先,在构造函数中,您将数据设置为对象而不是数组。 FlatList 不能处理对象,而它们只能处理数组,因此这会立即给您带来问题。您确实需要从 restaurantList 周围删除 {}

constructor() {
    super();
    this.state = {
        loading: false,
        data: {restaurantList}, // You shouldn't have {} around the restaurantList
        error: null,
    };
    this.arrayholder = []; // we also don't need this
}

您应该将构造函数更新为此

constructor (props) {
  super(props);

  this.state = {
    loading: false,
    data: restaurantList, // notice we have no {} around restaurantList
    error: null,
    value: ''
  };
}

渲染头

在您的 renderHeader 函数中,您使用的是 onChange 而不是 onChangeTextonChange 返回一个对象,但您需要已放入搜索栏中的文本。您需要将 renderHeader 函数更新为如下所示。

renderHeader = () => {
  return (
    <SearchBar
      placeholder="Type..."
      value={this.state.value}
      onChangeText={text => this.searchFilterFunction(text)} // now we are using the correct function to capture the text
    />
  );
};

搜索过滤函数

此功能存在几个问题。首先,您正在查看的是空的 this.arrayholder。我们实际上不需要额外的数组来保存数据,因为我们只需使用之前导入的 restaurantList 即可。其次,您在字符串上使用 indexOf ,最好使用 includes

searchFilterFunction = text => {
  this.setState({
    value: text
  });

  const newData = restaurantList.filter(item => {
    const itemData = `${item.name.toUpperCase()} ${item.type.toUpperCase()}`;
    const textData = text.toUpperCase();
    return itemData.includes(textData); // this will return true if our itemData contains the textData
  });

  this.setState({
    data: newData
  });
};

平面列表

在 FlatList 中,您应该使用 extraData 属性,因为这将允许 FlatList 在底层数据更改时更新。您还应该添加一个keyExtractor

<FlatList
  keyExtractor={(item, index) => `${index}`}
  extraData={this.state} // <- add this prop
  data={this.state.data}
  renderItem={({ item }) => (
    <Text>{item.name} {item.type}</Text>
  )}
  ItemSeparatorComponent={this.renderSeparator}
  ListHeaderComponent={this.renderHeader}
/>

把它们放在一起

因此,如果我们将所有这些放在一起,添加一个模拟数据,以便我们可以检查它是否有效。我们应该得到这样的东西。

// mock the data as you didn't provide an example
const restaurantList = [
  {
    type: 'Italian',
    name: 'DiMaggio'
  },
  {
    type: 'Greek',
    name: 'Athena'
  }
];

export default class SearchScreen extends React.Component {
  static navigationOptions = {
    title: 'Search for Restaurants'
  };
  constructor (props) {
    super(props);

    this.state = {
      loading: false,
      data: restaurantList,
      error: null,
      value: ''
    };
  }

  renderSeparator = () => {
    return (
      <View
        style={{
          height: 1,
          width: '86%',
          backgroundColor: '#CED0CE',
          marginLeft: '14%'
        }}
      />
    );
  };

  searchFilterFunction = text => {
    this.setState({
      value: text
    });

    const newData = restaurantList.filter(item => {
      const itemData = `${item.name.toUpperCase()} ${item.type.toUpperCase()}`;
      const textData = text.toUpperCase();
      return itemData.includes(textData);
    });

    this.setState({
      data: newData
    });
  };

  renderHeader = () => {
    return (
      <SearchBar
        placeholder="Type..."
        value={this.state.value}
        onChangeText={text => this.searchFilterFunction(text)}
      />
    );
  };

  render () {
    if (this.state.loading) {
      return (
        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
          <ActivityIndicator />
        </View>
      );
    } else {
      return (
        <View style={styles.container}>
          <FlatList
            keyExtractor={(item, index) => `${index}`}
            extraData={this.state}
            data={this.state.data}
            renderItem={({ item }) => (
              <Text>{item.name} {item.type}</Text>
            )}
            ItemSeparatorComponent={this.renderSeparator}
            ListHeaderComponent={this.renderHeader}
          />
        </View>
      );
    }
  }
}

小吃

您可以在以下小吃 https://snack.expo.io/@andypandy/flatlist-with-search 上看到它的工作原理

关于React-native 搜索栏和 flatList 问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54977824/

相关文章:

javascript - 在 React Native 中生成多个 APK?

android - 此构建中使用了已弃用的 Gradle 功能,使其与 Gradle 6 不兼容

javascript - 如何在 Javascript React Native 中获取对象的子值总和?

database - 全文搜索与标准数据库搜索

c# - 不区分大小写的列表搜索

search - REDIS 哈希 HMSET 中的 COUNT 和 SEARCH

android - TouchableOpacity、负边距和 Android 的问题 - React Native

react-native - FlatList 单选单元格

javascript - React Native TextInput 不允许输入/文本更改

react-native - 如何解决滚动中包含多个平面列表的虚拟列表警告