ReactNativeのScrollViewで、scroll to topする

ScrollViewには、scrollTo というpropsが用意されているので、ref経由で呼び出せばよい。

refとそれを使った関数を用意して、

  const scrollViewRef = useRef<ScrollView>(null);
  const goToTop = () => {
    if (scrollViewRef.current) {
      scrollViewRef.current.scrollTo({ x: 0, y: 0, animated: false });
    }
  };

こんな感じで使う

<ScrollView ref={scrollViewRef}>
  {content}
</ContentText>
<Button onPress={closeDialog}>
  press
</Button>

enjoy

React Hooks のテストを react-hooks-testing-library で書く

React Hooksのテストには react-testing-library を使っていたのですが、 react-hooks-testing-library を試したところ、なかなか良かったので簡単に紹介します。

react-hooks-testing-library とは

GitHub はこちら: https://github.com/testing-library/react-hooks-testing-library

Hooksをテストする際に、コンポーネントのコンテキスト内で実行するのでなく、 Hooksを直接呼び出してテストできるライブラリです。

もともとは react-testing-library と完全に別のパッケージだったようですが、

  • react-testing-library でも似たような機能を開発中であったこと
  • react-hooks-testing-library の作者が react-testing-library を意識して作成していたこと

から、最近 testing-library ファミリーに移動したようです。

useStateのテスト

たとえばこのような useState を使った Hooks をテストするとします。 単純に、Menuの開閉を状態に持ち、toggleする関数を作っただけです。

import { useState } from 'react';

export const useMenuOpen = () => {
  const [isOpen, setIsOpen] = useState(false);
  const toggle = () => setIsOpen(!isOpen);
  return { isOpen, setIsOpen, toggle };
};

これのテストは、下のように書けます。 ここで react-hooks-testing-library から import している act() は、'react-test-renderer'が提供しているものと同じものです。

import { act, renderHook } from '@testing-library/react-hooks';
import { useMenuOpen } from './useMenuOpen';

it('should toggle', () => {
  const { result } = renderHook(() => useMenuOpen());
  // 初期状態
  expect(result.current.isOpen).toBe(false);
 
  // 開閉する 
  act(() => {
    result.current.toggle();
  });
  expect(result.current.isOpen).toBe(true);

  // もう一度開閉する
  act(() => {
    result.current.toggle();
  });
  expect(result.current.isOpen).toBe(false);
});

react-testing-library を使うときように、何らかのコンポーネントレンダリングする必要はありません。 純粋にHooksだけのテストを書くことができます。

つかいどころ

react-hooks-testing-library を使えば、 適当なコンポーネントを記載する必要がないので、 Hooksのロジックのテストだけに集中できます。

しかし、Hooksのテストをすべて置き換えるものではなく、状況に応じて使い分けるべきです。

react-testing-library の README では、このように書いてあります。

NOTE it is not recommended to test single-use custom hooks in isolation from the components where it's being used. It's better to test the component that's using the hook rather than the hook itself. The React Hooks Testing Library is intended to be used for reusable hooks/libraries.

つまり、特定のコンポーネントに紐付いたHooksなどは、これまで同様に react-test-library を使ってコンポーネントそのものをテストと良いでしょう。クリック時のHooksの挙動などをテストする場合ですね。

一方、特定のコンポーネントに結びついていないHooks、例えば汎用的に使うHooksやパッケージ用に書いているHooksなどは、こちらの react-hooks-testing-library を使うほうがテストが見通しよく書けると思います。

以上です。

とりあえずしばらく使ってみようと思います。

Snapshotの結果がローカルとCircleCIで違っていた

Reactアプリ開発では Snapshot テストをよく使うんですが、あるとき ローカルとCircleCI上で作成したのSnapshotの結果が異なり、テストが落ちるという現象が発生しました。

これは少し調べたところ、Nodeのバージョン違いによってJest実行結果が異なっていたためでした。

当時のローカル(Mac)マシンの Node のバージョンは、

$ node -v
v12.8.0

ですが、CircleCIのNodeは、

version: 2
jobs:
  test:
    docker:
      - image: circleci/node:10.5.0

でした。

というわけで以下の手順で node をダウングレード。

$ brew unlink node
$ brew link node@10
$ brew link --force --overwrite node@10
$ node -v
v10.16.3

厳密にはまだバージョンが異なってましたが、Snapshotの結果はこれで一致。