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