【React hooks】意外と知らないrefの使い方

`React`でコンポーネントから子コンポーネントや要素などを操作するときに便利な`ref`だが、
意外に調べても使い方が出てこなかったので、様々な利用シーンに合わせて使い道をまとめてみた。

## DOMにアクセス
```jsx
import React, {
    useRef, useEffect
} from 'react';

const Component = () => {
    const el = useRef(null);

    useEffect(() => {
        console.log(el.current);
    }, []);

    return (
        <div ref={el}>DOM</div>
    );
};

export default Component;

```

`el.current`からdiv要素にアクセスできるようになる。
divの幅、高さを取ってきたり、D3などでDOMにグラフを描画する際に使用する。


## 子コンポーネントのインスタンスにアクセス
```jsx
import React, {
    useRef, useEffect
} from 'react';

class Child extends React.Component {
    someFunc = () => {
        return 'sample';
    }

    render() {
        return <div> </div>;
    }
}

const Component = () => {
    const ins = useRef(null);

    useEffect(() => {
        console.log(ins.current);
    }, []);

    return (
        <Child ref={ins} />
    );
};

export default Component;
```

`ins.current`から子コンポーネント`Child`のインスタンスにアクセスができる。
例えば、`ins.current.someFunc()`で子コンポーネントの関数を実行することができる。
※関数コンポーネント (functional component)ではインスタンスが作成されないため利用できない。

## refのフォワーディング(forwardRef)
```jsx
import React, {
    useRef, useEffect
} from 'react';

const Child = React.forwardRef((props, ref) => {
    return (
        <div ref={ref}>DOM</div>
    );
});

const Component = () => {
    const el = useRef(null);

    useEffect(() => {
        console.log(el.current);
    }, []);

    return (
        <Child ref={el} />
    );
};

export default Component;

```
`el.current`で親コンポーネントから子コンポーネントを介して`div`要素にアクセスすることができる。
HOC(Higher-Order Component)などでコンポーネントを関数で囲む際などに、refをそのまま受け渡すという目的で利用する。
アプリケーションが複雑になればなるほど重宝する機能。

## 複数refs
```jsx
import React, {
    useRef, useEffect
} from 'react';

const data = [0, 1, 2];

const Component = () => {
    const els = useRef([]);
    data.forEach((_, i) => {
        els.current[i] = React.createRef();
    });

    useEffect(() => {
        console.log(els.current);
    }, []);

    return (
        <div>
            {
                data.map((n, i) => {
                    return (
                        <div key={n} ref={els.current[i]} >{n}</div>
                    );
                })
            }
        </div>
    );
};

export default Component;

```
`els.current`には`div`要素の配列が格納されるようになる。
複数の要素にアクセスが必要なシーンで利用する。

## 応用編（複数Refs × 複数Refs）
```jsx
import React, {
    useRef, useEffect
} from 'react';

const data = [0, 1, 2];

const ChildComponent = React.forwardRef(({ val1 }, ref) => {
    const els = useRef([]);
    data.forEach((_, i) => {
        els.current[i] = React.createRef();
    });

    useEffect(() => {
        ref.current = els.current;
        return () => {
            ref.current = null;
        };
    }, []);

    return (
        <div>
            {
                data.map((val2, i) => {
                    return (
                        <div key={val2} ref={els.current[i]}>
                            {val1}
                            -
                            {val2}
                        </div>
                    );
                })
            }
        </div>
    );
});

const Component = () => {
    const els = useRef([]);
    data.forEach((_, i) => {
        els.current[i] = React.createRef();
    });

    useEffect(() => {
        console.log(els.current);
    }, []);

    return (
        <div>
            {
                data.map((val1, i) => {
                    return (
                        <ChildComponent key={val1} val1={val1} ref={els.current[i]} />
                    );
                })
            }
        </div>
    );
};

export default Component;

```
`els.current`には`[[div,div,div], [div,div,div], [div,div,div]]`みたいな感じで配列の配列で`div`要素が格納される。