MUI기반 개발을 하며 만났던 문제에 대해 정리해보려고 합니다.
텍스트를 작성할 시 텍스트필드가 가지고 있던 focus
를 잃어버리게 됩니다.
//App.js
function App() {
const [open, setOpen] = useState(false);
const [text, setText] = useState("");
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
const Body = ({text, setText}) => <div>
<Typography id="modal-modal-title" variant="h6" component="h2">
Text in a modal
</Typography>
<Typography id="modal-modal-description" sx={{mt: 2}}>
Duis mollis, est non commodo luctus, nisi erat porttitor ligula.
</Typography>
<TextField id="outlined-basic" label="Outlined" variant="outlined" defaultValue={text}
onChange={(e) => {
setText(e.target.value)
}}/></div>;
const content = <Body text={text} setText={setText}/>;
return (<div className="App">
<Button onClick={handleOpen}>Open modal</Button>
<BaseModal open={open} handleClose={handleClose} style={style} Body={content} />
</div>);
}
export default App;
//BaseModal.jsx
export default function BaseModal({open, handleClose, style, Body}) {
return <Modal
open={open}
onClose={handleClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box sx={style}>
{Body}
</Box>
</Modal>
}
(BaseModal에서) 텍스트를 입력할 때, (App.js에서 선언한) State
가 변경되고 이로 인해 텍스트필드가 포함된 Body
가 재생성되어 focus
를 잃어버리게 됩니다.
그렇다면 어떻게 해야 재생성을 피해 focus를 잃어버리지 않게 할 수 있을까요?
첫 번째 방법은 렌더링에서 벗어나는 것입니다.
//App.js
const Body = ({text, setText}) => <div>
<Typography id="modal-modal-title" variant="h6" component="h2">
Text in a modal
</Typography>
<Typography id="modal-modal-description" sx={{mt: 2}}>
Duis mollis, est non commodo luctus, nisi erat porttitor ligula.
</Typography>
<TextField id="outlined-basic" label="Outlined" variant="outlined" defaultValue={text}
onChange={(e) => {
setText(e.target.value)
}}/></div>;
function App() {
const [open, setOpen] = useState(false);
const [text, setText] = useState("");
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
const content = <Body text={text} setText={setText}/>;
return (<div className="App">
<Button onClick={handleOpen}>Open modal</Button>
<BaseModal open={open} handleClose={handleClose} style={style} Body={content} />
</div>);
}
export default App;
위 코드를 보시면, Body를 App()
밖으로 이동시켜 컴퍼넌트를 매번 생성하지 않도록 하였습니다. 따라서 변경사항만 리렌더링이 일어나 DOM이 가지고 있던 foucs
를 유지할 수 있습니다.
두 번째 방법은 컴퍼넌트 대신 엘리먼트를 넘겨주는 방식입니다.
//App.js
function App() {
const [open, setOpen] = useState(false);
const [text, setText] = useState("");
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
const Body = ({text, setText}) => <div>
<Typography id="modal-modal-title" variant="h6" component="h2">
Text in a modal
</Typography>
<Typography id="modal-modal-description" sx={{mt: 2}}>
Duis mollis, est non commodo luctus, nisi erat porttitor ligula.
</Typography>
<TextField id="outlined-basic" label="Outlined" variant="outlined" defaultValue={text}
onChange={(e) => {
setText(e.target.value)
}}/></div>;
const content = <Body text={text} setText={setText}/>;
return (<div className="App">
<Button onClick={handleOpen}>Open modal</Button>
<BaseModal open={open} handleClose={handleClose} style={style} Body={content} />
</div>);
}
export default App;
컴퍼넌트가 아니기 때문에 재생성되지 않고 변경사항의 리렌더링만 일어나 OM이 가지고 있던 foucs
를 유지할 수 있습니다.
재조정 (Reconciliation) – React
리액트의 렌더링은 어떻게 일어나는가?
javascript - React.js - input losing focus when rerendering - Stack Overflow
reactjs - Input is loosing focus on hooks update - Stack Overflow