forwardRef は、θ¦ͺγ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ«ε―Ύγ—γ¦ DOM γƒŽγƒΌγƒ‰γ‚’ ref γ¨γ—γ¦ε…¬ι–‹γ§γγ‚‹γ‚ˆγ†γ«γ—γΎγ™γ€‚

const SomeComponent = forwardRef(render)

γƒͺフゑレンス

forwardRef(render)

forwardRef() γ‚’ε‘Όγ³ε‡Ίγ™γ“γ¨γ§γ€γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγŒ ref γ‚’ε—γ‘ε–γ£γ¦γγ‚Œγ‚’ε­γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ«θ»’ι€ (forward) γ§γγ‚‹γ‚ˆγ†γ«γͺγ‚ŠγΎγ™γ€‚

import { forwardRef } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
// ...
});

さらに例を見る

εΌ•ζ•°

  • render: γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΏ½?レンダー閒数です。React はこ�?ι–’ζ•°γ‚’θ¦ͺγ‹γ‚‰ε—γ‘ε–γ£γŸ props γŠγ‚ˆγ³ ref とともに呼び出します。返す JSX γŒγ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΏ½?ε‡ΊεŠ›γ¨γͺγ‚ŠγΎγ™γ€‚

θΏ”γ‚Šε€€

forwardRef は JSX でレンダーできる React γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ‚’θΏ”γ—γΎγ™γ€‚γƒ—γƒ¬γƒΌγƒ³γͺ閒数として�?οΏ½ηΎ©γ•γ‚ŒγŸ React γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ¨γ―η•°γͺγ‚Šγ€forwardRef γ«γ‚ˆγ£γ¦θΏ”γ•γ‚Œγ‚‹γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ― ref ε±žζ€§γ‚’ε—γ‘ε–γ‚‹γ“γ¨γ‚‚γ§γγΎγ™γ€‚

注意点


render ι–’ζ•°

forwardRef γ―εΌ•ζ•°γ¨γ—γ¦γƒ¬γƒ³γƒ€γƒΌι–’ζ•°γ‚’ε—γ‘ε–γ‚ŠγΎγ™γ€‚React はこ�?ι–’ζ•°γ‚’ props γŠγ‚ˆγ³ ref とともに呼び出します。

const MyInput = forwardRef(function MyInput(props, ref) {
return (
<label>
{props.label}
<input ref={ref} />
</label>
);
});

εΌ•ζ•°

  • props: θ¦ͺγ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ‹γ‚‰ζΈ‘γ•γ‚ŒγŸ props です。

  • ref: θ¦ͺγ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ‹γ‚‰ζΈ‘γ•γ‚ŒγŸ ref ε±žζ€§γ§γ™γ€‚ref はγ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆοΏ½?ε ΄εˆγ¨ι–’ζ•°οΏ½?ε ΄εˆγŒγ‚γ‚ŠγΎγ™γ€‚θ¦ͺγ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγŒ ref を渑していγͺγ„ε ΄εˆγ― null にγͺγ‚ŠγΎγ™γ€‚ε—γ‘ε–γ£γŸ ref は、εˆ₯οΏ½?γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ«ζΈ‘γ™γ‹γ€useImperativeHandle に渑します。

θΏ”γ‚Šε€€

forwardRef は JSX でレンダーできる React γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ‚’θΏ”γ—γΎγ™γ€‚γƒ—γƒ¬γƒΌγƒ³γͺ閒数として�?οΏ½ηΎ©γ•γ‚ŒγŸ React γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ¨γ―η•°γͺγ‚Šγ€forwardRef γ«γ‚ˆγ£γ¦θΏ”γ•γ‚Œγ‚‹γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ― ref ε±žζ€§γ‚’ε—γ‘ε–γ‚‹γ“γ¨γŒγ§γγΎγ™γ€‚


使用法

θ¦ͺγ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ« DOM γƒŽγƒΌγƒ‰γ‚’ε…¬ι–‹γ™γ‚‹

