135
102

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Posted at

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

DOMにアクセス

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にグラフを描画する際に使用する。

子コンポーネントのインスタンスにアクセス

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)

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

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)

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要素が格納される。

135
102
1

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
135
102

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?