리액트의 특징
- 컴포넌트로 반복되는 부분을 하나의 구성요소로 만들어 코드를 재사용할 수 있다.
state
를 사용할 수 있다.virtual dom
: 페이지 로드 시 연산이 줄어들고 리소스를 재활용하기 좋다. (처음에는 로드하느라 조금 느릴 수 있지만 그다음부터는 페이지 전환이 빠름)- 단방향 데이터 바인딩
단방향 데이터 바인딩
데이터 바인딩 : 화면에 보이는 데이터와 브라우저 메모리에 있는 데이터를 일치시키는 것
양방향 데이터 바인딩의 경우, 데이터의 변화를 감시하는 watcher
가 두개라 컨트롤러(자바스크립트)와 뷰(HTML)에서 데이터가 변경될 수 있다.
단방향 데이터 바인딩의 경우 템플릿과 모델(데이터)을 합쳐 뷰에 반영한다. 자바스크립트의 데이터가 HTML에 표현되므로
단방향 데이터 바인딩을 하면 데이터의 변화를 예측/감지하기 쉽고, 데이터를 추적하거나 디버깅하는 데 용이하다.
리액트의 라이프사이클 함수 (생애주기)
constructor()
componentWillMount()
render()
componentDidMount()
클래스 컴포넌트
- 옛날 문법
render()
안에서return
함수 컴포넌트
- 함수(function)를 기반으로 작성
- 클래스 컴포넌트의
componentDidMount
는 함수 컴포넌트의useEffect
로 대체 가능하다.
리액트 개발 시 주의할 점
- react 사용할땐 DOM 직접 조작을 피하자. 변경하려는 유일한 것은
state
여야 한다. - 리액트에서
prop
을 주고 받을때 필요한 컴포넌트가 여럿 넓게 펼쳐져 있는 경우, 주고 받기 복잡하고 유지보수 또한 어려워진다. 그럴땐 리액트 자체Context API
를 사용하면 좋다. 필요한 상태가 있는 컨텍스트를 따로 만들어두고 필요할 때 그때그때 값을 사용할 수 있다.
리액트 최적화 방법
useMemo
로 복잡한 연산이 필요한 함수는 캐싱. 함수의 디펜던시(파라미터)가 같을 경우 이전에 리턴했던 참조값을 재사용함.- 클래스 컴포넌트에서는
React.memo
로 메모이제이션 (useMemo
와 같음) useCallback
으로 함수 선언을 메모이제이션- 자식 컴포넌트에게
props
를 전달할 때, 새로 객체를 생성해 보내지 말고state
그대로 넘기고, 필요한 데이터 가공은 자식 컴포넌트 안에서 하자.props
값이 변경될 때마다 컴포넌트를 렌더링하므로state
만 전달해주고 자세한 연산은 자식 컴포넌트 안에서 하도록 하는게 좋다. map()
함수 사용시 컴포넌트의key
값으로 인덱스 값 넣지 않기. 배열 중간에 요소가 삽입 또는 수정, 삭제될 경우 그 이후의 요소들은 전부 인덱스가 변경되기 때문이다.
리액트 컴포넌트가 리렌더링하는 상황
- 전달받은 props가 변경될 때
- 자신의 state가 바뀔 때
- 부모 컴포넌트가 리렌더링될 때
- forceUpdate 함수가 실행될 때
컴포넌트 리렌더링 방지법
-
class 형 컴포넌트: shouldComponentUpdate라는 라이프사이클 메서드 사용
-
함수 컴포넌트: export 할 때 React.memo() 사용해 컴포넌트의 props가 바뀌지 않은 경우 리렌더링하지 않도록 설정
-
setter 함수에 새로운 상태를 파라미터로 넣지 말고 상태 업데이트('->')를 어떻게 할지 정의하는 업데이트 함수를 넣기
[업데이트 전]
[업데이트 후]
- useReducer 사용 App.js 내(& 컴포넌트 밖)에 상태 업데이트 로직을 모아둘 수 있음.
[상태 업데이트 로직]
useState 써보기
function App() {
const [text, setText] = useState('감추기');
// 1
const buttonClick = () => {
text==='감추기'? setText("보이기") : setText("감추기")
}
// 2
return (
<div className="App">
{text==='보이기' && <MainHeader text='hello'></MainHeader>}
<button onClick={buttonClick}>{text}</button>
</div>
);
//3
}
- useState(초기값), 리턴값은 배열로 [상태데이터, 상태 세터함수]
- 간결하게 쓴 if문이다!
- text==='보이기'에 and 연산을 함으로써 해당 조건에 맞으면
<MainHeader>
컴포넌트를 표시하도록 하였다.
변수 객체화
이름의 성(lastname), 이름(first name)과 같이 거의 따로 쓰는 게 없을 때, 위의 코드보단
객체화해서 아래처럼 쓰는게 좋다.
const [name, setName] = useState({
first: '하은',
last: '박'
});
useRef()
- input 관리할 때 유용한 함수다.
return (
<div className="App">
<h1>{name.last}{name.first}</h1>
<input name='성' placeholder='성'></input>
<input name='이름' placeholder='이름'></input>
<button onClick={confirm}>확인</button>
</div>
);
위와 같은 코드가 있다고 할때, input에 입력된 값을 저장하거나 받는 함수가 없다.
그럴 때 쓸 수 있는게 useRef() 함수다!
const firstNameRef = useRef()
const lastNameRef = useRef()
const confirm = () => {
// setName();
console.log(firstNameRef.current.value)
}
return (
<div className="App">
<h1>{name.last}{name.first}</h1>
<input name='성' placeholder='성' ref={lastNameRef}></input>
<input name='이름' placeholder='이름' ref={firstNameRef}></input>
<button onClick={confirm}>확인</button>
</div>
);
이런 식으로 <input>
태그 ref 속성에 useRef 함수를 넣어주면 그 안에 들어온 인풋값을 참조해올 수 있다.
confirm()함수에서 firstNameRef.current.value
를 콘솔 찍어보니 input으로 받은 값이 나온다!
이벤트 함수에서 e.target.value와 비슷한 원리인 듯 하다.
useRef를 사용해 인풋 값을 화면에 띄우는 코드
function App() {
const [name, setName] = useState({
first: '하은',
last: '박'
});
const firstNameRef = useRef()
const lastNameRef = useRef()
const confirm = () => {
setName({
first: firstNameRef.current.value,
last: lastNameRef.current.value
})
}
return (
<div className="App">
<h1>{name.last}{name.first}</h1>
<input name='성' placeholder='성' ref={lastNameRef}></input>
<input name='이름' placeholder='이름' ref={firstNameRef}></input>
<button onClick={confirm}>확인</button>
</div>
);
}
export default App;
useRef 함수로 참조해온 값을 setter 함수(setName)에서 바꿔주면 간단하게 완성된다!!