새로고침 시 state 등이 코드에 설정된 값으로 초기화된다.
Local Storage
Session Storage
Local Storage에 데이터 저장 및 가져오기, 삭제
localStorage.setItem('age', '20');
localStorage.getItem('age');
localStorage.removeItem('age');
Session Storage도 위와 같이 앞의 이름만 바꾸면 된다.
object/array 자료는 저장이 불가능하고 문자만 가능하다.
다음과 같이 하면, 저장이 제대로 되지 않는다.
function App() {
// Local Storage에 저장해보기
let obj = {name: 'kim'};
localStorage.setItem('data', obj);
function App() {
// Local Storage에 저장해보기
let obj = {name: 'kim'};
localStorage.setItem('data', JSON.stringify(obj));
function App() {
// Local Storage에 저장해보기
let obj = {name: 'kim'};
localStorage.setItem('data', JSON.stringify(obj));
let temp = localStorage.getItem('data');
console.log(JSON.parse(temp));
console.log(JSON.parse(temp).name);
App.js
useEffect(() => {
let watched = Array();
localStorage.setItem('watched', JSON.stringify(watched));
}, [])
Detail.js
useEffect(() => {
let watched = JSON.parse(localStorage.getItem('watched'));
if (Object.values(watched).includes(detail.id) === false) {
watched.push(detail.id);
localStorage.setItem('watched', JSON.stringify(watched));
}
}, [])
혹은 다음과 같이 set 자료형으로 만들어서 할 수도 있다.
Detail.js
useEffect(() => {
let watched = JSON.parse(localStorage.getItem('watched'));
watched.push(detail.id);
watched = new Set(watched); // 중복 제거
watched = Array.from(watched);
localStorage.setItem('watched', JSON.stringify(watched));
})
App.js
useEffect(() => {
let watched = Array();
if (localStorage.getItem('watched') === null) {
localStorage.setItem('watched', JSON.stringify(watched));
}
}, [])
다음과 같은 경우에 React Query를 유용하게 사용할 수 있다.
실시간 SNS(코인 거래소 등) 사이트에서 잘 사용된다.
셋팅
npm install react-query
index.js
...
import { QueryClient, QueryClientProvider } from "react-query";
const queryClient = new QueryClient();
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<QueryClientProvider client={queryClient}>
<React.StrictMode>
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
</React.StrictMode>
</QueryClientProvider>
App.js
...
import { useQuery } from 'react-query';
...
useQuery를 쓸 때 장점
os 앞의 return과 {} 생략 가능
let result = useQuery('name', () => {
return axios.get('https://codingapple1.github.io/userdata.json')
.then((response) => {
return response.data;
})
})
result.data // axios GET 요청 후 성공 시 데이터를 가져온다.
result.isLoading // Load 중일 때(요청 중일 때) true가 된다.
result.error // axios GET 요청 후 실패 시 true가 된다.
<Nav className="ms-auto" style={{ color: 'white', fontWeight: 'bold'}}>
{result.data && result.data.name}
{result.isLoading && '로딩 중'}
{result.error && '에러'}
</Nav>
let result = useQuery('name', () => {
return axios.get('https://codingapple1.github.io/userdata.json')
.then((response) => {
console.log('요청됨');
return response.data;
})
}, {staleTime: 2000})
ajax 성공 결과를 5분간 들고 있는다.
사실 redux-toolkit을 설치하면 RTK Query가 자동적으로 설치된다. React Query랑 유사하나 문법적으로 더러운 부분이 있다.
브라우저 개발자 도구의 Components에서 컴포넌트들을 구조화해서 보여준다.
Profiler에서는 성능(렌더링하는 속도)을 측정해볼 수 있다.
Redux DevTools
lazy import
single page application으로, build 후 배포하게 되면 html, css, javascript 각각 하나의 파일로 뭉쳐진다.
이렇게 큰 javascript 파일을 잘게 분리해서 다운 받게 해주려면?
...
import { lazy, Suspense, createContext, useEffect, useState } from "react";
...
// import Detail from "./routes/Detail.js";
// import Cart from "./routes/Cart.js"
// lazy loading
const Detail = lazy(() => import('./routes/Detail'));
const Cart = lazy(() => import('./routes/Cart'));
위와 같이 lazy import가 필요한 곳을 변경한다.
그리고 다음과 같이 필요한 곳에 Suspense로 감싼다.
<Suspense fallback={<Loading></Loading>}>
<Detail shoes={shoes} />
</Suspense>
로딩 중일 경우 fallback의 컴포넌트들이 보이게 된다.
Routes 안의 컴포넌트 전체를 lazy import하고 Routes를 모두 Suspense로 감싸도 된다. 그렇게 되면 모든 컴포넌트들이 lazy import될 것이다. 이러면 처음 페이지 방문 시 속도가 빨라질 것 같다.
function Child() {
console.log('재렌더링됨');
return <div>Child</div>
}
function Cart() {
let user = useSelector((state) => state.user);
let dispatch = useDispatch();
let [count, setCount] = useState(0);
return (
<div>
<Child></Child>
<button onClick={() => { setCount(count + 1) }}>+</button>
memo
...
import { memo, useState } from 'react';
let Child = memo( function() {
console.log('재렌더링됨');
return (
<div>Child</div>
)
})
function Cart() {
let user = useSelector((state) => state.user);
let dispatch = useDispatch();
let [count, setCount] = useState(0);
return (
<div>
<Child></Child>
<button onClick={() => { setCount(count + 1) }}>+</button>
...
<Child count={count}></Child>
<button onClick={() => { setCount(count + 1) }}>+</button>
useMemo
function func() {
let result = 0;
for (let i = 0; i < 1000000000; i++) {
result += 1;
}
return result
}
function Cart() {
let user = useSelector((state) => state.user);
let dispatch = useDispatch();
let [count, setCount] = useState(0);
let result = useMemo(() => {
return func();
}, [user]);
let result2 = func();
// console.log(result);
console.log(result2);
return (
<div>
<Child count={count}></Child>
<button onClick={() => { setCount(count + 1) }}>+</button>
automatic batch
- state 변경 함수가 여러 개 이어져 있으면 각 함수들마다 재렌더링이 일어나는 것이 아니라, 마지막에 1번 재렌더링 된다.
ajax 요청, setTimeout 내부에서 작성된 state 변경 함수 실행은 react 17까지는 automatic batch가 일어나지 않았다.
하지만 react 18부터는 automatic batch을 ajax 요청 및 setTimeout 내부에서도 지원하게 되었다.
useTransition