J'essaie d'apprendre à utiliser react-native. Je crée donc une petite application qui récupère une liste d'utilisateurs d'un serveur, l'affiche dans une variable listview
et où le fait d'appuyer sur une ligne ouvre un écran affichant les données de l'utilisateur.
J'ai configuré un navigateur pour passer d'un écran à un autre. En appuyant sur la ligne, je peux ouvrir un nouvel écran, mais je ne vois pas comment passer des données à ce nouvel écran.
J'ai configuré un navigateur dans index.Android.js
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);
Tout d’abord, j’affiche un écran avec un bouton et une liste vide (ContactList.js). Après avoir appuyé sur le bouton, je récupère des données JSON utilisées pour mettre à jour la listview
:
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;
}
}
Lorsqu'on appuie sur une ligne, un nouvel écran ContactDetails.js
apparaît, qui est censé afficher les données de l'utilisateur:
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>
);
}
À ce stade, j'ai eu cette erreur:
indéfini n'est pas un objet (évaluer 'this.props.title')
J'ai essayé beaucoup de choses telles que:
this.props.navigator.Push({
component: ContactDetails,
title: rowData.name.title,
first: rowData.name.first,
last: rowData.name.last,
});
ou
this.props.navigator.Push({
component: ContactDetails,
props: {
title: rowData.name.title,
first: rowData.name.first,
last: rowData.name.last,
}
});
ou
this.props.navigator.Push({
component: ContactDetails,
passProps: {
title: rowData.name.title,
first: rowData.name.first,
last: rowData.name.last,
}
});
Mais en vain.
J'ai aussi lu que je devrais utiliser redux. Est-ce que je fais quelque chose de mal ?
EDIT: Le problème est ici:
<TouchableHighlight
onPress={() => {
this.props.navigator.Push({
component: ContactDetails,
});
}}
>
Je suppose que je devrais passer certains paramètres ici, mais tout ce que j'ai essayé ci-dessus a échoué.
Donc, le problème semble se situer ici:
<Navigator
initialRoute={{ name: 'index', component: ContactList }}
renderScene={(route, navigator) => {
if (route.component) {
return React.createElement(route.component, { navigator });
}
return undefined;
}}
/>
Dans la fonction renderScene, vous recevez la route (et utilisez route.component), mais vous ne transmettez pas votre route.props ou route.passProps ou ce que vous voulez appeler! Et d'après ce que je vois dans la source de Navigator à ce moment-là, vous devriez avoir l'objet route complet tel que vous l'avez créé. Donc, vous devriez pouvoir passer vos accessoires.
Par exemple:
<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: { /* ... */ }
});
}}
>
Vous pouvez également configurer redux, mais ce n’est pas une obligation. Cependant, si votre application grossit, vous devriez vraiment envisager d'utiliser un magasin externe!
Mettre à jour:
Il y a aussi un autre problème.
Vous utilisez des composants fonctionnels. Les composants fonctionnels n'ont pas de this
. Ils reçoivent les accessoires en paramètre.
Donc ça devrait être comme ça:
export default function ContactDetails(props) {
return (
<View style={styles.container}>
<Text>{props.title}</Text>
<Text>{props.first}</Text>
<Text>{props.last}</Text>
</View>
);
}
Je pense toujours que la navigation par défaut est trop lourde. Je suggère fortement d'utiliser cette bibliothèque pour le routage: https://github.com/aksonov/react-native-router-flux
Remplacer le code:
<TouchableOpacity
onPress={() => this._handlePress()}
style={styles.button}
>
Avec :
<TouchableOpacity
onPress={() => this._handlePress()}
style={styles.button}
navigator={navigator} {...route.props}
>
C'est bon:
this.props.navigator.Push({
component: ContactDetails,
props: {
title: rowData.name.title,
first: rowData.name.first,
last: rowData.name.last,
}
});