React Loss Of Focus

Hansu Park·2024년 2월 11일
0
post-custom-banner

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

post-custom-banner

0개의 댓글