反応ナビゲーションフォーカスレンダリングであなたの反応ネイティブアプリケーションパフォーマンスを改善すること


反応ネイティブでのパフォーマンスは常に戦いのビットであった、偉大なパフォーマンスは達成可能ですが、従来のWeb開発よりも最適化されたコードにはるかに敏感です.

背景
私は最近、私のホームタブ画面に複数の再レンダリングを含むコンポーネントのコレクションを持っていた私のアプリケーションで問題を発見した.
問題は解決するのに全く簡単でした、しかし、これの間、私はこの非Performantスクリーンが私のアプリケーションの中で他のタブを減速させているとわかりました.

これがなぜ起こったかの簡単な例
この問題を再現する簡単な例を作りましょう.
消耗品
これが問題を引き起こしている私たちのコンポーネントは、ここでは、我々は明らかなパフォーマンスヒットを見ることがレンダリングされるたびに.それはreduxに接続され、カウントの状態が変更されるたびに再描画されます.
const ExpensiveComponent = () => {
  const {count} = useSelector((state) => ({
    count: state.count,
  }));
  return  (
    <>
      {!!count && <Text>Count is {count}</Text>}
      {new Array(5000).fill(0).map((v, k) => (
        <Text key={k}>{v}</Text>
      ))}
    </>
  );
};

ホームレス
私たちのホーム画面は、高価なコンポーネントをレンダリングし、画面2に行くことができます.ExpensiveComponentが再描画される限り、我々は明らかなパフォーマンスヒットを見るでしょう.
const HomeScreen = () => {
    const navigation = useNavigation();
    const goScreen2 = ()=>{
        navigation.navigate('Screen2')
    }
    return (
        <>
            <Button
                title={'Go to Screen 2'}
                onPress={goScreen2}
            />
            <ExpensiveComponent />
        </>
    );
};
スクリーン2
我々の第2のスクリーンはそれ自体でパフォーマンス問題を持ちません、それはカウントを更新するために行動を出すボタンを含みます.それは多くのレンダリングされませんし、ボタンを押すと表示されるカウントを即座に期待して表示されます.
const Screen2: React.FC<ComponentType> = ({}) => {
    const {count} = useSelector((state) => ({
        count: state.count,
    }));
    const dispatch = useDispatch();
    const setCount = useCallback(
        (data: number) => {
            return dispatch(AppActions.setCount(data));
        },
        [dispatch],
    );
    const onPress = ()=> {
        setCount((count || 0) + 1)
    }
  return (
      <Button
        onPress={onPress}
        title={`Update Count (${count || 0})`}/>
  );
};
スクリーン2には、パフォーマンスの問題はないと思いますか?悪い.更新カウントボタンを押すと一貫して約250 msでUIをブロックしていた、これは私が作ったツールを使用して見ることができますreact-native-performance-monitor.


それで、なぜ、これは起こりましたか?
その理由は、他のタブは、また、それが判明したとして、HomeMabで使用された状態を更新を使用していた、アクティブなタブは、彼らが示されていない場合でも、再レンダリングされます.
最適化されたコンポーネントでさえ、これが起こるという事実は意識するのに役に立ちます.あなたがセッションで訪問されているあなたのアプリケーションの5つのタブがある場合は、すべての状態の更新は、それらのすべての再レンダリングをトリガすることができます.

react-navigation-focus-renderでこの振舞いを防ぐ
これは私の新しいNPMパッケージが入っているところです.画面をフォーカスするまで、子供たちはレンダリングされません
const ExpensiveComponent = () => {
  const {count} = useSelector((state) => ({
    count: state.count,
  }));
  return  (
    **<FocusRender>**
      {!!count && <Text>Count is {count}</Text>}
      {new Array(5000).fill(0).map((v, k) => (
        <Text key={k}>{v}</Text>
      ))}
    **</FocusRender>**
  );
};

単にこれを加えることによって、我々の例はより多くの実行者です.以下にそれを証明する比較を示します.

これは、元の250 msに対して約6 msの平均レンダリング時間を示している.

これはどうやって動くの?
それは非常に簡単です、このモジュールは、画面がフォーカスされるまで非アクティブな画面の画面の再レンダリングを防止することによって動作します.
コード全体はhereです.これは、スクリーンショットがフォーカスされている場合にのみtrueを返します.
これを明確に説明するには、上記の例の動作を示します.

ご覧のように、コンポーネントがどれくらい高価かによって、この再描画が発生したときに顕著です.しかし、極端な場合には以下のように振る舞う傾向があります.


結論
このライブラリは最適化されたコンポーネントを置き換えるべきではありませんが、大きなアプリケーションを信じています.
この便利なことが分かったら、GitHubで調べてください.
ハッピーハッキング!