Реакционно-родная панель поиска и проблема flatList - PullRequest
0 голосов
/ 04 марта 2019

Может кто-нибудь, пожалуйста, помогите мне устранить неполадки?

Я пытаюсь выполнить поиск в локальном файле 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 голосов
/ 04 марта 2019

Это действительно хорошее начало.Однако есть несколько проблем с вашим кодом.Итак, давайте пройдемся по нему и посмотрим, где мы можем сделать некоторые улучшения.

Конструктор

Во-первых, в своем конструкторе вы делаете данные объектом, а не массивом.Плоские списки не работают с объектами, которые они работают с массивами, поэтому это немедленно вызовет у вас проблемы.Вы действительно должны удалить {} из 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 вместо onChangeText.onChange возвращает объект, но вы хотите, чтобы 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 prop, поскольку это позволит 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

...