This is a solution to the Todo app challenge on Frontend Mentor. This project is a full-stack web application built using the MERN stack.
Note: Delete this note and update the table of contents based on what sections you keep.
Users should be able to:
- View the optimal layout for the app depending on their device's screen size
- See hover states for all interactive elements on the page
- Add new todos to the list
- Mark todos as complete
- Delete todos from the list
- Filter by all/active/complete todos
- Clear all completed todos
- Toggle light and dark mode
- Solution URL: Solution URL
- Live Site URL: Live site
- Node.js (Express)
- MongoDB
- Deployed on Render
- React
- TailwindCSS
- React Query
- Deployed on Vercel
- Clerk
Throughout this project, I managed to grasp the knowledge of building a full-stack application using Express.js for the backend and React for the client side.
Also I tried to use Tanstack Query in this project. One thing that made me stuck was to make it work with Clerk.
Since my backend is protected by the Clerk middleware. It requires me to get the session token from the client which can be get through useAuth
hook.
const Content: FC<ContentProps> = ({ type, session }) => {
const { data, isLoading, isError } = useQuery<Todo[]>({
queryKey: ["todos"],
queryFn: async () => {
try {
const res = await axios.get(BACKEND_URI, {
headers: {
Authorization: `Bearer ${session}}`
}
});
return res.data;
} catch (error) {
throw error;
}
},
});
if (isLoading) {
return <Loader />;
}
if (isError) {
return (
<div className="..."></div>
);
}
return (
<div className="..."></div>
);
};
The issue with passing the token through props is that the token is obtained asynchronously, meaning it may not be available immediately when the component mounts and the axios request is made. Since the request is made synchronously during rendering, passing the token as a prop directly could result in the token not being ready when needed for the request, causing errors.
Then I came across a better approach based on the Clerk's docs about using Clerk with Tanstack Query. With that, I created a custom hook as follows that I can call in any necessary component as follows:
export const useGetTodosQuery = (type: 'all' | 'active' | 'completed') => {
const { getToken } = useAuth();
return useQuery<Todo[]>({
queryKey: ["todos", { type }],
queryFn: async () => {
try {
const res = await axios.get(BACKEND_URI, {
headers: {
Authorization: `Bearer ${await getToken()}`
}
});
return res.data;
} catch (error) {
throw error;
}
},
});
}
With this, the token will always be ready whenever useQuery
executes the query function.
Clerk is still in dev mode because it is still not supporting production apps on name.vercel.app
. Explaination by the CTO here. If Clerk can support deploying to Vercel without any paid domain, I will migrate it to production mode.
- GitHub - Zubair Adham
- Frontend Mentor - @atmahana