react-native搜索栏和flatList问题

时间:2019-03-04 06:40:05

标签: 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"
}
]

1 个答案:

答案 0 :(得分:0)

这是一个非常好的开始。但是,您的代码有几个问题。因此,让我们看一下它,看看可以在哪里进行一些改进。

构造函数

首先,在构造函数中,您要使数据成为对象而不是数组。 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

renderHeader函数中,您使用的是onChange而不是onChangeTextonChange返回一个对象,但是您希望将text放入搜索栏中。您需要像这样更新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
    />
  );
};

searchFilterFunction

此功能存在多个问题。首先,您正在查看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

在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

上看到它的工作原理