React Nativeで、react-navigationとunstatedを同居させるTips


課題

 React Nativeにおいて、react-navigationとunstatedはどちらも有用で使いたいが、unstatedをconstructor()内や、componentDidMount()内などで使いたくてコンポーネントをHOC化すると、react-navigationが使えなくなる。

ダメなコード1

 react-navigationは準備済みだとして、以下の普通のコードだと、constructor()内では、unstatedで作ったグローバルStateがよめない。

HomeScreen.js
import React from "react";
import { Button, Text, View } from "react-native";
import { Subscribe } from "unstated";

import GlobalStateContainer from "../containers/GlobalStateContainer";

export default class HomeScreen extends React.Component {
  constructor(props) {
    super(props);
    this.state = { item: globalState.state.item };  // ← [ダメ]グローバルState内のデータをローカルStateにコピーして使いたいのに、できない
  }

  render() {
    return (
      <Subscribe to={[GlobalStateContainer]}>
        {globalState =>
          <View>
            <Text>Hello World! {globalState.state.item}</Text>        // ← これはOK
            <Button title="Go to InvoiceEdit" onPress={() => this.props.navigation.navigate("InvoiceEdit")} />
          </View>
        }
      </Subscribe>
    );
  }
}

ダメなコード2

 HOC化する。navigationがつかえなくなる。

HomeScreen.js
import React from "react";
import { Button, Text, View } from "react-native";
import { Subscribe } from "unstated";

import GlobalStateContainer from "../containers/GlobalStateContainer";

class HomeScreenContent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { item: props.globalState.state.item };  // ← これはOK
  }

  render() {
    let globalState = this.props.globalState;
    return (
      <View>
        <Text>Hello World! {globalState.state.item}</Text> // ← これはOK
        <Text>Hello World! {this.state.item}</Text>        // ← これはOK
        <Button title="Go to InvoiceEdit" onPress={() => this.props.navigation.navigate("InvoiceEdit")} />  // ← ここでコケる
      </View>
    );
  }
}

const HomeScreen = () => {
  return (
    <Subscribe to={[GlobalStateContainer]}>
      {globalState => <HomeScreenContent globalState={globalState} />}
    </Subscribe>
  );
};

export default HomeScreen;

 HOC化したことで、HomeScreenContentのthis.propsにnavigationが入ってこないため、this.props.navigation.navigate()でコケるのです。原因は、以下の部分でSubscribeからHomeScreenContentに向けて、自動的にはpropsが引き継がれないために、navigationもHomeScreenContentに届けられないためです。

打開策

 以下ならうまく動く。

HomeScreen.js
import React from "react";
import { Button, Text, View } from "react-native";
import { Subscribe } from "unstated";

import GlobalStateContainer from "../containers/GlobalStateContainer";

class HomeScreenContent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { item: props.globalState.state.item };  // ← これはOK
  }

  render() {
    return (
      <View style={styles.container}>
        <Text>Hello World! {globalState.state.item}</Text> // ← これはOK
        <Text>Hello World! {this.state.item}</Text>        // ← これはOK
        <Button title="Go to InvoiceEdit" onPress={() => this.props.navigation.navigate("InvoiceEdit")} />  // ← これもOK
      </View>
    );
  }
}

const HomeScreen = ({ navigation }) => {
  return (
    <Subscribe to={[GlobalStateContainer]}>
      {globalState => <HomeScreenContent globalState={globalState} navigation={navigation} />}
    </Subscribe>
  );
};

export default HomeScreen;

 ポイントは、navigationを名指しで子供に伝搬してやったことです。通常のpropsもこのように伝搬させなくてはいけません。