web-dev-qa-db-ja.com

React-Native FlatListで選択したアイテムを強調表示する

リモートサービスからデータを取得し、FlatListに読み込むために、単純なReactネイティブアプリケーションを作成しました。ユーザーがアイテムをタップすると、そのアイテムが強調表示され、選択が保持されます。このような些細な操作は難しくないはずです。何が欠けているのかわかりません。

import React, { Component } from 'react';
import {
  StyleSheet,
  Text,
  View,
  FlatList,
  ActivityIndicator,
  Image,
  TouchableOpacity,
} from 'react-native';

export default class BasicFlatList extends Component {
  constructor(props) {
    super(props);

    this.state = {
      loading: false,
      data: [],
      page: 1,
      seed: 1,
      error: null,
      refreshing: false,
      selectedItem:'null',
    };
  }

  componentDidMount() {
    this.makeRemoteRequest();
  }

  makeRemoteRequest = () => {
    const {page, seed} = this.state;
    const url = `https://randomuser.me/api/?seed=${seed}&page=${page}&results=20`;
    this.setState({loading: true});
    fetch(url)
      .then(res => res.json())
      .then(res => {
        this.setState({
          data: page === 1 ? res.results : [...this.state.data, ...res.results],
          error: res.error || null,
          loading: false,
          refreshing: false
        });
      })
      .catch(error => {
        this.setState({error, loading: false});
      });
  };

  onPressAction = (rowItem) => {
    console.log('ListItem was selected');
    console.dir(rowItem);
    this.setState({
      selectedItem: rowItem.id.value
    });
  }

  renderRow = (item) => {
    const isSelectedUser = this.state.selectedItem === item.id.value;
    console.log(`Rendered item - ${item.id.value} for ${isSelectedUser}`);
    const viewStyle = isSelectedUser ? styles.selectedButton : styles.normalButton;
    return(
        <TouchableOpacity style={viewStyle} onPress={() => this.onPressAction(item)} underlayColor='#dddddd'>
          <View style={styles.listItemContainer}>
            <View>
              <Image source={{ uri: item.picture.large}} style={styles.photo} />
            </View>
            <View style={{flexDirection: 'column'}}>
              <View style={{flexDirection: 'row', alignItems: 'flex-start',}}>
                {isSelectedUser ?
                  <Text style={styles.selectedText}>{item.name.first} {item.name.last}</Text>
                  : <Text style={styles.text}>{item.name.first} {item.name.last}</Text>
                }
              </View>
              <View style={{flexDirection: 'row', alignItems: 'flex-start',}}>
                <Text style={styles.text}>{item.email}</Text>
              </View>
            </View>
          </View>
        </TouchableOpacity>
    );
  }

  render() {
    return(
      <FlatList style={styles.container}
        data={this.state.data}
        renderItem={({ item }) => (
          this.renderRow(item)
        )}
      />
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    marginTop: 50,
  },
  selectedButton: {
    backgroundColor: 'lightgray',
  },
  normalButton: {
    backgroundColor: 'white',
  },
  listItemContainer: {
    flex: 1,
    padding: 12,
    flexDirection: 'row',
    alignItems: 'flex-start',
  },
  text: {
    marginLeft: 12,
    fontSize: 16,
  },
  selectedText: {
    marginLeft: 12,
    fontSize: 20,
  },
  photo: {
    height: 40,
    width: 40,
    borderRadius: 20,
  },
});

ユーザーがリスト内のアイテムをタップすると、選択したアイテムの情報を使用して「onPress」メソッドが呼び出されます。ただし、Flatlistのハイライトアイテムの次のステップは実行されません。 「UnderlayColor」も役に立たない。

どんなヘルプ/アドバイスも大歓迎です。

15
Krishnan Sriram

代わりに this.state.selectedItemおよびrowItem.id.value、RN FlatListのドキュメントの例に示されているように、キーと値のペアでMapオブジェクトを使用することをお勧めします。 https://facebook.github.io/react-native/docs/flatlist.html 。 js Mapのドキュメントもご覧ください: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map

@ j.I-Vが推奨するextraDataプロパティは、this.state.selectedが選択時に変更されたときに再レンダリングが行われるようにします。

OnPressActionは、特定の時間に選択の数を制限するか、ユーザーが選択を切り替えることを許可しないかなどによって、明らかに下の例から少し変更されます。

さらに、決して必要ではありませんが、renderItemコンポーネントに別のクラスまたは純粋なコンポーネントを使用したいと思います。次のようになります:

export default class BasicFlatList extends Component {
  state = {
    otherStateStuff: ...,
    selected: (new Map(): Map<string, boolean>) //iterable object with string:boolean key:value pairs
  }

  onPressAction = (key: string) => {
    this.setState((state) => {
      //create new Map object, maintaining state immutability
      const selected = new Map(state.selected);
      //remove key if selected, add key if not selected
      this.state.selected.has(key) ? selected.delete(key, !selected.get(key)) : selected.set(key, !selected.get(key));
      return {selected};
    });
  }

  renderRow = (item) => {
    return (
        <RowItem
          {...otherProps}
          item={item}
          onPressItem={this.onPressAction}
          selected={!!this.state.selected.get(item.key)} />
    );
  }

  render() {
    return(
      <FlatList style={styles.container}
        data={this.state.data}
        renderItem={({ item }) => (
          this.renderRow(item)
        )}
        extraData={this.state}
      />
    );
  }
}


class RowItem extends Component {
  render(){
    //render styles and components conditionally using this.props.selected ? _ : _
    
    return (
      <TouchableOpacity onPress={this.props.onPressItem}>
        ...
      </TouchableOpacity>
    )
  }
}
6
swimisbell

ExtraListプロパティをFlatListに渡して、選択に基づいてアイテムを再レンダリングする必要があります

ここに :

<FlatList style={styles.container}
    data={this.state.data}
    extraData={this.state.selectedItem}
    renderItem={({ item }) => (
      this.renderRow(item)
    )}
 />

ソース: https://facebook.github.io/react-native/releases/next/docs/flatlist.html

RenderItem関数が依存するすべてのものが、更新後===ではないprop(extraDataなど)として渡されることを確認してください。そうでない場合、変更時にUIが更新されない可能性があります

5
j.l-V

次のようなことができます:

  1. FlatListの参照を作成します。
  2. RenderItemには、renderItemのインデックスを渡すonPressイベントでTouchableOpacityのようなものを使用します。配列の各アイテムには、次のようなインデックスが必要です。

    const CategoryList = [{name: "a name"、index:0}、....]

  3. コンポーネントの状態を制御する変数を作成します。

    this.state = {selected_category: ''}

  4. 上記で作成した状態を変更する関数:

    _handleCategorySelect =(インデックス)=> {this.setState({selected_category:index}); }

flatList:

<FlatList
   data={CategoryList}
   ref={(e) => this.categoryList = e}
   renderItem={(category) => 
      <TouchableOpacity 
        onPress={() => this._handleCategorySelect(category.item.index)}
        style={this.state.selected_category === category.item.index ?  
                 styles.selected : null}
      >
         <Text>{category.item.name}</Text>
      </TouchableOpacity>
   }
/>
  1. 選択したアイテムのスタイルを作成してください!
1
Maicon Gilton