γƒ‡γƒ•γ‚©γƒ«γƒˆγ§γ―γ€ε„γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆε†…οΏ½? DOM γƒŽγƒΌγƒ‰γ―γƒ—γƒ©γ‚€γƒ™γƒΌγƒˆγ§γ™γ€‚γ—γ‹γ—γ€ζ™‚γ«γ―θ¦ͺに DOM γƒŽγƒΌγƒ‰γ‚’ε…¬ι–‹γ™γ‚‹γ“γ¨γŒζœ‰η”¨γͺε ΄εˆγŒγ‚γ‚ŠγΎγ™γ€‚δΎ‹γˆγ°γ€γƒŽγƒΌγƒ‰γ«γƒ•γ‚©γƒΌγ‚«γ‚Ήγ‚’ε½“γ¦γ‚‹γ“γ¨γ‚’θ¨±ε―γ—γŸγ„ε ΄εˆγ§γ™γ€‚γ“γ‚Œγ‚’ζ˜Žη€Ίηš„γ«θ¨±ε―γ™γ‚‹γŸγ‚γ«γ€γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΏ½?οΏ½ηΎ©γ‚’ forwardRef() でラップします。

import { forwardRef } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
const { label, ...otherProps } = props;
return (
<label>
{label}
<input {...otherProps} />
</label>
);
});

props οΏ½?後�?第 2 引数として ref γŒζΈ‘γ•γ‚ŒγΎγ™γ€‚ε…¬ι–‹γ—γŸγ„ DOM γƒŽγƒΌγƒ‰γ«γγ‚Œγ‚’ζΈ‘γ—γ¦γγ γ•γ„γ€‚

import { forwardRef } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
const { label, ...otherProps } = props;
return (
<label>
{label}
<input {...otherProps} ref={ref} />
</label>
);
});

γ“γ‚Œγ§γ€θ¦ͺοΏ½? Form γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγŒγ€MyInput γ«γ‚ˆγ£γ¦ε…¬ι–‹γ•γ‚ŒγŸ <input> DOM γƒŽγƒΌγƒ‰γ«γ‚’γ‚―γ‚»γ‚Ήγ§γγ‚‹γ‚ˆγ†γ«γͺγ‚ŠγΎγ™γ€‚

function Form() {
const ref = useRef(null);

function handleClick() {
ref.current.focus();
}

return (
<form>
<MyInput label="Enter your name:" ref={ref} />
<button type="button" onClick={handleClick}>
Edit
</button>
</form>
);
}

こ�? Form γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ― MyInput に ref を渑しています。MyInput γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ―γοΏ½? ref をブラウア�? <input> タグに軒送しています。そ�?η΅ζžœγ€Form γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ―γ“οΏ½? <input> DOM γƒŽγƒΌγƒ‰γ«γ‚’γ‚―γ‚»γ‚Ήγ—γ€focus() γ‚’ε‘Όγ³ε‡Ίγ™γ“γ¨γŒγ§γγ‚‹γ‚ˆγ†γ«γͺγ‚ŠγΎγ™γ€‚

γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆε†…οΏ½? DOM γƒŽγƒΌγƒ‰γΈοΏ½? ref γ‚’ε…¬ι–‹γ™γ‚‹γ“γ¨γ§γ€εΎŒγ§γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΏ½?内部を倉更する�?γŒι›£γ—γγͺγ‚‹γ“γ¨γ«ζ³¨ζ„γ—γ¦γγ γ•γ„γ€‚ι€šεΈΈγ―γ€γƒœγ‚Ώγƒ³γ‚„γƒ†γ‚­γ‚Ήγƒˆε…₯εŠ›γƒ•γ‚£γƒΌγƒ«γƒ‰γͺど�?ε†εˆ©η”¨ε―θƒ½γͺδ½Žγƒ¬γƒ™γƒ«γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ‹γ‚‰γ― DOM γƒŽγƒΌγƒ‰οΏ½?ε…¬ι–‹γ‚’θ‘Œγ„γΎγ™γŒγ€γ‚’γƒγ‚ΏγƒΌγ‚„γ‚³γƒ‘γƒ³γƒˆοΏ½?γ‚ˆγ†γͺγ‚’γƒ—γƒͺケーションレベル�?γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ§γ―θ‘Œγ„γΎγ›γ‚“γ€‚

