Nie wspominałem o tym, bo wydawało mi się to oczywiste, ale tak jak propsy możemy przekazywać w dół w jedynym słusznym kierunku przez wiele komponentów, to samo możemy robić z forwardRef.
Oto komponent najniższy:
import { forwardRef } from 'react';
const MyInput = forwardRef((props, ref) => {
const { label, ...otherProps } = props;
return (
<label>
{label}
<input {...otherProps} ref={ref} />
</label>
);
});
export default MyInput;
Dlatego najniższy, bo jego forwardRef jest forwardowane do czegoś, co można najogólniej opisać jako obiekt klasy HTMLElement a nie RFC.
Teraz komponent wyżej, komponent pośrednik:
import { forwardRef, useState } from 'react';
import MyInput from './MyInput.js';
const FormField = forwardRef(function FormField({ label, isRequired }, ref) {
const [value, setValue] = useState('');
return (
<>
<MyInput
ref={ref}
label={label}
value={value}
onChange={e => setValue(e.target.value)}
/>
{(isRequired && value === '') &&
<i>Required</i>
}
</>
);
});
export default FormField;
Dlatego pośrednik, bo choć przypisuje ref do nieelementu RFC, który forwarduje to dalej, to sam żadnego ref nie tworzy tylko sam od rodzica przyjmuje i forwarduje.
Oto rodzic:
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>
);
}
Wydaje mi się oczywiste, co tu się dzieje. Widzieliśmy już, jak można propsy przekazywać z góry w dół, to samo mamy z refami. Może na początku trochę to podchwytliwe, ale bez przesady.
Może gdyby tak do tego przykładu wprowadzić useImperativeHandle dla komponentu najniższego, to zrobiłoby się trudniej, ale przynajmniej ciekawiej, moglibyśmy zrozumieć już w pełni i pozbierać do kupy naszą wiedzę o refach.
Zostawiam to jako potencjalne zadanie domowe – robimy wielopoziomowe refy + useImperativeHandle.
Więcej Reacta niedługo…