web-dev-qa-db-ja.com

React-native-ある画面から別の画面にデータを渡す

反応ネイティブを使用する方法を学習しようとしているので、サーバーからユーザーのリストを取得し、listviewに表示し、行を押すとユーザーの画面を表示する小さなアプリを作成していますデータ。

ある画面から別の画面に移動するようにナビゲータを設定しました。行を押すと、新しい画面を開くことができますが、この新しい画面にデータを渡す方法がわかりません。

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

最初に、ボタンと空のリストビュー(ContactList.js)を含む画面を表示し、ボタンを押した後、listviewの更新に使用されるJSONデータを取得します。

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

行が押されると、ユーザーのデータを表示することになっている新しい画面ContactDetails.jsが開きます。

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

この時点で私はこのエラーを受け取りました:

undefinedはオブジェクトではありません(「this.props.title」を評価)

私は次のような多くのことを試しました:

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

または

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

または

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

しかし、無駄に。

また、reduxを使用する必要があることも読みました。私は何か間違ったことをしていますか?

編集:問題はここにあります:

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

そこにいくつかのパラメーターを渡す必要があると思いますが、上記で試したものはすべて失敗しました。

10
vincenth

したがって、問題はここにあるようです:

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

    return undefined;
  }}
/>

RenderScene関数では、ルートを受け取ります(そしてroute.componentを使用します)が、route.propsまたはroute.passPropsを渡さないでください。そして、その時点でNavigatorのソースに表示されているものから、作成したときに完全なルートオブジェクトが必要です。そのため、小道具を渡すことができるはずです。

例えば:

<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: { /* ... */ }
    });
  }}
>

Reduxをセットアップすることもできますが、それは要件ではありません。アプリが大きくなる場合は、外部ストアの使用を検討する必要があります!


更新:

別の問題もあります。

機能コンポーネントを使用します。機能コンポーネントにはthisがありません。彼らはパラメーターで小道具を受け取ります。

したがって、次のようになります。

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

私はまだデフォルトのナビゲーションが面倒だと感じています。このライブラリをルーティングに使用することを強くお勧めします: https://github.com/aksonov/react-native-router-flux

1
Shivam Sinha

コードを置き換える:

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

で:

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

これは結構です:

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