【経緯】
簡単に私がハマった経緯を説明します。
React NavigationのcreateStackNavigatorを使い、メイン画面と設定画面の二画面からなるアプリを作成していました。
アプリの動きとして、メイン画面から設定画面へ遷移して何かしら設定を行う。そして、ヘッダーのBackボタンでメイン画面に戻った際、メイン画面が設定画面で設定した値に更新されているというのが、私が
期待した動作です。
しかし、更新されない。
調べたところ、createStackNavigatorでは画面の遷移は元の画面の上に積みあがるイメージで、元の画面は残ったまま、新しい画面が描画されるようです。
つまり、子画面から親画面に戻っても、componentDidMountは呼び出されないということです。
#【更新する方法】
以上の経緯から、調べ倒しました。三日くらい……。
とりあえずソースをどうぞ。
headerRightを押下時にメイン画面から子画面のSettingsへと遷移します。
前提として、親画面のcomponentDidMountで親画面の更新がおこなわれることとします。
【親画面】
export default class Main extends React.Component {
constructor(){
super();
this.state = {
}
}
static navigationOptions = ({ navigation }) => {
return {
headerRight: (
<Button title={"Button"}
onPress={()=>{navigation.state.params.goToSettings()}}
/>
),
};
};
componentDidMount(){
this.props.navigation.setParams({
goToSettings: this.goToSettings.bind(this),
});
goToSettings(){
this.props.navigation.navigate("Settings",{refresh:
this.componentDidMount.bind(this)});
}
render() {
return (
<View/>
);
}
}
【子画面】
//好きな場所でこれを呼び出すと親画面の更新が行われる。
this.props.navigation.state.params.refresh();
【説明】
ポイントは三つです。
①componentDidMount内で、this.props.navigation.setParamsを使用し
親画面(メイン画面)のパラメータに自クラスのメソッドを設定する。
②上で設定したメソッドは、子画面(設定画面)への遷移と、
子画面に親画面のcomponentDidMountをパラメータとして渡すという内容。
③static navigationOptionsでヘッダーに設定したボタンのonPressで
①のパラメータを呼び出す。
【①setParamsする】
公式に書いてあったやり方でやってます。
Header interaction with its screen componentの項を参照ください。
ここでは、②で作成するメソッドをgoToSettingsってパラメータにセットしてます。
componentDidMount(){
this.props.navigation.setParams({
goToSettings: this.goToSettings.bind(this),
});
【②子画面に遷移するメソッドを作成する】
これですね。
goToSettings(){
this.props.navigation.navigate("Settings",{refresh:
this.componentDidMount.bind(this)});
}
Settingsって画面に遷移してます。
それで"Settings"の後ろに注目していただきたいんですけど、
{refresh: this.componentDidMount.bind(this)}
この部分でパラメータを、親画面のcomponentDidMountをrefreshていう名前で渡してます。
上にも書きましたが、this.props.navigation.state.params.refresh();を
子画面(上のソースだとSettings)で呼び出すと、親画面(Main)が更新(componentDidMount)されます。
【③navigationOptionsのonPressで①のパラメータを呼び出す。】
static navigationOptions = ({ navigation }) => {
return {
headerRight: (
<Button title={"Button"}
onPress={()=>{navigation.state.params.goToSettings()}}
/>
),
};
};
自分のアプリではヘッダーライトにボタンを設置して、それのonPressで設定したパラメータを呼び出しています。
【まとめ】
分かりやすいように考える順番にまとめます。
(上の説明はソースコードの順番で、まとめで書いているのは考え方の順番です。)
1. 子画面への遷移と親画面の更新メソッドをパラメータで渡すメソッドを作成する
2. 1のメソッドを親画面のパラメータとしてセットする。
3. 2のパラメータをstatic navigationOptionsのonPressで呼び出す。
# 【あとがき】
なぜこんな風になったのかというと、
headerRightのところで、子画面へ遷移してメソッドをパラメータとして渡す方法だと、子画面で呼び出したときにうまく処理されませんでした。
子画面から親画面を更新する方法を調べても日本語の記事がとても少なかったです。それで仕方なく、「react navigation refresh」って調べると海外の質問サイトで結構質問があって、それを参考にしてたんですけど、どの方法もうまくいきませんでした。
あれやこれやと試行錯誤してたら、たまたまうまくいく方法が見つかったんですねー。
その方法が、render内で this.props.navigation.navigateする方法。
render() {
return (
<View>
<Button onPress={()=>{this.props.navigation.navigate("Settings",
{refresh: this.componentDidMount.bind(this)});}}
</View>
);
}
じゃあこれでいいかというと、これだとヘッダーにボタン配置できなかったので(flexに関して数時間調べた)、やっぱりstatic navigationOptions使いたいってなりました。
結果、これだとうまく親の更新ができることがわかったので、navigateするだけの関数を作ってheaderRightのonPressで呼び出したらどうかと思いついて、以上の方法になりました。
ここでの呼び出しにも一苦労あり、公式のサイトを調べました。
Adding a button to the headerの項を見ていただくと、
The binding of this in navigationOptions is not the HomeScreen instance,
so you can't call setState or any instance methods on it
ていう一文がありまして、thisがstatic navigationOptionsを書いたクラスのインスタンスを表していないからthis.メソッドが使えないんですよね。
static navigationOptions = ({ navigation }) => {
return {
headerRight: (
<Button title={"Button"}
onPress={()=>{this.goToSettings()}}
/>
),
};
};
つまり、上のコードはダメだということなので、困ったなと読め進めていくと
Header interaction with its screen componentのところに解決策が書いてありました。
詳しくはそちらを参考にしてください。
いや、クリティカルに問題部分だけを解決したくて、質問版とか公式ドキュメントのサンプルコードを漁ってたんですけど、一から読んでしっかり理解していったほうが早いし、正しい知識が身に着くのかもしれませんね。
急がば回れみたいな……。