ref οΏ½?軒送�?δΎ‹

δΎ‹ 1/2:
γƒ†γ‚­γ‚Ήγƒˆε…₯εŠ›γƒ•γ‚£γƒΌγƒ«γƒ‰γ«γƒ•γ‚©γƒΌγ‚«γ‚Ή

γƒœγ‚Ώγƒ³γ‚’γ‚―γƒͺックすると、ε…₯εŠ›γƒ•γ‚£γƒΌγƒ«γƒ‰γ«γƒ•γ‚©γƒΌγ‚«γ‚ΉγŒε½“γ¦γ‚‰γ‚ŒγΎγ™γ€‚Form γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ― ref γ‚’οΏ½?οΏ½ηΎ©γ—γ€γγ‚Œγ‚’ MyInput γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ«ζΈ‘γ—γΎγ™γ€‚MyInput γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ―γοΏ½? ref をブラウア�? <input> γ«θ»’ι€γ—γΎγ™γ€‚γ“γ‚Œγ«γ‚ˆγ‚Šγ€Form γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ― <input> γ«γƒ•γ‚©γƒΌγ‚«γ‚Ήγ‚’ε½“γ¦γ‚‰γ‚Œγ‚‹γ‚ˆγ†γ«γͺγ‚ŠγΎγ™γ€‚

import { useRef } from 'react';
import MyInput from './MyInput.js';

export default function Form() {
  const ref = useRef(null);

  function handleClick() {
    ref.current.focus();
  }

  return (
    <form>
      <MyInput label="Enter your name:" ref={ref} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}


θ€‡ζ•°γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ‚’η΅Œη”±γ—γŸ ref οΏ½?軒送

ref γ‚’ DOM γƒŽγƒΌγƒ‰γ«θ»’ι€γ™γ‚‹δ»£γ‚γ‚Šγ«γ€η‹¬θ‡ͺγ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ§γ‚γ‚‹ MyInput に軒送することもできます。

const FormField = forwardRef(function FormField(props, ref) {
// ...
return (
<>
<MyInput ref={ref} />
...
</>
);
});

さらにそ�? MyInput γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγŒθ‡ͺθΊ«οΏ½? <input> に ref γ‚’θ»’ι€γ™γ‚Œγ°γ€FormField へ�? ref はそ�? <input> へ�?参照を受け取ることにγͺγ‚ŠγΎγ™γ€‚

function Form() {
const ref = useRef(null);

function handleClick() {
ref.current.focus();
}

return (
<form>
<FormField label="Enter your name:" ref={ref} isRequired={true} />
<button type="button" onClick={handleClick}>
Edit
</button>
</form>
);
}

Form γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ― ref γ‚’οΏ½?οΏ½ηΎ©γ—γ€γγ‚Œγ‚’ FormField に渑しています。FormField γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ―γοΏ½? ref γ‚’ MyInput に軒送し、MyInput γ―γγ‚Œγ‚’γƒ–γƒ©γ‚¦γ‚ΆοΏ½? <input> DOM γƒŽγƒΌγƒ‰γ«θ»’ι€γ—γ¦γ„γΎγ™γ€‚γ“γ‚Œγ§ Form が DOM γƒŽγƒΌγƒ‰γ«γ‚’γ‚―γ‚»γ‚Ήγ§γγ‚‹γ‚ˆγ†γ«γͺγ‚ŠγΎγ™γ€‚

import { useRef } from 'react';
import FormField from './FormField.js';

export default function Form() {
  const ref = useRef(null);

  function handleClick() {
    ref.current.focus();
  }

  return (
    <form>
      <FormField label="Enter your name:" ref={ref} isRequired={true} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}


DOM γƒŽγƒΌγƒ‰οΏ½?δ»£γ‚γ‚Šγ«ε‘½δ»€εž‹γƒγƒ³γƒ‰γƒ«γ‚’ε…¬ι–‹γ™γ‚‹

DOM γƒŽγƒΌγƒ‰γ‚’γΎγ‚‹γ”γ¨ε…¬ι–‹γ™γ‚‹δ»£γ‚γ‚Šγ«γ€δ½Ώη”¨γ§γγ‚‹γƒ‘γ‚½γƒƒγƒ‰γ‚’εˆΆι™γ—γŸγ‚«γ‚Ήγ‚Ώγƒ γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ§γ‚γ‚‹γ€ε‘½δ»€εž‹γƒγƒ³γƒ‰γƒ« (imperative handle) γ‚’ε…¬ι–‹γ™γ‚‹γ“γ¨γŒγ§γγΎγ™γ€‚γ“γ‚Œγ‚’θ‘Œγ†γ«γ―γ€DOM γƒŽγƒΌγƒ‰γ‚’δΏζŒγ™γ‚‹γŸγ‚οΏ½?εˆ₯οΏ½? ref γ‚’οΏ½?�義します。

const MyInput = forwardRef(function MyInput(props, ref) {
const inputRef = useRef(null);

// ...

return <input {...props} ref={inputRef} />;
});

γγ—γ¦ε—γ‘ε–γ£γŸ ref γ‚’ useImperativeHandle に渑し、ref γ§ε…¬ι–‹γ—γŸγ„ε€€γ‚’ζŒ‡οΏ½?�します。

import { forwardRef, useRef, useImperativeHandle } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
const inputRef = useRef(null);

useImperativeHandle(ref, () => {
return {
focus() {
inputRef.current.focus();
},
scrollIntoView() {
inputRef.current.scrollIntoView();
},
};
}, []);

return <input {...props} ref={inputRef} />;
});

