this is my first question on Stackoverflow after one year of Front-end development self-learning. I've already serached foe an answer to my doubts, but since these questions are returning for the third time, I think it's the moment to ask the Web.
What I'm trying to build
I'm trying to build a library service, where a guest user can login, reserve books, add to wishlist, return etc. For the front-end I'm using react
, react-router
, react-bootstrap
and redux-toolkit
. Since I have no knowledge about back-end yet, I fetch data with json-server
, which watch a simply database in which there are two big objects: users
and the books catalogue
.
The app flow, in my opinion
In the code there is a <Catalogue />
component, which send a request to the json-server' to get the data (with
useEffect), which is stored in the state. When data is fetched, it renders
` components, with the necessary info and two buttons. One of them is intended to reserve a book. The logic behind the Reserve Button is:
- verify IF the current user is not an admin
- verify IF the current user has already reserved THIS book
- verify IF there is at least one book to reserve in database
ONLY IF everything it's ok, the code dispatch the action created with createAsyncThunk
in catalogueSlice
. In this thunk, it remove a copy of the book from the database and pass the result to the extrareducers
to update the state. The handleClick function is async
and waits for the end of this action. When the operation is finished, the code dispatch another action, the one created with createAsyncThunk
in userSlice
, which updates the database, adding that book in the current reservation of that user, and like the other thunk, pass the result to the state, and updates the user's state.
My doubts and questions
My main question
- Where is the correct place for the above IF statements which verify user, current reservations and presence of the book in the database? In the React component or in the createAsyncThunk? I mean: is it better to verify IF an action has to be dispatched, or dispatch the action and block it after? Is it ok for example to call
dispatch
inside the thunk using thethunkAPI
?
My other doubts
Is it usually better to retrieve the state using
useAppSelector
or, when it's possible, pass it through the childrenprops
?Until now, I have already
reserve
,addToWishlist
,login
,register
, which are all created withcreateAsyncThunk
, and results a lot of code in theextrareducers
of the slices. I'm planning to add more, which will be other thunks which send request to the server. Is this work-flow ok? I'm doing something big logic mistakes with the back-end?I don't understand, which is the difference between a
thunk
and amiddleware
. Any useful resource?
These are the two main thunk to send the requests. I'm sorry but they're not so clean, but I think it's enough to understand the question.
export const patchCatalogue = createAsyncThunk(
'catalogue/patch',
async ({book, userInfo}: {
book: IBook;
userInfo: IUserInfo;
}, thunkAPI) => {
const {
id: bookId,
book_status: {
copies,
history,
current
}
} = book;
const {
id: userId,
username
} = userInfo;
try {
const response = await axios.patch(
`http://localhost:5000/catalogue/${bookId}`,
{
book_status: {
copies: copies - 1,
current: [...current, [userId, username]],
}
});
if (response.status === 200) {
const result = await thunkAPI.dispatch(reserve({ book, userInfo }))
// console.log(result)
return response.data;
}
}
catch (error) {
console.error(`PATCH failed - ${error}`)
}
},
);
export const reserve = createAsyncThunk(
'user/reserve',
async ({ book, userInfo }: {
book: IBook;
userInfo: IUserInfo;
}, thunkAPI) => {
const {
id: userId,
reservations: {
current,
history,
}
} = userInfo;
try {
const response = await axios.patch(`http://localhost:5000/users/${userId}`,
{
reservations: {
current: [...current, book],
history: [...history],
}
});
return response.data;
}
catch (error) {
console.error(error)
}
}
);
This is the Button component, which dispatches the two actions.
if (userInfo.role === 'user') {
if (action === 'Book now!') {
const alreadyBooked = userInfo.reservations.current.find(item => item.id === book.id)
if (!alreadyBooked) {
await dispatch(patchCatalogue({ book, userInfo }));
dispatch(reserve({ book, userInfo }))
}
else {
alert('The book is already reserved!')
}
}
CodePudding user response:
The Answer to the main Question.
According to me, The correct place for the above IF statements, is the React Component. Dispatching an action means, involving the Redux Store in the App workflow. So it is useless to involve the Store, just for checking the conditions. It also helps to keep the code clean in the store slice.
Keen to know the answer to your other doubts.
CodePudding user response:
In my opinion, for conditions such as availability and user role:
- Do check in the React Component, so a
disabled
button can be rendered if the conditions are not met. - This prevents the dispatch to run in the first place, and a helper text such as
"out of stock", "already reserved"
can also be displayed for better user experience. - When calling the api, another verification can happen on the back end before writing the data base, with strict rules for security.