React에서의 데이터 흐름
React 개발 방식의 가장 큰 특징은 컴포넌트 단위로 개발한다는 것이다. 앱의 프로토타입을 전달받은 경우 먼저 컴포넌트를 찾아야 한다. 컴포넌트를 만든 후 페이지를 조립해 나간다. → 상향식(bottom-up) 개발
⇒ 테스트가 쉽고 확장성이 좋다!
✅ 앱의 디자인을 전달받으면 컴포넌트 계층 구조로 나누는 것이 최우선!
React에서 데이터는 위에서 아래로 흐른다(Flux 패턴 ). 컴포넌트는 부모 컴포넌트로부터 props를 전달받을 수 있다. 즉 데이터의 흐름은 하향식(top-down)이다.
✅ React는 단방향 데이터 흐름(One-way data flow)을 따른다!
상태(state)의 조건
상태(state) 위치 정하기
상태가 특정 컴포넌트에만 유의미하다면 특정 컴포넌트에 위치하면 되지만, 하나의 상태에 여러 컴포넌트가 영향을 받는 경우 공통 부모 컴포넌트에 위시치켜야 한다.
역방향 데이터 흐름
부모 컴포넌트의 상태가 하위 컴포넌트에 의해 변하는 것
⇒ State 끌어 올리기(Lifting state up) : handler를 하위 컴포넌트에 props로 전달
Lifting State Up
상위 컴포넌트의 "상태를 변경하는 함수" 그 자체를 하위 컴포넌트로 전달하고, 이 함수를 하위 컴포넌트가 실행한다.
Effect Hook
Side Effect(부수 효과) : ****함수 내에서 어떤 구현이 함수 외부에 영향을 끼는 경우 해당 함수는 Side Effect가 있다.
React는 Side Effect를 다루기 위한 Hook인 Effect Hook을 제공한다.
useEffect
의 첫번째 인자는 함수로 해당 함수 내에서 side effect를 실행하면 된다.
새롭게 컴포넌트가 렌더링될 때 Effect Hook이 실행
ex) 브라우저 API를 이용해 타이틀 변경
import { useEffect, useState } from "react";
import "./styles.css";
export default function App() {
const proverbs = [
"좌절감으로 배움을 늦추지 마라",
"Stay hungry, Stay foolish",
"Memento Mori",
"Carpe diem",
"배움에는 끝이 없다"
];
const [idx, setIdx] = useState(0);
const handleClick = () => {
setIdx(idx === proverbs.length - 1 ? 0 : idx + 1);
};
return (
<div className="App">
<button onClick={handleClick}>명언 제조</button>
<Proverb saying={proverbs[idx]} />
</div>
);
}
function Proverb({ saying }) {
useEffect(() => {
document.title = saying;
});
return (
<div>
<h3>오늘의 명언</h3>
<div>{saying}</div>
</div>
);
}
조건부 effect(dependency array)
useEffect
의 두번째 인자는 배열로 조건을 담고 있다. 조건은 어떤 값의 변경이 일어 날 때를 의미한다. 배열에는 어떤 값이 들어간다. 이 배열을 종속성 배열이라고 부른다.
종속성 배열내의 종속성의 값이 변할 때 첫번째 인자의 함수가 실행된다.
import { useEffect, useState } from "react";
import "./styles.css";
import { getProverbs } from "./storageUtil";
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,count]);
const handleChange = (e) => {
setFilter(e.target.value);
};
const handleCounterClick = () => {
setCount(count + 1);
};
return (
<div className="App">
필터
<input type="text" value={filter} onChange={handleChange} />
<ul>
{proverbs.map((prvb, i) => (
<Proverb saying={prvb} key={i} />
))}
</ul>
<button onClick={handleCounterClick}>카운터 값: {count}</button>
</div>
);
}
function Proverb({ saying }) {
return <li>{saying}</li>;
}
✅ 두번째 인자로 빈 배열을 넣는 경우 컴포넌트가 처음 생성될 때만 effect함수가 실행된다.
⇒ 외부 API를 통해 리소스를 받아오고 더이상 API호출이 필요하지 않을 경우 사용
fetch API 사용한 AJAX 요청
useEffect(() => {
setIsLoading(true);
fetch(`http://서버주소/proverbs?q=${filter}`)
.then(resp => resp.json())
.then(result => {
setProverbs(result);
setIsLoading(false);
});
}, [filter]);
✅ 외부 API 접속이 느릴 경우를 고려하여, 로딩 화면(loading indicator)의 구현(fetch 요청의 전후로 setIsLoading
을 설정해주어 보다 나은 UX를 구현)