i'm working with react query queryClient, i want to add new data from result of mutation to previous data with key ["feed", id], i combined res and previous to newQuestions and when i console.log newQuestions i get the right data, but when i put that to queryClient.setQueryData it returned undefined
const { isLoading, mutate } = useMutation(createQuestion, {
onSuccess: (res) => {
//get prev data
const previousQuestion = queryClient.getQueryData(["feed", id]).data;
//combine
const newQuestions = [res, ...previousQuestion];
console.log(newQuestions);
//put
queryClient.setQueryData(["feed", id], newQuestions);
}});
however if i put res or previous to queryClient.setQueryData it works
const { isLoading, mutate } = useMutation(createQuestion, {
onSuccess: (res) => {
//put
queryClient.setQueryData(["feed", id], res); //it works
}});
and
const { isLoading, mutate } = useMutation(createQuestion, {
onSuccess: (res) => {
//put
queryClient.setQueryData(["feed", id], (previous)=>previous); //it works
}});
but
const { isLoading, mutate } = useMutation(createQuestion, {
onSuccess: (res) => {
//put
queryClient.setQueryData(["feed", id], (previous)=>[res,...previous]); //doesnt work
}});
Related
I want to revalidate the date when on change. This is what I tried:
const fetchData = async() => {
const {
data
} = await axios.get(`/api/admin/orders${criteria}`, {
params: {
name: debouncedValue,
},
});
return data;
};
const {
data,
error: err,
mutate,
} = useSWR(`/api/admin/orders${criteria}/${nameSearch}`, fetchData);
const handleChange = (e: React.ChangeEvent < HTMLInputElement > ) => {
mutate();
setNameSearch(e.target.value);
};
But the data is no revalidate, I have to use the onTabFocus revalidation.
I want to apply server side search filter by text using redux toolkit.
I have two query builder methods in place. One for fetching all items and second for fetching only filtered data.
Query builder for fetching all items is
getAllBlogs: builder.query<BlogType[], void>({
queryFn: async () => {
const collectionRef = collection(Firestore, BLOG_COLLECTION)
const q = query(collectionRef, limit(1000))
const resp = await getDocs(q)
return {
data: resp.docs.map((doc) => doc.data() as BlogType),
}
},
providesTags: (result) => {
const tags: { type: 'Blogs'; id: string }[] = [
{ type: 'Blogs', id: 'LIST' },
]
if (result) {
result.forEach(({ id }) => {
tags.push({
type: 'Blogs',
id,
})
})
}
return tags
},
}),
This works fine and I'm getting the whole list through useGetAllBlogsQuery data.
Query builder for fetching filtered data is here: (Partially completed)
getBlogsByTitle: builder.query<BlogType[], string>({
queryFn: async (title) => {
const collectionRef = collection(Firestore, BLOG_COLLECTION)
const q = query(
collectionRef,
where('searchIndex', 'array-contains', title),
limit(1000),
)
const resp = await getDocs(q)
return {
data: resp.docs.map((doc) => doc.data() as BlogType), // Correct data
}
},
// I'm trying to only push the resultant items in state. This is not working
providesTags: (result) => {
const tags: { type: 'Blogs'; id: string }[] = []
if (result) {
result.forEach(({ id }) => {
tags.push({
type: 'Blogs',
id,
})
})
}
return tags
},
}),
I have react component looks like this where I'm calling these queries.
const Blogs: NextPage = () => {
const { data: blogs } = blogsApi.useGetAllBlogsQuery()
const [getBlogsByTitle] = blogsApi.useLazyGetBlogsByTitleQuery()
const debounced = useDebouncedCallback(async (value) => {
const { data } = await getBlogsByTitle(value)
console.log(data) // Correct data
}, 500)
return (
<div>
<InputText
onChange={(e) => debounced(e.target.value)}
/>
</div>
)}
The above code has two functionalities.
Fetch all the items on initial load.
Filter when debounced function is being called.
What I want is when getBlogsByTitle is called it will auto update the same state blogs in redux and we don't have to do much.
We are getting correct response in getBlogsByTitle but this query is not updating state with only its filtered response.
I'm new to redux-toolkit. Can someone help me out here where am I doing wrong ?
I'm getting a 400 client error and saying state.category.push is not a function and the current state is pending. I will be happy if someone helps me out on this. The category array is not accepting the data coming into it. I have both the form file and the reducer file down there. I am trying to create a category array to accepts users category and later output it out.
blogSlice.js
My Reducer file
const urlParams = new URLSearchParams(window.location.search)
const TokenAuthless = urlParams.get('enter')
if(TokenAuthless){localStorage.setItem('authless',
JSON.stringify(TokenAuthless))}
var Token = JSON.parse(localStorage.getItem("authless"))
const initialState = {
blogItems: [],
isLoading: null,
category: [{}],
authors: [],
}
const categoryUrl = 'api/v1/admin/createBlogCat';
var api_origin = 'xxxxxxxxxxxx'
export const insertCategory = createAsyncThunk('blog/insertCategory', async(data,
thunkAPI) => {
const{ rejectWithValue } = thunkAPI;
try {
const res = await fetch(`${api_origin}/${categoryUrl}`,{
method :'POST',
mode: 'cors',
body: JSON.stringify({data}),
headers : {
'Authorization': `Bearer ${Token}`,
'Content-type':'application/json',
'Accept':'application/json',
'Access-Control-Allow-Origin':'*',
},
})
const catData = await res.json();
return catData.data;
} catch (error) {
return rejectWithValue(error.message)
}
})
[insertCategory.pending]:(state, action) => {
state.isLoading = true;
},
[insertCategory.fulfilled]: (state, action) => {
state.isLoading = false;
console.log(action.payload);
console.log(current(state))
state.category.push(action.payload);
console.log('the state category', state.category);
},
[insertCategory.rejected]:( state, action ) => {
state.isLoading = false;
},
CreateCategory.js
Creating a form to accept the input here
const CreateCatAut = () => {
const [name, setName] = useState('');
const dispatch = useDispatch()
const handleSubmit=(e)=>{
e.preventDefault();
const categoryData = {name}
dispatch(insertCategory(categoryData));
console.log(categoryData)
}
return (
<form onSubmit={handleSubmit}>
<input type="text" placeholder="Active Input"
value={name}
onChange={(e)=> setName(e.target.value)} />
)
It means that your state.category is not an array. At
state.category.push(action.payload)
I assigned a value to the state.category which is not yet an array. By first creating an array and then putting that element into it.
state.category= [action.payload]
and at the next iterations, i will have an array with one element and can use push on it.
I have multiple API calls with fairly lengthy, yet similar, response/error handling for each call.
What is the best non-repetitive ways to make multiple independent api calls that update state using fetch?
Copying and pasting 40+ instances of fetch doesn't seem right.
I want to avoid doing this ....
fetch(url,options)
.then((response) => {
// ...
return response.json
})
.then((data) => {
setState(data)
//...
})
.catch((err) => {
//Error logic here
})
Here's what I've done so far:
I made (found and modified) a useFetch hook...
useFetch.ts
//Only calls fetch() when .load() is called.
const useFetch = (path : string, HttpMethod : string, dependencies : any = [] , body : {} | undefined = undefined) => {
const history = useHistory()
const [response, setResponse] = useState<any>({});
const [error, setError] = useState<string>("");
const [isLoading, setIsLoading] = useState<boolean>(false);
const [controller, setController] = useState(2)
const [isReady, setIsReady] = useState<any>(false)
const load = ():void => {
setError("")
//This prevents useEffect from triggering on declaration.
if (isReady) {
//Math.random() is just to get useEffect to trigger.
setController(Math.random())
}
}
const token = localStorage.getItem("token");
let requestOptions:any = {
method: HttpMethod,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "* always",
Authorization: "Token " + token,
},
};
if (body !== undefined) {
requestOptions["body"] = {
body: JSON.stringify(body)
}
}
const URI = BASE_URI + path
useEffect(() => {
const fetchData = async () => {
if (controller !== 2) {
setIsLoading(true);
try {
const res = await fetch(URI, requestOptions);
const json = await res.json();
if (json?.action == "ENFORCE_BILLING" ) {
history.push(BILLING_CREDENTIALS_PATH, { enforceBillingPopUp: true });
}
if (json?.action == "ENFORCE_SMS_CONFIRMATION") {
// Should we log user out, as well?
history.push(CONFIRMATION_CODE_PATH)
}
if (res.ok) {
setResponse(json);
setIsLoading(false)
} else {
setError(json)
setIsLoading(false)
}
} catch (err) {
setError(err);
// Error logic here...
}
}
}
};
fetchData()
setIsReady(true)
}, [controller, ...dependencies]);
return { response, setResponse ,error, isLoading, load, isReady };
};
Component.tsx
//Inside react functional component...
// Prepares to fetch data from back-end
const data1 = useFetch(PATH1, "GET");
const data2 = useFetch(PATH2, "GET");
const data3 = useFetch(PATH3, "GET");
useEffect(() => {
// Initial on load data fetch
// .load() fetches data
data1.load();
data2.load();
data3.load();
}, [activeReservations.isReady]);
// Sort data depending on sort selection
...
Is useFetch considered bad practice? What are the advantages of using Redux, instead?
Any help would be greatly appreciated. Thanks.
I'm trying to make a "edit" feature for my project, and I'm stuck at this part..
I have a put request :
export const updateEvent = (event, id) => (dispatch, getState) => {
request
.put(`${baseUrl}/event/${id}`)
.send(event)
.then(response => {
dispatch(updatedEvent(response.body))
})
.catch(error => console.log(error))
}
This is the route for the said put, with Sequelize as ORM:
router.put('/event/:id', async (req, res, next) => {
const { id } = req.params
try {
const event = await Event.findByPk(id)
const updatedEvent = await event.update(req.body)
res.send(updatedEvent)
} catch (error) {
next(error)
}
})
When I test it with postman, everything works as expected. Where I ran into my problem is when I'm sending the put data from React in the frontend.
I have a form, and I save my data in the local state, and then dispatch it to actions like this:
handleSubmit = e => {
e.preventDefault()
const id = this.props.event.id
const updatedEvent = {
name: this.state.name,
description: this.state.description,
picture: this.state.picture,
startDate: this.state.startDate,
endDate: this.state.endDate,
userId: this.props.userId
}
this.props.updateEvent(updatedEvent, id)
}
Any value that is left empty in the form is overwriting my fields with nothing (an empty string). How do I properly handle this?
A solution is to filter your object, such that you remove any properties which have empty values and therefore won't be included in the database update.
In your router.put():
router.put('/event/:id', async (req, res, next) => {
const { id } = req.params
try {
const event = await Event.findByPk(id);
// filter req.body to remove empty values
const { body } = req;
const filteredBody = Object.keys(body).reduce((resultObj, key) => {
if(body[key] != ''){
resultObj[key] = body[key];
}
return resultObj;
}, {});
const updatedEvent = await event.update(filteredBody);
res.send(updatedEvent)
} catch (error) {
next(error)
}
})