목표
- Ref로 Dom을 다루어 본다.
- Form을 다루어 본다.
- 에러 경계(Error Boundaries)에 대해 알아본다.
🤔 의문
- 로그인 페이지가 있다고 가정했을 때, 로그인 페이지가 렌더링되고 그 후에 input값에 focus를 주고 싶을 때는 어떻게 해야할까?
<body>
<input id="input" />
<script>
document.getElementById("input").focus();
</script>
</body>
<script type="text/babel">
const rootElement = document.getElementById("root");
const App = () => {
const inputRef = React.useRef();
React.useEffect(() => {
inputRef.current.focus();
}, []);
return (
<>
<input ref={inputRef} />
</>
);
};
ReactDOM.render(<App />, rootElement);
</script>
💡 주의
- useRef를 사용할 때 주의해야 할 점이 있는데, 바로 current 속성을 사용해야 한다. 이는 우리가 선택하고자 하는 DOM을 가리킨다고 보면 된다.
🤔 의문
- document 객체로 접근하면 간단하게 접근할 수 있는데 리액트에서는 굳이 useRef를 제공하는 이유는 무엇일까?
그 이유는 효율성 때문이다.
document 객체를 사용해서 DOM에 직접적으로 도달하게 되면 비효율적인 상황이 발생할 수 있다.
리액트에서는 컴포넌트의 상태 안에서 특정 변수로 라이프 사이클과는 독립적이고 최적화된 상태로 사용하고 싶은 저장 공간을 제공한다.
<form>
<label for="fname">First name:</label><br>
<input type="text" id="fname" name="fname"><br>
<label for="lname">Last name:</label><br>
<input type="text" id="lname" name="lname"><br><br>
<input type="submit" value="Submit">
</form>
const App = () => {
return (
<>
<form>
<label for="fname">First name:</label>
<br />
<input type="text" id="fname" name="fname" value="John" />
<br />
<label for="lname">Last name:</label>
<br />
<input type="text" id="lname" name="lname" value="Doe" />
<br />
<br />
<input type="submit" value="Submit" />
</form>
</>
);
};
💡 주의
- 리액트에서 br태그나 input 태그 처럼 닫는 태그를 넣어주는 것이 좋은데 그 이유는 엘리먼트 요소에 닫는 태그가 없으면 인식하지 못하기 때문이다.
💡 주의
- 리액트에서는 label 태그에서 for대신 htmlFor를 쓰는 것을 권장하고 있다.
- for는 리액트가 이해하지 못하거나 혼동될 수 있는 키워드이기 때문이다.
💡 주의
- 또한, 리액트에서는 input 태그에서 value 프로퍼티를 사용할 때 onChange 함수와 같이 쓰거나 또는 value 대신 defalutValue 프로퍼티를 이용하라고 권장하고 있다.
주의사항들을 처리하면 다음과 같다.
const App = () => {
return (
<>
<form>
<label htmlFor="fname">First name:</label>
<br />
<input type="text" id="fname" name="fname" defaultValue="John" />
<br />
<label htmlFor="lname">Last name:</label>
<br />
<input type="text" id="lname" name="lname" defaultValue="Doe" />
<br />
<br />
<input type="submit" value="Submit" />
</form>
</>
);
};
이제, submit 버튼을 눌렀을 때 onSubmit을 이용해 alert창으로 First name과 Last name을 띄우려고 한다.
그 전에 한 가지 처리해야할 부분이 있는데 기본적으로 form 태그는 submit 버튼을 누르면 action을 취하게 되고 새로고침이 된다.
🤔 의문
- 그렇다면 어떻게 새로고침되는 것을 막을 수 있을까?
방법은 onSubmit에서 event의 기본적인 동작들을 막는 것이다. 아래 코드와 같이 파라미터로 들어오는 event 인자를 이용하면 된다. event 객체의 preventDefault() 함수를 이용하면 form에 실행되는 이벤트들을 모두 막을 수 있다.
const onSubmit = (event) => {
event.preventDefault();
};
const App = () => {
return (
<>
<form onSubmit={onSubmit}>
<label htmlFor="fname">First name:</label>
<br />
<input type="text" id="fname" name="fname" defaultValue="John" />
<br />
<label htmlFor="lname">Last name:</label>
<br />
<input type="text" id="lname" name="lname" defaultValue="Doe" />
<br />
<br />
<input type="submit" value="Submit" />
</form>
</>
);
};
🤔 의문
- 이제 alert창만 띄우는 일만 남았는데 input에 있는 value값을 어떻게 접근해야 할까?
const onSubmit = (event) => {
event.preventDefault();
console.log(event.target);
};
콘솔로 event.target 을 찍어보면 다음과 같이 태그가 출력된다.
참고로 태그 대신 객체의 속성을 출력해보고 싶다면 console.log 대신 console.dir()을 이용하면 된다. 다음과 같이 객체의 속성이 출력된다.
그 중에서도 elements라는 속성이 있는데 이를 통해 input값에 있는 value들을 접근할 수 있다.
const onSubmit = (event) => {
event.preventDefault();
alert(`
firstName: ${event.target.elements.fname.value},
lastName: ${event.target.elements.lname.value}
`);
};
요즘에는 React Hook 때문에 함수형 컴포넌트가 대부분이다. 하지만, 아직 Hook으로 지원되지 않는 몇 가지 개념들이 있는데 그 중에 하나가 에러 경계(Error Boundaries)이다. 따라서, 에러 경계는 클래스형 컴포넌트로 코드를 구성해야 한다.
아래의 코드는 부모(App)와 자식(Child) 컴포넌트 그리고 클래스로 구성된 ErrorBoundary 컴포넌트에 대한 코드이다.
class ErrorBoundary extends React.Component {
state = { error: null };
static getDerivedStateFromError(error) {
return { error };
}
render() {
const { error } = this.state;
if (error) {
return <p>There is Something Wrong.</p>;
}
return this.props.children;
}
}
const Child = () => {
throw new Error();
return (
<>
<p>Child</p>
</>
);
};
const App = () => {
return (
<>
<p>App</p>
<ErrorBoundary>
<Child />
</ErrorBoundary>
</>
);
};
🤔 의문
- ErrorBoundary 컴포넌트 마다 다르게 리턴해주고 싶다면 어떻게 해야할까?
props로 컴포넌트를 넘겨주어 custom화 할 수 있다.
class ErrorBoundary extends React.Component {
state = { error: null };
static getDerivedStateFromError(error) {
return { error };
}
render() {
const { error } = this.state;
if (error) {
return <this.props.fallback />;
}
return this.props.children;
}
}
const Child1Fallback = (error) => {
console.log(error);
return <p>Child1 is Something Wrong</p>;
};
const Child2Fallback = (error) => {
console.log(error);
return <p>Child2 is Something Wrong</p>;
};
const App = () => {
return (
<>
<p>App</p>
<ErrorBoundary fallback={Child1Fallback}>
<Child1 />
</ErrorBoundary>
<ErrorBoundary fallback={Child2Fallback}>
<Child2 />
</ErrorBoundary>
</>
);
};