How to build query in React MERN stack - reactjs

I use the MERN stack and I want to create something like this on the frontend: http://localhost:3000/products?color=0000&sort=latest&category=man etc.
I'm using the redux toolkit and my getProductSlice looks like this:
export const getProducts = createAsyncThunk(
'product/get',
async ({ page, sort, localValue, colorFilter, categoryFilter }, thunkAPI) => {
let colorStr = colorFilter.substring(1);
let url = `/api/v1/products?page=${page}&sort=${sort}&color=${colorStr}&cat=${categoryFilter}`;
if (localValue) {
url = url + `&search=${localValue}`;
}
try {
const { data } = await axios.get(url);
return data;
} catch (error) {
const message = error.response.data.msg;
return thunkAPI.rejectWithValue(message);
}
}
);
Every filter works great, in the backend I return products depending on which filter is applied. I dont know how to build query in frontend and when i enter that url i want to filters to be applied based on url.

To build a query string in the frontend and apply filters based on the query string in the URL, you can do the following:
1 Parse the query string from the URL using the URLSearchParams API:
const searchParams = new URLSearchParams(window.location.search);
Extract the values of the filters from the query string using the get method of URLSearchParams:
const color = searchParams.get('color');
const sort = searchParams.get('sort');
const category = searchParams.get('category');
Use the extracted filter values to create a payload object for the getProducts async thunk:
const payload = {
page: 1, // or whatever page you want to start from
sort,
localValue: '', // or whatever value you want to use for the localValue filter
colorFilter: color,
categoryFilter: category
};
Dispatch the getProducts async thunk with the payload object:
dispatch(getProducts(payload));
This should apply the filters based on the query string in the URL when the getProducts async thunk is dispatched.

Related

How to fetch with parameters using React Query?

For the sake of this question let's first assume existence of such entity:
export interface Event {
id: number;
date: Date;
}
Then let's assume there's backend with such endpoints:
GET /events -> returns all events
GET /events?startDate=dateA&endDate=dateB -> returns all events between dateA and dateB
I create hook containing 4 methods (one for each CRUD operation) in my frontend code like this:
export function useEvents() {
const getEvents() = async () => {
const response = await axios.get(`events`);
return response.data;
}
const postEvent()...
const updateEvent()...
const deleteEvent()...
const query = useQuery('events', getEvents);
const postMutation = ...
const updateMutation = ...
const deleteMutation = ...
return { query, postMutation, updateMutation, deleteMutation }
}
This architecture works like a charm but I got to the point where I would like to conditionaly fetch events based on currently chosen month in my Calendar.tsx component.
How would I inject this information into useQuery() and getEvents()?
the query key should contain all "dependencies" that you need for your fetch. This is documented in the official docs here, and I've also blogged about it here.
So, in short:
const getEvents(month) = async () => {
const response = await axios.get(`events/${month}`);
return response.data;
}
const query = useQuery(['events', month], () => getEvents(month));
The good thing is that react-query will always refetch when the key changes, so data for every month is cached separately, and if the month changes, you'll get a fetch with that month.

How to share data from useState in React to another Component

