LoginSignup
3

More than 5 years have passed since last update.

React Nativeで、いいねした時のタイムラグをなくす/componentWillReceivePropsをやめてgetDerivedStateFromPropsを使う

Last updated at Posted at 2018-12-20

目的

ツイッターぽいいいねを実装する。
ツイッターていいね押すとすぐハートが赤くなるけど、(少し昔の)airbnbは赤くなるまで少し時間がかかる。
どちらが良いかという話ではなく、自分はツイッターみたくすぐ赤くしたかった。
これを実現するのが目的。

もう一つの話として、
reactのライフサイクルが新しくなる話。
https://mae.chab.in/archives/60040

今まで componentWillReceiveProps をダメと知りながらも愛用してきたが、やっぱり文句言われすぎて、手放すことにした。

上の記事によると、
componentWillMountcomponentWillReceivePropscomponentWillUpdate
と合わせて、api通信時に色々問題を起こす可能性があるかつ、使い方が明確でなく、混乱するユーザーが多くいたらしい。

ので書き換えることにした。
componentWillReceivePropsの代用はgetDerivedStateFromPropsでいけるらしい。

今まで

ツイッターみたいな「いいね」をreact native + rails + mysqlで実装するときcomponentWillReceivePropsを使っていた。
(いまどきfirebase使えという話ではあるが、、)

demo_181220.gif

流れ的には
アイテムをいいねする(ハートをonPress)
→apiにrequest
→サーバ側でこのアイテムのステータスを(いいねされた状態に)変える
→レスポンスを返す
→レスポンスが返ってきたらハートの色を変える

である。

このレスポンスが返ってきたとき、propsが変化するのでこれをcomponentWillReceivePropsでキャッチしていた。

具体的には以下。

good.js
import React, { Component } from 'react';
...

type Props = {
...
}

class Good extends Component<Props> {
 state = {
   isGood: false, <-- アイテムがいいねされているか
 }

 componentDidMount() {
  ... 
  this.setState({
    isGood, <--- 初期状態でapiからいいねされているか取ってきてsetStateする
  });
 }

 componentWillReceiveProps(nextProps) {
   const { isGood } = nextProps;
   this.setState({
     isGood,  <--- いいねした時apiからレスポンス返ってきたらsetStateする
   });
 }

 render() {
   const { isGood } = this.state;
   return (
    ...
    <TouchableOpacity
      onPress{() => 
        this.setState({ 
          isGood: !isGood,
        });
        this.someAction(...);  <--いいねするaction 
      } 
    >
     ...     <--- いいねボタン(ハート)
    </TouchableOpacity>
    ...
   )
 }

こんな感じ。
わざわざ、storeで管理しているものをいちいちsetStateしているのはいいねして返ってくるまで時間がかかって、色が変わるのがラグく感じてしまうのをやめたかった。
昔のairbnbのいいねはこのタイプだった。
ツイッターはすぐ色変わる。

つまり、onPress内でまず、stateを更新してすぐ色変え、api通信から戻ってきた結果をcomponentWillReceiveProps内でsetStateしている、ということ。
onPress内でsetStateしないバージョンが上のgif(さっき載せたやつと同じ)
setStateすると下のようになる

demo_181220.gif

demo_181220_1.gif

これで一応やりたいことはできている。
ちなみにcomponentWillUpdateの中身でsetStateするのはメモリ食いつぶすので禁止されているのでここでは使えない。

getDerivedStateFromPropsを使う

componentWillReceiveを使っていたところを書き換えた。

 

good.js

 // componentWillReceiveProps(nextProps) {
 // const { isGood } = nextProps;
 //  this.setState({
 //   isGood,  <--- いいねした時apiからレスポンス返ってきたらsetStateする
 //  });
 //}

 static getDerivedStateFromProps(nextProps, prevState) {
   const { isGood } = nextProps;
   if (prevState.isGood !== isGood) { <--nextPropsが更新されたら
     return { isGood: isGood };
   }
   return {};
 }

nextPropsには新しいprops, prevStateには今のstateが自動的に入る。
nextPropsが更新されたらstateを書き換える。
これで全く同じことが実現できた。

getDerivedStateFromPropsはstatic メソッドでこれを実行することは
componentDidUpdate + setStateするのと同じことらしい。

【追記】reduxを使っていればgetDerivedStateFromPropsは不要

コメントをいただいて気づいた。
上で実現したかったことはreduxを使い、変数isGoodをstoreで管理していれば
getDerivedStateFromPropsは不要になる。
一例を書けば、以下のようになる。

good.js
import React from 'react';
...

type Props = {
...
}

const Good = (props: Props) => {
  const { isGood } = props;
  return (
    <TouchableOpacity
      onPress{() => 
        this.setIsGood(!isGood)         <--これがないときはツイッターみたいにすぐ色変わらない
        this.updateIsGood(!isGood);  <--いいねするaction 
      } 
    >
     ...     <--- いいねボタン(ハート)
    </TouchableOpacity>
    ...
   )
 }
actions/index.js
  export const setIsGood = isGood => ({
    type: SET_IS_GOOD,
    payload: isGood,
  });

   export const updateIsGood = isGood => ({
     postでapiにリクエスト飛ばす処理
      .then((res) => {    <-- resに いいねされているかに対応する true or falseが入っているとする
        dispatch(setIsGood(res));  <-- storeに値をsetする
      .catch((err) => {
        dispatch(setIsGood(!isGood)) <-- レスポンスエラーの時storeに変更前の値を入れる
      }
  });
reducers/index.js
const initialState = {
  isGood: false,
};

export function isGoodState(state = initialState, action) {
  switch (action.type) {
    case SET_IS_GOOD:
      return {
        ...state,
        isGood: action.payload,
      };
     default:
       return state;
  }
}

...

const rootReducers = combineReducers({
  isGoodState,
  ...
})

export default rootReducers;

これならgetDerivedStateFromPropsはいらないし、
pure functionで書くことができる

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3