webentwicklung-frage-antwort-db.com.de

React-native - Übergabe von Daten von einem Bildschirm an einen anderen

Ich versuche zu lernen, wie man reaktives verwenden kann, also setze ich eine kleine App ein, die eine Liste von Benutzern von einem Server abruft, diese in einer listview anzeigt und durch Drücken einer Zeile ein Fenster mit Benutzerdaten angezeigt wird. 

Ich habe einen Navigator eingerichtet, um von einem Bildschirm zum anderen zu gelangen. Wenn Sie auf die Zeile drücken, kann ich einen neuen Bildschirm öffnen, aber ich weiß nicht, wie ich Daten an diesen neuen Bildschirm übergeben soll.

Ich habe einen Navigator in index.Android.js eingerichtet

import React from 'react';
import {
  AppRegistry,
  Navigator,
} from 'react-native';

import ContactList from './src/containers/ContactList.js';

function MyIndex() {
  return (
    <Navigator
      initialRoute={{ name: 'index', component: ContactList }}
      renderScene={(route, navigator) => {
        if (route.component) {
          return React.createElement(route.component, { navigator });
        }

        return undefined;
      }}
    />
  );
}

AppRegistry.registerComponent('reactest', () => MyIndex);

Zuerst zeige ich einen Bildschirm mit einer Schaltfläche und einer leeren Listenansicht (ContactList.js) an, nachdem ich die Schaltfläche gedrückt habe, rufe ich einige JSON-Daten ab, die zum Aktualisieren der listview verwendet werden: 

import React, { Component, PropTypes } from 'react';
import {
  Text,
  View,
  TouchableOpacity,
  TouchableHighlight,
  ListView,
  Image,
} from 'react-native';

import styles from '../../styles';
import ContactDetails from './ContactDetails';

const url = 'http://api.randomuser.me/?results=15&seed=azer';

export default class ContactList extends Component {
  static propTypes = {
    navigator: PropTypes.object.isRequired,
  }
  constructor(props) {
    super(props);

    const datasource = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
    this.state = {
      jsonData: datasource.cloneWithRows([]),
      ds: datasource,
    };
  }
  _handlePress() {
    return fetch(url)
      // convert to json
      .then((response) => response.json())
      // do some string manipulation on json
      .then(({ results }) => {
        const newResults = results.map((user) => {
          const newUser = {
            ...user,
            name: {
              title: `${user.name.title.charAt(0).toUpperCase()}${user.name.title.slice(1)}`,
              first: `${user.name.first.charAt(0).toUpperCase()}${user.name.first.slice(1)}`,
              last: `${user.name.last.charAt(0).toUpperCase()}${user.name.last.slice(1)}`,
            },
          };

          return newUser;
        });

        return newResults;
      })
      // set state
      .then((results) => {
        this.setState({
          jsonData: this.state.ds.cloneWithRows(results),
        });
      });
  }
  renderRow(rowData: string) {
    return (
      <TouchableHighlight
        onPress={() => {
          this.props.navigator.Push({
            component: ContactDetails,
          });
        }}
      >
        <View style={styles.listview_row}>
          <Image
            source={{ uri: rowData.picture.thumbnail }}
            style={{ height: 48, width: 48 }}
          />
          <Text>
            {rowData.name.title} {rowData.name.first} {rowData.name.last}
          </Text>
        </View>
      </TouchableHighlight>
    );
  }
  render() {
    const view = (
      <View style={styles.container}>
        <TouchableOpacity
          onPress={() => this._handlePress()}
          style={styles.button}
        >
          <Text>Fetch results?</Text>
        </TouchableOpacity>
        <ListView
          enableEmptySections
          dataSource={this.state.jsonData}
          renderRow={(rowData) => this.renderRow(rowData)}
          onPress={() => this._handleRowClick()}
        />
      </View>
    );

    return view;
  }
}

Wenn eine Zeile gedrückt wird, öffnet sich ein neuer Bildschirm ContactDetails.js, der die Benutzerdaten anzeigen soll: 