Hey guys let me quickly explain my problem.
I currently have Component in which User can Search something. And after they Click on a Button I get the Data from Firebase which is then stored in a useState which I map afterwards. This whole operation is in one function.
But for now I show the result at the same Page because I dont know how to transfer the data in the useState to the other component.
const handleClick = async () => {
const kurzRef = collectionGroup(db, 'kurzwaffensub' );
const MOD = query(kurzRef,where("kurzModell", "==", `${kurzModell}` ));
if(kurzModell) {
const getWaffenDaten = async () => {
const modell = await getDocs(MOD);
const data = [];
for (const doc of modell.docs) {
const parentDoc = await getDoc(doc.ref.parent.parent);
const { Name, avatar,avatarPath, Erfahrung, Adresse, Schützenverein } = parentDoc.data();
const waffenbilderRef = collection(db, 'users', doc.data().uid, 'waffenbildersub')
const subCollectionDocs = await getDocs(waffenbilderRef)
const subCollectionData = subCollectionDocs.docs.map((doc) => {
return { id: doc.id, ...doc.data()}
})
data.push({
...doc.data(),
Name,
subCollectionData,
avatar,
avatarPath,
Erfahrung,
Adresse,
Schützenverein
});
}
setTest(data)
}
getWaffenDaten()
}
After that operation I just return the Result in the same Page . And I want to change the page after the onClick event with the Result. Because I dont want to see the User Interface of the Search Component.
Perhabs its pretty simple but Im still a beginner and would be very glad if you can help me out and teach me something new and important.
You can do this in multiple ways:
You can pass search query as URL parameter if you using router and fetch the data from result page
You can use state management tool like Redux or built in context api.

How to navigate to the update/detail page after a POST in RTK query

I've got a form with a POST in REACT RTK-query and then it should navigate to the second step, but for that I need to know the id of the newly created record. The id is not sent in POST but AutoIncremented in the backend. I don't want to navigate to the list-view to get that id but directly.
const [addMyModel] = useNewMyModelMutation();
const handleSubmit = (e: { preventDefault: () => void; }) => {
e.preventDefault();
const my_model = {
"user_id": user_id,
"date_time_field": new Date(),
"icm": icmChoice,
...
};
dispatch(setUniqueFilters(unique_filters))
//localStorage.setItem('unique_filters', JSON.stringify(unique_filters))
//localStorage.setItem('selection_positions', JSON.stringify(positions))
//localStorage.setItem('my_model', JSON.stringify(my_model))
addMyModel(my_model)
push(`/icm/mymodels/list`) // push(`/icm/mymodels/${answer_question}`);
What could be the best solution?
try to send the id in response from the backend after POST (Django Rest Framework),
change the primary key in the backend as such that I will know the id before handleSubmit (did that before but forgot the reason why I set everything back.)
saving step1 in the localStorage (but then there will be problems with updating previous records),
saving step1 in the state (but than I cannot refresh)
It's simple, but only in case your API returns a newly created item on POST response (which is expected for RESTfull API).
If so - just await the answer from hook:
const data = await addMyModel(my_model).unwrap()
// id should be in `data`
Considering that mutation "trigger" function returns a Promise, you can use it as usual:
addMyModel(my_model).then(newItem => {
if ("data" in newItem) {
const expectedId = item.data.id;
// use you expectedId
}
});
const [addMyModel, result] = useNewMyModelMutation();
...
const handleSubmit = (e: any) => {
e.preventDefault();
const my_model = {
"user_id": user_id,
"date_time_field": new Date(),
"icm": icmChoice,
...
};
addMyModel(my_model)}
result.status === "fulfilled" && push(`/icm/mymodels/update/step2/${result.data["id"]}`)

Is it possible to send the pages id in nextJS ussing useSWR so I can fetch data based on the ID of the page I am on

I am doing a request with useSWR in next js and the api route needs the id of the page I am on, it is in the query params at router.query.id But how do i pass that to useSWR so I can pull in the right data from my api router function that talks to my DB.
Here is the simple request
const router = useRouter();
//PASS ROUTER.QUERY.ID to api/dataSingle
const { data: valuesData, error: valuesError } = useSWR(
`/api/dataSingle/`,
fetcher,
);
UPDATE: If I try to pass the array as per documentation the requerst body in the api function is undefined. Here is what I have tried since reading it.
const fetcher = (url) => fetch(url).then((r) => r.json());
const router = useRouter();
const { data: valuesData, error: valuesError } = useSWR(
[`/api/dataSingle/`, router.query.id],
fetcher
);
api Route
import { query as q } from "faunadb";
import { serverClient } from "../../utils/fauna-auth";
export default async (req, res) => {
console.log(req.body);
returns undefined
};
UPDATE: I have tried the following fetcher, but still don;t grasp what it is even doing so it's not working.
const fetcher = (url, id) => fetch(url, id).then((r) => r.json());
The first parameter in useSWR has two functions:
It is converted into a key for the cache that stores the data received.
It is passed to the fetcher function to allow for retrieval of data.
const fetchWithId = (url, id) => fetch(`${url}?id=${id}`).then((r) => r.json());
const Example = ({ id }) => {
const { data, error } = useSWR([`/api/dataSingle/`, id], fetchWithId);
return (
<pre>
{data && JSON.stringify(data, null, '\t')}
</pre>
)
}
When using an array for the first parameter of useSWR the cache key will be associated with each entry in the array. This means ['/api/dataSingle/', 1] receives a different cache key to ['/api/dataSingle/', 2].
The allows you to pass data other than route or query string parameters that are required by your fetch code.
NOTE DIRECTLY FROM DOCUMENTATION: SWR shallowly compares the arguments on every render, and triggers revalidation if any of them has changed.
As i understand it, you want to fetch data on your api route based on the page id. so if so i am assuming your api route contains query params.
const router = useRouter();
//PASS ROUTER.QUERY.ID to api/dataSingle
const { data: valuesData, error: valuesError } = useSWR(
`/api/dataSingle?id=${router.query.id}`,
fetcher,
);
in the api route, you'll get the query with req.query
import { query as q } from "faunadb";
import { serverClient } from "../../utils/fauna-auth";
export default async (req, res) => {
console.log(req.query);
returns undefined
};

Creating Dynamic Routes using 'getStaticPaths' and 'getStaticProps' in NextJS

I am trying to create dynamic pages that show individual book details (.i.e. title/author) on a separate page based on a query string of the "id" for each book. In a previous question I asked, answers from users were very helpful and I have a much better understanding of how to use getStaticPaths and getStaticProps correctly. However, I am not quite there in my code for how to do this.
Here is the basic setup and context.
I am running NextJS 9.4 and would like to use a API endpoint instead of querying the database directly.
The book data is being pulled from a MongoDB Atlas Database and uses Mongoose
Documents in the MongoDB have a "_id" as a unique ID.
I have tried to incorporate and learn from existing Github examples and NextJS documentation but I still get the following error.
Error: A required parameter (id) was not provided as a string in getStaticPaths for /book/[id]
Here is the code I have so far. I have tried to keep the code as clean as possible for now.
export default function Book({ book }) {
return (
<article>
<h1>Book Details Page</h1>
<p>{book.title}</p>
<p>{book.author}</p>
</article>
)
}
export async function getStaticPaths() {
const url = `${baseUrl}/api/books/books`
const response = await axios.get(url);
const books = response.data
const paths = books.map((book) => ({
params: { id: book.id },
}))
return { paths, fallback: false }
}
export async function getStaticProps({ params }) {
const url = `${baseUrl}/api/books/books/${params.id}`
const res = await axios.get(url)
const book = await res.json()
return { props: { book }}
}
The API endpoint looks like this:
import Book from '../../../models/Book';
import dbConnect from '../../../utils/dbConnect';
// conects to the database
dbConnect();
// This gets all the book from the database
export default async (req, res) => {
const books = await Book.find()
res.status(200).json(books)
}
Any support or feedback would be greatly appreciated. Once I get this working, I can hopefully be able to understand and help assist others in creating dynamic routes with NextJs. Thank you.
You can't make calls to Next.js API routes inside getStaticProps or getStaticPaths. These functions are executed at build time, so there is no server is running to handle requests. You need to make request to DB directly.
If you want to keep it clean you could create a helper module like allBooksIds() and keep DB query in a separate file.
See the same issue - API call in NextJS getStaticProps
Simply add toString() method in getStaticPaths because the book id is of type ObjectID("ID") if you do params: { id: book._id.toString() } it will convert ObjectID("ID") to type string which is accepted by getStaticPaths().The complete code for the nextjs part is below also update your API route as follows :-
The upper one is the API route the bellow one is Nextjs Page
import Book from '../../../models/Book';
import dbConnect from '../../../utils/dbConnect';
// conects to the database
dbConnect();
// This gets all the book from the database
export default async (req, res) => {
const books = await Book.find({})
res.status(200).json(books)
}
export default function Book({ book }) {
return (
<article>
<h1>Book Details Page</h1>
<p>{book.title}</p>
<p>{book.author}</p>
</article>
)
}
export async function getStaticPaths() {
const url = `${baseUrl}/api/books/books`
const response = await axios.get(url);
const books = response.data
const paths = books.map((book) => ({
params: { id: book._id.toString() },
}))
return { paths, fallback: false }
}
export async function getStaticProps({ params }) {
const url = `${baseUrl}/api/books/books/${params.id}`
const res = await axios.get(url)
const book = await res.json()
return { props: { book }}
}
Hope this is helpful

Resources