何らか�?γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγŒ MyInput へ�? ref を取得すると、DOM γƒŽγƒΌγƒ‰οΏ½?δ»£γ‚γ‚Šγ«γ‚γͺγŸγŒζ›Έγ„γŸ { focus, scrollIntoView } というγ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ‚’ε—γ‘ε–γ‚ŠγΎγ™γ€‚γ“γ‚Œγ«γ‚ˆγ‚Šγ€DOM γƒŽγƒΌγƒ‰γ«γ€γ„γ¦ε…¬ι–‹γ™γ‚‹ζƒ…ε ±γ‚’ζœ€ε°ι™γ«εˆΆι™γ™γ‚‹γ“γ¨γŒγ§γγΎγ™γ€‚

import { useRef } from 'react';
import MyInput from './MyInput.js';

export default function Form() {
  const ref = useRef(null);

  function handleClick() {
    ref.current.focus();
    // This won't work because the DOM node isn't exposed:
    // ref.current.style.opacity = 0.5;
  }

  return (
    <form>
      <MyInput placeholder="Enter your name" ref={ref} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}

ε‘½δ»€εž‹γƒγƒ³γƒ‰γƒ«οΏ½?使用に぀いて詳しくθͺ­γ‚€

落とし穴

ref οΏ½?過度γͺ使用に注意してください。ref は、props として葨現できγͺγ„γ€ε‘½δ»€εž‹οΏ½?ε‹•δ½œγ«οΏ½?γΏδ½Ώη”¨γ™γ‚‹γΉγγ§γ™γ€‚δΎ‹γˆγ°γ€γƒŽγƒΌγƒ‰γΈοΏ½?γ‚Ήγ‚―γƒ­γƒΌγƒ«γ€γƒŽγƒΌγƒ‰γΈοΏ½?フォーカス、をニパーション�?γƒˆγƒͺγ‚¬γ€γƒ†γ‚­γ‚ΉγƒˆοΏ½?選択γͺどです。

何かを props γ¨γ—γ¦θ‘¨ηΎγ§γγ‚‹ε ΄εˆγ―γ€ref γ‚’δ½Ώη”¨γ™γΉγγ§γ―γ‚γ‚ŠγΎγ›γ‚“γ€‚δΎ‹γˆγ°γ€Modal γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ‹γ‚‰ { open, close } οΏ½?γ‚ˆγ†γͺε‘½δ»€εž‹οΏ½?ハンドルを公開する�?ではγͺく、<Modal isOpen={isOpen} /> οΏ½?γ‚ˆγ†γ«γ€isOpen γ‚’ props γ¨γ—γ¦ε—γ‘ε–γ‚‹ζ–ΉγŒθ‰―γ„γ§γ—γ‚‡γ†γ€‚ε‘½δ»€εž‹οΏ½?ε‹•δ½œγ‚’ props γ¨γ—γ¦ε…¬ι–‹γ™γ‚‹ιš›γ«γ―γ‚¨γƒ•γ‚§γ‚―γƒˆγŒε½Ήη«‹γ‘γΎγ™γ€‚


γƒˆγƒ©γƒ–γƒ«γ‚·γƒ₯ーティング

γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ‚’ forwardRef でラップしている�?に、ref が常に null にγͺγ‚‹

γ“γ‚Œγ―ι€šεΈΈγ€ε—γ‘ε–γ£γŸ ref γ‚’οΏ½?οΏ½ιš›γ«δ½Ώη”¨γ™γ‚‹οΏ½?γ‚’εΏ˜γ‚Œγ¦γ„γ‚‹γ“γ¨γ‚’ζ„ε‘³γ—γΎγ™γ€‚

δΎ‹γˆγ°γ€γ“οΏ½?γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ― ref γ‚’ε…¨γδ½Ώη”¨γ—γ¦γ„γΎγ›γ‚“οΌš

const MyInput = forwardRef(function MyInput({ label }, ref) {
return (
<label>
{label}
<input />
</label>
);
});

οΏ½?正するにはこ�? ref を、DOM γƒŽγƒΌγƒ‰γ‹γ€ref を受けε…₯γ‚Œγ‚‹γ“γ¨γŒγ§γγ‚‹εˆ₯οΏ½?γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ«ζΈ‘γ—γΎγ™γ€‚

const MyInput = forwardRef(function MyInput({ label }, ref) {
return (
<label>
{label}
<input ref={ref} />
</label>
);
});

一部�?γƒ­γ‚Έγƒƒγ‚―γŒζ‘δ»Άδ»˜γγ§γ‚γ‚‹ε ΄εˆγ«γ‚‚γ€MyInput へ�? ref が null にγͺγ‚‹γ“γ¨γŒγ‚γ‚ŠγΎγ™γ€‚

const MyInput = forwardRef(function MyInput({ label, showInput }, ref) {
return (
<label>
{label}
{showInput && <input ref={ref} />}
</label>
);
});

showInput が false οΏ½?ε ΄εˆγ€ref はど�?γƒŽγƒΌγƒ‰γ«γ‚‚θ»’ι€γ•γ‚Œγͺγ„γŸγ‚γ€MyInput へ�? ref は空�?ままにγͺγ‚ŠγΎγ™γ€‚η‰Ήγ«γ€δ»₯δΈ‹οΏ½?δΎ‹οΏ½?γ‚ˆγ†γ«ζ‘δ»ΆγŒεˆ₯οΏ½?γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ€δΎ‹γˆγ° Panel οΏ½?δΈ­γ«ιš γ•γ‚Œγ¦γ„γ‚‹ε ΄εˆγ€γ“γ‚Œγ‚’θ¦‹θ½γ¨γ—γŒγ‘γ§γ™γ€‚

const MyInput = forwardRef(function MyInput({ label, showInput }, ref) {
return (
<label>
{label}
<Panel isExpanded={showInput}>
<input ref={ref} />
</Panel>
</label>
);
});