2
votes

Composant / onglet de re-rendu Expo / React Native TabView lors du changement d'état

J'écris une application en utilisant Expo (framework React Native). Chaque fois que l'état d'un composant est modifié avec la méthode setState (...), le composant doit effectuer un nouveau rendu pour afficher les dernières données d'état. Cela fonctionne avec les composants de base de React Native tels que View, Text, Button (j'ai essayé ces trois éléments), mais cela ne rend pas ici, où j'utilise le composant personnalisé TabView. Ceci est indiqué dans la documentation sur le composant:

"Toutes les scènes rendues avec SceneMap sont optimisées à l'aide de React.PureComponent et ne sont pas rendues à nouveau lorsque les accessoires ou les états des parents changent. Si vous avez besoin de plus de contrôle sur la mise à jour de vos scènes ( par exemple - déclencher un nouveau rendu même si navigationState n'a pas changé), utilisez directement renderScene au lieu d'utiliser SceneMap. "

Je ne comprends pas très bien comment gérer cela. Je voudrais que le composant soit à nouveau rendu chaque fois que l'état est modifié, dans ce cas, après avoir cliqué sur le bouton et appelé la fonction writeData ().

La documentation du composant TabView est ici: https://github.com/react-native-community/react-native-tab- voir .

import React from 'react';
import { TabView, SceneMap } from 'react-native-tab-view';

import { StyleSheet, Text, View, Dimensions, Button, ToastAndroid } from 'react-native';

export default class MojTabView extends React.Component {
state = {
    index: 0,
    routes: [
      { key: 'allEvents', title: 'VÅ¡etko' },
      { key: 'sortedEvents', title: 'Podľa druhu' },
      { key: 'myEvents', title: 'Moje akcie'}
    ],
    name: "Robert"
  };

MyEvents = () => (
    <View style={[styles.scene, { backgroundColor: 'white' }]}>
        <Text>Some content</Text>
        <Button
          style={{ margin: 5 }}
          onPress={this.writeData}
          title="Write data"
          color="#841584"
        />
        <Text>Name in state: {this.state.name}</Text>
    </View>
  );

SortedEvents = () => (
      <Text>Some content</Text>
);

AllEvents = () => (
     <Text>Some other content</Text>
);

writeData = () => {
  ToastAndroid.show("button click works!", ToastAndroid.LONG);
  this.setState({ name: "Richard"});

}
render() {
    return (
        <TabView
          navigationState={this.state}
          renderScene={SceneMap({
              allEvents: this.AllEvents,
              myEvents: this.MyEvents,
              sortedEvents: this.SortedEvents
          })}
          onIndexChange={index => this.setState({ index })}
          initialLayout={{ width: Dimensions.get('window').width }}
        />
    );
}

}

J'ai passé quelques heures à essayer d'y parvenir, sans solution. C'est ma première question StackOverflow, merci.


3 commentaires

Vous devez fournir votre propre implémentation pour renderScene si vous voulez forcer le nouveau rendu à chaque changement d'état ... car SceneMap qu'ils fournissent est un PureComponent (ne se restitue pas lorsque les accessoires ou les états du parent changent)


Avez-vous une idée / un indice sur la manière de réaliser cette propre implémentation?


consultez la réponse plz


3 Réponses :


0
votes

Les deux éléments d'état qui devraient déclencher le nouveau rendu de votre TabView sont:

  • index.
  • itinéraires

Ils l'ont fait à des fins d'optimisation des performances

si vous voulez forcer le rendu de votre TabView si vous modifiez une autre valeur d'état qui n'a rien à voir avec ces valeurs ... alors selon la documentation ... vous avez besoin pour fournir votre propre implémentation pour renderScene comme:

renderScene = ({route, jumpTo}) => { commutateur (route.key) { cas 'musique': revenir ; cas 'albums': revenir ; } };

dans ce cas:

Vous devez vous assurer que vos itinéraires individuels implémentent un shouldComponentUpdate pour améliorer les performances.


3 commentaires

D'accord, j'ai trouvé une solution un peu différente pour ce problème, mais merci d'avoir expliqué un peu.


Oui, je l'écris :)


Que faire si je souhaite simplement restituer le contenu de dans le cadre de ma scène, mais pas nécessairement la vue à onglets dans ce cas.



3
votes

D'accord, j'ai enfin pu trouver une bonne solution. Le but est de définir le contenu des onglets / routes individuels dans un fichier séparé comme un composant React Native typique avec son propre état, pas à l'intérieur de ce composant MyTabView, comme cela est fait même dans l'exemple de la documentation sur TabView. Ensuite, cela fonctionne comme il se doit.

Exemple simple: Voici le contenu de l'un des onglets:

 import MyExampleTab from './MyExampleTab'
 ...
 export default class MyTabView extends React.Component {
 ...
       render() {
           return (
              <TabView
                  navigationState={this.state}
                  renderScene={SceneMap({
                       ...
                       someKey: MyExampleTab,
                       ...
                  })}
                  onIndexChange={index => this.setState({ index })}
                  initialLayout={{ width: Dimensions.get('window').width }}
              />
         )

Voici comment je le référence dans le composant MyTabView:

import React from 'react';
import { Text, View, Button } from 'react-native';

export default class MyExampleTab extends React.Component {
state = {
    property: "Default value",
}

writeData = () => {
    this.setState({
        property: "Updated value"
   })
}

render() {
    return (
        <View>
            <Button
            style={{ margin: 5 }}
            onPress={this.writeData}
            title="Change the state and re-render!"
            color="#841584"
            />
            <Text>Value of state property: {this.state.property}</Text>
        </View>
    )
}
}

Je n'utilise donc pas , comme je le ferais normalement en utilisant un composant personnalisé, mais je l'écris tel quel, MyExampleTab . Ce qui est tout à fait logique. Et sur le bouton, cliquez sur les changements d'état et l'onglet ré-rend.


1 commentaires

Comment puis-je passer des accessoires avec cette approche?



0
votes

En vous basant sur ce que @druskacik a écrit ci-dessus, vous pouvez également transmettre un composant avec des accessoires

import MyExampleTab from './MyExampleTab'
 ...
 export default class MyTabView extends React.Component {
 ...
       render() {
           return (
              <TabView
                  navigationState={this.state}
                  renderScene={SceneMap({
                       ...
                       someKey:() => <MyExample foo={foodata} />,
                       ...
                  })}
                  onIndexChange={index => this.setState({ index })}
                  initialLayout={{ width: Dimensions.get('window').width }}
              />
         )


0 commentaires