React의 개발 방식의 가장 큰 특징은 페이지 단위가 아닌 컴포넌트 단위로 시작한다는 점이다.
페이지를 만들기 이전에 컴포넌트를 찾아내 먼저 컴포넌트를 만들고
페이지를 조립해 나간다.
- 장점
테스트가 쉽고 확장성이 좋다.
하나의 컴포넌트는 한가지 일만 해야한다.
- 데이터는 위에서 아래로 흐른다. (하향식)
- 컴포넌트는 외부 컴포넌트에서 props를 이용해 데이터를 arguments나 attributes 처럼 전달 받을 수 있다.
- 컴포넌트는 props를 통해 전달받은 데이터가 어디서 왔는지 모른다.
- 데이터를 전달하는 주체가 부모 컴포넌트가 된다.
state(상태)를 두기 위해 변하는 값과 변하지 않는 값을 찾는다.
어떤 데이터를 state(상태)로 두면 안되는 가?
- 부모로부터 props를 통해 전달되는 데이터
- 시간이 지나도 변하지 않는 데이터
- 컴포넌트 안의 다른 state나 props를 가지고 계산이 가능한 데이터
두 컴포넌트가 하나의 상태에 영향을 받는다면?
공통 소유 컴포넌트(공통 부모 컴포넌트)에 상태를 위치시킨다.
부모 컴포넌트에 있는 state가 하위 컴포넌트에 의해 변해야 하는 경우 Lifting state up로 해결한다.
상태를 변경시키는 함수(handler)를 하위 컴포넌트에 props로 전달한다.
예시
import React, { useState } from "react";
export default function ParentComponent() {
const [value, setValue] = useState("초기값");
const handleChangeValue = () => {
setValue("바뀐 값");
};
return (
<div>
<div>부모컴포넌트의 상태는 {value} 입니다</div>
<ChildComponent handleButtonClick={handleChangeValue}/>
</div>
);
}
function ChildComponent({handleButtonClick}) {
const handleClick = () => {
handleButtonClick()
};
return <button onClick={handleClick}>상태 변경버튼</button>;
}
부모 컴포넌트의 state 값은 value에 담겨있고
handleChangeValue는 부모 컴포넌트의 state 값을 변경시키는 함수이다.
ParentComponent 함수의 리턴값을 보면
handleButtonClick 이라는 인자로 handleChangeValue를 자식 컴포넌트에 넘겨주고 있다.
함수 내에서 어떤 구현이 함수 외부에 영향을 끼치는 경우 해당 함수는 Side Effect가 있다고 이야기 한다.
let result = 'good';
function unexpect (){
result = 'bad';
}
unexpect(); // Side Effect를 발생시키고 있다.
오직 함수의 입력만이 함수의 결과에 영향을 주는 함수이다. Side Effect가 없다.
함수의 입력이 아닌 다른 값이 함수의 결과에 영향을 미치는 경우 순수함수라 부를 수 없다.
항상 똑같은 리턴값을 보장하므로 예측 가능한 함수이기도 하다.
function upper(str) {
return str.toUpperCase(); // toUpperCase 메소드는 원본을 수정하지 않는다.
}
upper('good') // 'GOOD'
upper 함수는 외부에 영향을 미치지 않고 결과 값을 예측할 수 있으므로 순수함수이다.
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
getRandomInt(1,10) // ?
Math.random()은 결과 값을 예측할 수 없으므로 순수함수가 아니다.
function getFlight(filterBy = {}) {
let endpoint = `...생략`
return fetch(endpoint)
.then(res => res.json())
fetch API의 응답값을 예측할 수 없기때문에 getFlight 함수는 순수함수가 아니다.
React의 함수 컴포넌트는, props가 입력으로, JSX Element가 출력으로 나간다. 여기에는 Side Effect가 없으며, 순수 함수로 작동한다.
컴포넌트 내에서 Side Effect를 실행할 수 있게하는 Hook이다.
Hook
React 16.8에 새로 추가된 기능으로 class를 작성하지 않고도 state와 다른 React의 기능들을 사용할 수 있도록 해준다.
- 반복문,조건문 등 중첩된 함수내에서 사용할수 없으며 최상위에서만 Hook을 호출해야 한다.
- React 함수 컴포넌트에서 Hook을 호출해야 한다.
useEffect(함수, 배열)
useEffect의 첫번째 인자는 함수이다. 해당 함수 안에서 side effect를 실행한다.
useEffect의 두번째 인자는 배열이다. '해당 배열에 들어간 값이 변경될때'의 조건을 담는 배열이다.
useEffect(함수)
useEffect(함수, [])
외부 API를 리소스로 받아오고 더이상 API 호출이 필요하지 않을 때 사용할 수 있다.
처음 단 한번, 전체 목록 데이터를 불러오고 검색어로 filter 하는 식
// ...생략
export default function App() {
const [proverbs, setProverbs] = useState([]);
const [filter, setFilter] = useState("");
useEffect(() => {
console.log("effect 함수 실행됨");
const result = getProverbs();
setProverbs(result);
}, []);
const handleChange = (e) => {
setFilter(e.target.value);
};
return (
//...생략...
);
}
HTTP 요청의 빈도를 줄일 수 있다
브라우저(클라이언트)의 메모리 상에 많은 데이터를 갖게 되므로, 클라이언트의 부담이 늘어난다
검색어가 바뀔 때마다 컴포넌트 외부로 API 호출, 필터링 한 결과를 받아오는 식
// ...생략
export default function App() {
const [proverbs, setProverbs] = useState([]);
const [filter, setFilter] = useState("");
const [count, setCount] = useState(0);
useEffect(() => {
console.log("effect 함수 실행됨");
const result = getProverbs(filter);
setProverbs(result);
}, [filter]);
const handleChange = (e) => {
setFilter(e.target.value);
};
const handleCounterClick = () => {
setCount(count + 1);
};
return (
//...생략...
);
}
클라이언트가 필터링 구현을 생각하지 않아도 된다
빈번한 HTTP 요청이 일어나게 되며, 서버가 필터링을 처리하므로 서버가 부담을 가져간다