중첩라우팅, Suspense, lazy Loading을 각각의 컴포넌트별로 적용가능하다.
children으로 todoRouter를 정의함.
이런식으로 라우팅을 정의하면 => 페이지가 많아졌을때 라우터 설정하는게 효과적임
//root.js
import { Suspense, lazy } from "react";
import todoRouter from "./todoRouter";
const { createBrowserRouter } = require("react-router-dom");
const Loading = <div>Loading....</div>
const About = lazy(() => import("../pages/AboutPage"))
const Main = lazy(() => import("../pages/MainPage"))
const TodoIndex = lazy(() => import("../pages/todo/IndexPage"))
const TodoList = lazy(() => import("../pages/todo/ListPage"))
const root = createBrowserRouter([
{
path: "",
element: <Suspense fallback={Loading}><Main/></Suspense>
},
{
path: "about",
element: <Suspense fallback={Loading}><About/></Suspense>
},
{
path: "todo",
element: <Suspense fallback={Loading}><TodoIndex/></Suspense>,
children: todoRouter()
}
])
export default root;
//todoRouter
import { Suspense, lazy } from "react";
import { Navigate } from "react-router-dom";
const todoRouter = () => {
const Loading = <div>Loading....</div>
const TodoList = lazy(() => import("../pages/todo/ListPage"));
const TodoRead = lazy(() => import("../pages/todo/ReadPage"));
const TodoModify = lazy(() => import("../pages/todo/ModifyPage"));
const TodoAdd = lazy(() => import("../pages/todo/AddPage"));
return [
{
path: "list",
element: <Suspense fallback={Loading}><TodoList /></Suspense>
},
{
path: "read/:tno",
element: <Suspense fallback={Loading}><TodoRead /></Suspense>
},
{
path: "modify/:tno",
element: <Suspense fallback={Loading}><TodoModify /></Suspense>
},
{
path: "add",
element: <Suspense fallback={Loading}><TodoAdd /></Suspense>
},
{
path: "",
element: <Navigate replace to="/todo/list" />
},
]
}
export default todoRouter;
//app.js
function App() {
return (
<RouterProvider router={root}></RouterProvider>
);
}
URL 전체를 이동할 때 => useNavigate
parameter를 바꿀때 => useSearchParams(리렌더링됨)
const [qureyParams, setQueryParams] = useSearchParams();
page가 변하지 않을때도 최신데이터를 받아오는 방법
refresh라는 state를 deps에 넣어서 관리함.
ListComponent의 key값을 api호출시마다 변경 => 항상 리렌더링 발생함 (근데 좋은 방법은 아닌듯?)
const ListComponent = ({ page, size, refresh }) => {
useEffect(() => {
getList({ page, size }).then(data => console.log(data));
}, [page, size, refresh]); //page번호 바꾸면 최신데이터 받아옴.
// page가 변하지 않는다면? 실시간으로 데이터가 안변함.
return (<>
<div>Todo List Component {page} ---- {size}</div>
</>);
}
<ListComponent page={page} size={size} key={key}></ListComponent>
JS 클로저를 이용한 패턴
함수가 끝나고 나면 메모리 공간 초기화(지역변수)
=> 함수는 상태를 유지못함.
숨겨진 참조라는 뜻!!
import { useState, useCallback } from 'react';
function useInputs(initialForm) {
const [form, setForm] = useState(initialForm);
// change
const onChange = useCallback(e => {
const { name, value } = e.target;
setForm(form => ({ ...form, [name]: value }));
}, []);
const reset = useCallback(() => setForm(initialForm), [initialForm]);
return [form, onChange, reset];
}
export default useInputs;
이 코드에서 onChange라는 함수는 form, setform을 참조
return시 form, onChange, reset만 return, setform을 외부에서 사용 할 수 없는 클로저형태임.
라우터쪽에서 잡는경우 => Error Boundary를 많이 씀
보통 세세한 에러잡을때는 catch절에서 잡음(이렇게 처리하는것도 나쁜 방법은 아님)
비동기 처리할때 문제가 많이 되는데 => react Query사용해서 처리함
useQuery key에서 querykey시 배열에 여러가지 값을 줄 수 있음.
isFetching
isFetching은 어떠한 react-query요청 내부의 비동기 함수가 처리되었는지 여부 에 따라 true/false로 나누어 짐.
그럼 isLoading은?
isLoading은 캐시된 데이터조차 없이, 처음 실행된 쿼리 일 때 로딩 여부에 따라 true/false로 나누어 진다.
즉, 결론적으로 isLoading과 isFetching은 비슷하게 '로딩' 이라는 개념을 사용하지만 기존에 캐시된 데이터가 있느냐 에 따라 다르다.
실시간성이 중요한 sns같은경우 refresh를 key로 주어서 사용가능(실시간 데이터 받아오는 형식) => 어떤 페이지냐에 따라 다르겠네요.
정적데이터같은 경우 => page, size를 이용해 캐싱가능 => api호출을 줄인다.
100% Real Time이 필요없을때 호출을 최소화 하고 싶으면 React Query사용이 맞음
Real Time의 성격이 강할때 Web Socket(ex) 채팅)
const { isLoading, isFetching, data } =
useQuery(["todo/list", { page, size, refresh}], () => getList({ page, size }), { staleTime: 1000 * 5 });