document.addEventListener('DOMContentLoaded', () => {
const input1 = document.getElementById('input1');
const input2 = document.getElementById('input2');
input1.addEventListener('input', () => {
if (input1.value.length === input1.maxLength) {
input2.focus();
}
});
});
간단하다. 컴포넌트를 재사용할 때는, hook form의 useController를 사용해서 prop들을 넘겨준다.
react hook form에서는 setFocus라는 메소드를 제공하기 때문에 직접 돔에 접근해서 focus를 주지 않아도 쉽게 다음 focus로 이동하도록 구현 할 수 있다.
import React from 'react';
import { useForm, useController } from 'react-hook-form';
import { TextField, Button, Box } from '@mui/material';
const InputField = ({ name, control, maxLength, setFocus, nextField }) => {
const { field } = useController({ name, control });
const handleChange = (event) => {
field.onChange(event);
if (event.target.value.length >= maxLength && nextField) {
setFocus(nextField);
}
};
return (
<TextField
{...field}
variant="outlined"
inputProps={{ maxLength }}
onChange={handleChange}
/>
);
};
function App() {
const { control, handleSubmit, setFocus } = useForm();
const onSubmit = (data) => console.log(data);
return (
<Box component="form" onSubmit={handleSubmit(onSubmit)} noValidate autoComplete="off">
<InputField
name="input1"
control={control}
maxLength={5}
setFocus={setFocus}
nextField="input2"
/>
<InputField
name="input2"
control={control}
maxLength={5}
setFocus={setFocus}
nextField={null} // 마지막 입력창이므로 null
/>
<Button type="submit" variant="contained" color="primary">
Submit
</Button>
</Box>
);
}
export default App;
이때, controller prop을 커스텀에서 사용할때, field의 ref값이 존재한다.
결국 focus가 되는 이동하는 원리는 ref를 통해 직접 돔에 접근해서 focus를 바꾸는 것이기 때문에, ref값을 hook form이 제어하도록 전달해줘야한다.
material UI같은 경우 ref와 inputRef라는 속성을 분리해서 사용한다.
ref) https://mui.com/material-ui/api/input-base/#input-base-prop-inputProps
The ref is forwarded to the root element.
inputRef는 material UI안에 있는 input태그에 ref를 전달하는 prop이다. 그래서 ref로 전달하는 방식이 아닌, inputRef라는 prop을 통해 전달해야 한다.
만약 컴포넌트에 ref를 prop으로 전달하고 싶다면 ? react의 forwardRef와 useImperativeHandle를 사용해 처리해야한다.
import React, { forwardRef, useImperativeHandle, useRef } from 'react';
const ChildComponent = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focusInput: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} type="text" />;
});
export default ChildComponent;
import React, { useRef } from 'react';
import ChildComponent from './ChildComponent';
function ParentComponent() {
const childRef = useRef();
const handleFocus = () => {
if (childRef.current) {
childRef.current.focusInput();
}
};
return (
<div>
<ChildComponent ref={childRef} />
<button onClick={handleFocus}>Focus on Input</button>
</div>
);
}
export default ParentComponent;