November 05, 2021
Server state and client state are fundamentally different.
흔히, 서버로 request 한 후에 받은 response data를 화면에 그리기 위해서 redux store에 저장한다 . CRUD user interaction이 이루어지면 request 를 다시 날릴 뿐 아니라 새로운 state 값을 사용하도록 reducer에서 state를 조작해야한다.
이 방법의 단점:
React Query는 response data는 Server State로 보고 State Management tool에서 저장하지 않아야 하며 Redux store에는 UI State들인 Client State만 다뤄야한다고 본다.
여기서 말하는 Server State란 어떤 것일까?
Server State:
이제는 더 이상 이러한 서버 state들을 client state tool에 저장하지 말아야 한다.
redux store를 사용한다면, useSelector로 state가 새로 업데이트 될 때마다 새로운 값을 가져올 수 있다. 또한 useSelector로 가져온 값은 다른 컴포넌트에서도 동일한 값을 가져올 수 있다.
React Query도 이와 비슷하게 같은 key 값을 사용하고 있는 Query가 있다면 그 Query를 사용하는 곳에서는 항상 같은 cache 데이터를 사용할 수 있도록 해준다. 사실상 useSelector로 값을 가져오는 것과 거의 동일하다. (개념상)
const result = useQuery({
queryKey, // Query에 사용되는 unique key, key가 변경될 때마다 새로 query함
queryFn, // 실제로 data를 request 하는 요청
enabled, // 이 Query를 실행할지 말지 나타내는 조건
})
StaleTime
CacheTime
inactive나 unused data가 메모리에 저장되서 유지되는 시간
거의 staleTime을 변경시켜야 하고, cacheTime은 거의 건들 일이 없을 것이다.
이러한 stale data는 아래 상황에서 다시 refetch 된다.
window가 refocused 되었을 때
Read 는 useQuery, Create, Update, Delete 는 useMutation을 사용한다.
보통 어떤 리스트에 item을 추가할 때, 서버에 POST 요청하고 response를 받는데 까지 시간이 걸리기 때문에 loader를 붙이는게 자연스럽다. useMutation를 이용하여 추가시 loader 추가나 리스트에 지연 없이 바로 아이템이 추가된 것 처럼 보여지게 할 수 도 있다.
useMutation은 [호출할 함수, mutation 요청에 대한 정보]을 return 한다.
const [createPost, createPostInfo] = useMutation((values) => axios.post('/api/posts', values, {
onSuccess: () => { // Mutation이 성공했을 때 다시 posts를 가져오도록 함 실행되는 callback
queryCache.invalidateQueries('posts')
},
onError: (error) => {
window.alert(error.response.data.messsage)
},
onSettled: () => { // onSuccess나 onError와 비슷함.
// 위의 onSuccess를 정의하지 않고 onSettled에서 호출하도록 하면 에러 발생시에도 다시 query를 해온다.
queryCache.invalidateQueries('posts')
}
}))
return (
<form
onSubmit={createPost}
buttonText={
createPostInfo.isLoading
? 'Saving...'
: createPostInfo.isSuccess
? :'Saved'
: 'Create New Post'}
/>
)
const [savePost, savePostInfo] = useMutation(
values =>
axios.patch(`/api/posts/${values.id}`, values).then(res => res.data),
{
onSuccess: (data, values) => {
queryCache.setQueryData(['posts', String(values.id)], data) // 변경하고자 하는 값 바로 UI에 만영
queryCache.invalidateQueries(['post', String(values.id)]) // Data Accuracy를 위해서 다시 refetch
},
}
)
typeDefinition of updater function in setQueryData
queryCache.setQueryData('key', oldData => ({
...oldData,
lastName: 'Smith',
}))
oldData가 undefined 일 수 있다는 런타임 에러가 뜬다.
현재 Typed에서는 documentId로 문서 내에 있는 폴더 데이터 배열을 가져온다.(Folder[]). 이 배열을 cache로 저장하기 보다는 folders 자체의 변경 사항(폴더 순서 변경, 삭제, 추가)보다 폴더 자체의 변경 사항(folder에 리소스 추가, 삭제 등)이 많기 때문에 folder 데이터 각각을 cache로 저장해야한다고 생각했다. 하지만 folder의 순서도 어쨌든 배열로 cache 되어 있어야 하기 때문에 어떻게 구조를 잡아야할지 다시 고민됐다.