TIP -PACKAGE ❤️✅
~~~~~
✅Tạo ID generates - uuid npm
***REDUX❤️
Concept::
::Redux là gì
~ Là 1 state management tool
+ Tách state(data) ra khỏi app để quản lý, thêm xóa sửa và UI sẽ auto update data when change.
+ Trong redux state là 1 obj, là data
✅combineReducers(reducers)
~ Chia nhỏ chức năng, quản lý state riêng biệt
—————————————————————————————————————
***MUI❤️
Syntax::
sx={{
display: { md: 'none' },
visibility: { xs: 'hidden', md: 'visible' },
}}
DEMO::✅
import { createSlice } from '@reduxjs/toolkit';
import { useCallback } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { StateType } from './redux-store';
const initialState = {
list: [] as { id: number }[],
title: '',
};
const todoReducer = createSlice({
name: 'todo',
initialState,
reducers: {
_addTodo: (state, action) => {
state.list.unshift(action.payload);
},
_setTitle: (state, action) => {
state.title = action.payload;
},
},
});
const { reducer, actions } = todoReducer;
interface Return {
list: { id: number }[];
title: string;
addTodo: (data: string) => void;
setTitle: (title: string) => void;
}
const useTodo = (): Return => {
const { list, title } = useSelector((state: StateType) => state.todo);
const dispatch = useDispatch();
const { _addTodo, _setTitle } = actions;
const addTodo = useCallback((data: string) => {
dispatch(_addTodo(data));
}, []);
const setTitle = useCallback((title: string) => {
dispatch(_setTitle(title));
}, []);
return {
list,
title,
addTodo,
setTitle,
};
};
export { useTodo };
export default reducer;
—————————————————————————————————————
***React-query❤️
::Concept ❤️❤️❤️
→ Quản lý dữ liệu từ phía server.
*cacheTime: Thời gian data được lưu cache tồn tại. Nếu hết thời gian, giá trị của query tương ứng với key này sẽ là undefined.
*staleTime: Thời gian data trong cache được tính là mới, tức là nếu data query này trong cache được tính là mới thì khi gọi query sẽ không call queryFuntion để lấy dữ liệu cập nhật vào cache nữa. "Còn mới thì gọi api làm gì". Mặc định staleTime là 0, tức là cứ dùng query sẽ gọi đến queryFunction.
→ Giải thích: staleTime là thời gian của data trong cache vẫn được tính là mới(fesh), nếu hết time thì data trong cache được tính là cũ (stale).
→ Trong thời gian data là fesh, khi chuyển trang khác và quay về, data sẽ không refesh mà vẫn lấy data từ cache(nếu cache không tồn tại, nó sẽ call API để lấy data)
✅Lợi ích
+ Windown focus refetching: Khi user rời khổ tab app, RQ sẽ đánh dấu dât đó là “CŨ”, và fetch khi user quay trở lại
+ Request retry: Ta có thể đặt số lần refetch khi gặp lỗi.
+ Prefetching: Neesuapp cần data mới sau khi update, user có thể query data bằng 1 từ khóa cụ thể,
+ Optimize update: khi muốn sửa or xóa 1 phần tử trong list, bạn có thể đưa ra bản update list lạc quan
✅How to Fetch Data!
→ nếu tập data thứ 2 mà giống với tập data thứ nhất, thì RQ sẽ giữ cả 2 làm tham chiếu mà không buộc tải lại.
—————————————————————————————————————
::Hook API ❤️❤️❤️
✅useQuery (read)
*Syntax: const { a, b, c...props} = useQuery({ queryKey: ['todos'], queryFn: fetchTodos })
const { data, isLoading } = useQuery(”Key”, fn: () => void);
::Break code
→ “Key”: là key để lấy data từ cache, nếu dùng chung thì các đầu api trả về sẽ trả cho tất cả → giống như global state
→ fn: là function thực hiện get data
→ options: chú ý những thuộc tính này
+ cacheTime: Infinity, //Thời gian cache data, ví dụ: 5000, sau 5s thì cache sẽ bị xóa, khi đó data trong cache sẽ là undefined, khi đó nó sẽ call để lấy data.
+ staleTime: 10000, //Thời gian data trong cache được làm mới, nếu trong time này query được gọi thì nó sẽ lấy từ cache ra, nếu ngoài time này thì nó sẽ call api queryFn
+ refetchOnWindowFocus: false,
✅useMutation (create/update/delete)
→ Sd để update/delete/create data new
→ Cung cấp allow access vào fn Mutation, có thể truyền đối số trực tiếp, sau đó trả về trạng thái thông tin api request
State:
+ idle: trạng thái nhàn dỗi/mới: đặt lại
+ Loading: api đang running.
+ Error: Khi gặp lỗi
+ Success: trạng thái thành công / dữ liệu sẵn sàng
→ Có thể access state thông qua props (isIdle, isLoading, isError, isSuccess)
*Syntax: {isIdle, isLoading, isSuccess, isError} =useMutation(key: [] | string, fnCallApi);
✅Tip::
*Khi ngoại tuyến đọt ngột: sd “Request retry”
Syntax:: const {} = useMutation(fn, {retry: 3})
*Query client: hàm đóng gói và kèm nhiều phương thức xử lý bộ nhớ đệm cache
Syntax:: const queryClient = new QueryClient()
Method::
→ invalidateQueries: đánh dấu một truy vấn đã khóa được cho là không hợp lệ để cho RQ request lại api đó, được sd trong useMutation
→ setQueryData: sd cho việc cải thiện update data đã lưu vào cache
→ prefetchQuery: method giúp fetch một số data trước khi cần và rendered với useQuery
→ clear: xóa all data đã lưu cache
*useMutation hook options::
→ onMutate: hoạt động trc useMutation, sd cho việc update trên bộ nhớ cache cục bộ và update data trên UI trước khi mutation trên server.
→ onSuccess: fn run khi mutation success, request với key đã định trước và fetch dưới nền app
→ onError: fn run khi mutation gặp lỗi
✅Options for queries::
→ Gộp tất cả response error gần nhất thành 1 error gần nhất trả về cho user
DEMO::✅
*useQuery / useMutation
export default function Index() {
const [title, setTitle] = useState<string>('');
const queryClient = useQueryClient();
const { isLoading, isSuccess, isError, error, isIdle, mutate, data, status, reset } = useMutation(
'UPDATE_EVENT',
updateEvent,
{
onSuccess: () => {
queryClient.invalidateQueries('GET_LIST_EVENT');
setTitle('');
reset();
},
}
);
const {
isLoading: isLoadingList,
isRefetching: isRsfetchingList,
data: listFetch,
refetch,
} = useQuery(['GET_LIST_EVENT'], fetchSearchSwimlane);
return (
<Box height="100%" position="relative">
{(isLoadingList || isRsfetchingList) && (
<Box
position="absolute"
sx={{
backdropFilter: 'opacity(20%)',
zIndex: '1',
width: '100%',
height: '100%',
}}
>
<Loading />
</Box>
)}
<Box display="flex" gap={2}>
<TextField
size="small"
value={title}
disabled={isLoading}
onChange={(e) => setTitle(e.target.value)}
/>
<Button size="small" onClick={() => mutate({ title })}>
Submit
</Button>
</Box>
<Typography variant="caption">
{!isIdle && (isLoading ? 'Saving...' : isError ? 'Save error!' : 'Saved!')}
</Typography>
<Box>
{listFetch?.map((item: any, index: number) => {
return <pre key={index}>{JSON.stringify(item)}</pre>;
})}
</Box>
</Box>
);
}
*Optimize update
const { isLoading, isSuccess, isError, error, isIdle, mutate, data, status, reset } = useMutation(
'UPDATE_EVENT',
updateEvent,
// When mutate is called:
{
onMutate: async (newEvent) => {
// Cancel any outgoing refetches
// (so they don't overwrite our optimistic update)
await queryClient.cancelQueries('GET_LIST_EVENT');
// Snapshot the previous value
const previousEvent = queryClient.getQueryData('GET_LIST_EVENT');
// Optimistically update to the new value
queryClient.setQueryData('GET_LIST_EVENT', (old: any) => [...old, newEvent]);
// Return a context with the previous and new todo
return { previousEvent };
},
// If the mutation fails, use the context we returned above
onError: (error, newEvent, context) => {
queryClient.setQueryData('GET_LIST_EVENT', context?.previousEvent);
},
// Always refetch after error or success
onSettled: () => {
queryClient.invalidateQueries('GET_LIST_EVENT');
setTitle('');
reset();
},
// Single add
// await queryClient.cancelQueries({ queryKey: ['GET_LIST_EVENT', newEvent.id] });
// const previousEvent = queryClient.getQueryData(['GET_LIST_EVENT', newEvent.id]);
// queryClient.setQueryData(['GET_LIST_EVENT', newEvent.id], newEvent);
// console.log(newEvent, ['GET_LIST_EVENT', newEvent.id]);
// return { previousEvent, newEvent };
// },
// onError: (error, newEvent, context) => {
// queryClient.setQueryData(['GET_LIST_EVENT', context?.newEvent.id], context?.previousEvent);
// },
// onSettled: () => {
// setTitle('');
// reset();
// },
}
);
*TIP - React query
// in utils.ts or constants.ts
// Create a const to summarize all the keys used with react-query, to avoid duplicate keys when working with react-query
const getQueryKeys = (baseKey: string) => {
return {
all: [baseKey],
many: (params: Record<string, unknown>) => [baseKey, params],
one: (id: string) => [baseKey, id],
};