web-dev-qa-db-fra.com

Mettre en surbrillance un élément sélectionné dans FlatList React-Native

J'ai mis en place une simple application native de React pour obtenir les données d'un service distant et les charge dans une liste plate. Lorsqu'un utilisateur appuie sur un élément, celui-ci doit être mis en surbrillance et la sélection conservée. Je suis sûr qu'une opération aussi triviale ne devrait pas être difficile. Je ne sais pas ce qui me manque. 

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,
  },
});

Lorsque l'utilisateur appuie sur un élément de la liste, la méthode "onPress" est appelée avec les informations sur l'élément sélectionné. Mais l'étape suivante de l'élément en surbrillance dans Flatlist ne se produit pas. 'UnderlayColor' n'est d'aucune aide non plus. 

Toute aide/conseil sera très apprécié.

11
Krishnan Sriram

Au lieu de this.state.selectedItem et de définir/rechercher un rowItem.id.value, je vous recommanderais d'utiliser un objet Map avec des paires clé: valeur, comme indiqué dans l'exemple de documentation RN FlatList: https://facebook.github.io/react-native/ docs/flatlist.html . Jetez également un coup d’œil aux documents de js Map: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map .

Le paramètre extraData recommandé par @ j.I-V garantira le rendu du rendu lorsque cet état sélectionné est modifié.

Votre onPressAction changera évidemment un peu par rapport à l'exemple ci-dessous, selon que vous souhaitez limiter le nombre de sélections à un moment donné ou que vous n'autorisiez pas l'utilisateur à basculer la sélection, etc.

De plus, bien que cela ne soit absolument pas nécessaire, j'aime utiliser une autre classe ou un composant pur pour le composant renderItem; finit par ressembler à quelque chose comme ce qui suit:

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>
    )
  }
}

5
swimisbell

Vous devez passer un accessoire extraData à votre FlatList afin qu’il restitue vos éléments en fonction de votre sélection.

Ici : 

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

Source: https://facebook.github.io/react-native/releases/next/docs/flatlist.html

Assurez-vous que tout ce dont dépend votre fonction renderItem est passé en tant que prop (par exemple, extraData) qui n'est pas === après les mises à jour, sinon votre interface utilisateur risque de ne pas être mise à jour en cas de modification

3
j.l-V

Vous pouvez faire quelque chose comme:

  1. Créez une référence pour votre FlatList;
  2. Pour le renderItem, utilisez quelque chose comme TouchableOpacity avec un événement onPress transmettant l'index du renderItem, chaque élément de votre tableau devrait avoir un index comme ci-dessous: 

    const CategoryList = [ {nom: "un nom", index: 0}, ....]

  3. Créez une variable pour contrôler l'état du composant: 

    this.state = {selected_category: ''}

  4. La fonction pour changer l'état créé ci-dessus:

    _handleCategorySelect = (index) => { this.setState ({selected_category: index}); }

la 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. Créez un style pour l'élément sélectionné et c'est tout!
0
Maicon Gilton