import React, {
} from 'react';

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

import styles from '../../styles';

export default function ContactDetails() {
  return (
    <View style={styles.container}>
      <Text>{this.props.title}</Text>
      <Text>{this.props.first}</Text>
      <Text>{this.props.last}</Text>
    </View>
  );
}

Zu diesem Zeitpunkt habe ich diesen Fehler erhalten: 

undefined ist kein Objekt (wertet 'this.props.title' aus)

Ich habe viele Dinge ausprobiert wie: 

this.props.navigator.Push({
    component: ContactDetails,
    title: rowData.name.title,
    first: rowData.name.first,
    last: rowData.name.last,
});

oder 

this.props.navigator.Push({
    component: ContactDetails,
    props: {
        title: rowData.name.title,
        first: rowData.name.first,
        last: rowData.name.last,
    }
});

oder

this.props.navigator.Push({
    component: ContactDetails,
    passProps: {
        title: rowData.name.title,
        first: rowData.name.first,
        last: rowData.name.last,
    }
});

Aber ohne Erfolg.

Ich habe auch gelesen, dass ich Redux verwenden sollte. Mache ich etwas falsch ?

EDIT: Das Problem ist hier: 

<TouchableHighlight
    onPress={() => {
      this.props.navigator.Push({
        component: ContactDetails,
      });
    }}
>

Ich gehe davon aus, dass ich dort einige Parameter übergeben sollte, aber was ich oben versucht habe, ist fehlgeschlagen.

8
vincenth

Das Problem scheint also hier zu liegen:

<Navigator
  initialRoute={{ name: 'index', component: ContactList }}
  renderScene={(route, navigator) => {
    if (route.component) {
      return React.createElement(route.component, { navigator });
    }

    return undefined;
  }}
/>

In der renderScene-Funktion erhalten Sie die Route (und verwenden Sie route.component), aber Sie übergeben nicht Ihre route.props oder route.passProps oder wie auch immer Sie es nennen möchten! Und von dem, was ich zu diesem Zeitpunkt in der Quelle von Navigator sehe, sollten Sie das vollständige Routenobjekt haben, wenn Sie es erstellt haben. Sie sollten also in der Lage sein, Ihre Requisiten weiterzugeben.

Zum Beispiel: 

<Navigator
  initialRoute={{ name: 'index', component: ContactList }}
  renderScene={(route, navigator) => {
    if (route.component) {
      return React.createElement(route.component, { navigator, ...route.props });
    }

    return undefined;
  }}
/>

// Push
<TouchableHighlight
  onPress={() => {
    this.props.navigator.Push({
      component: ContactDetails,
      props: { /* ... */ }
    });
  }}
>

Sie können auch Redux einrichten, aber das ist keine Voraussetzung. Wenn Ihre App jedoch größer wird, sollten Sie einen externen Store in Betracht ziehen!


Aktualisieren:

Es gibt auch ein anderes Problem.

Sie verwenden eine Funktionskomponente. Funktionskomponenten haben keine this. Sie erhalten die Requisiten in Parameter.

Also sollte es so sein:

export default function ContactDetails(props) {
  return (
    <View style={styles.container}>
      <Text>{props.title}</Text>
      <Text>{props.first}</Text>
      <Text>{props.last}</Text>
    </View>
  );
}
5
Hugo Dozois

Ich finde immer noch, dass die Standardnavigation zu umständlich ist. Ich empfehle dringend, diese Bibliothek für das Routing zu verwenden: https://github.com/aksonov/react-native-router-flux

1
Shivam Sinha

Code ersetzen:

<TouchableOpacity
      onPress={() => this._handlePress()}
      style={styles.button}
>

Mit: 

<TouchableOpacity
      onPress={() => this._handlePress()}
      style={styles.button}
     navigator={navigator} {...route.props}
>

Das ist okay:

this.props.navigator.Push({
component: ContactDetails,
   props: {
    title: rowData.name.title,
    first: rowData.name.first,
    last: rowData.name.last,
  }
});
1