Skip to content

Conversation

task-dev-alt
Copy link

No description provided.

Copy link

@gpositive gpositive left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @task-dev-alt ,

Great work mate! Thank you a lot for the effort you put in this PR!

Could you please check my comments when you have some time?

  • How come you used tailwind? Are you familiar with alternatives?
  • I notice you chose to use React Query for state management instead of Redux or similar solutions. Could you explain your thought process behind this decision and any trade offs you considered?
  • The current favorite cats implementation fetches all favorites at once. How would you modify this to handle thousands of favorites while maintaining good performance?
  • How could we improve the performance of our page load time regarding images? Are there any ways the API gives us to achieve that?

Again many thanks for your work!

@@ -0,0 +1,22 @@
const apiKey =
import.meta.env.VITE_API_KEY ??
"live_TvpSUchzwViDMtV85zilyB9NY9RH8Zz0atuDHnSP9X5LycsRtLQFoCh52EhzfwrQ";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any concerns on adding this value in here?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normally I'd avoid hardcoding the key, but in this demo I chose to add it for the following reasons:

  • there's a rate limit in the API,
  • the key would anyway be visible in the network tab and available in the bundled code,
  • the app is entirely client side.

Client side apps can't hide things, so using an environment variable would do little here, in my opinion.

What do you think?

...(breedId && { breed_id: breedId }),
});

return useInfiniteQuery({

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come you've decided to use useInfiniteQuery in here? What are the reasons behind this implementation?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Showing a list of 10 random images with a button to load more is a great case for useInfiniteQuery, as the newly fetched items are appended to the previous ones on every fetch. The React Query docs also suggest this.

BreedFilters and Breeds were storing the
filters state twice. State was moved to
useBreedsState and handlers are being
passed to BreedFilters.

Also rearranged the useBreedsState a bit to
make it more readable.
@task-dev-alt
Copy link
Author

task-dev-alt commented Jun 6, 2025

Hello @gpositive,

Thanks for the nice words and for the thought provoking comments! I pushed a few updates today but I'll stop since you started reviewing it.

  • Regarding tailwind: I find it very handy for small projects as it allows you to build things quite fast and you can skip the hard part of naming things 😄 . This, combined with using an LLM to generate the jsx code, is a huge productivity boost for such projects. For bigger projects I've used vanilla css with BEM methodology, styled components, and css modules. I find the latter mixed with sass the best so far.
  • I'd say React Query is the standard nowadays when it comes to server state management. This app didn't really need client state, as cat / breed selection was saved in the url and the breed filters were only used in the breeds page. I'd choose another solution if I wanted a normalized state with very frequent access (e.g. live data) as React Query doesn't do this, but my go to solution would be React Query + Zustand as they're both very simple to start with and scale pretty well.
  • To optimize the favourite cats fetching, I'd add pagination to it. I noticed the /favourites endpoint returns pagination information in the response headers so it'd be possible. (edit: added another comment below regarding this one)
  • This is an interesting question about image load optimization that I haven't explored so much so far. I think I would do something like this:
    • set the limit param according to how many images are visible in the viewport, and/or set the loading and fetchpriority <img /> attributes to lazy - high. I'm not completely sure but maybe setting lazy and high to the first images and eager and low to the rest would work as well.
    • Also, the API supports a media_type param, so I'd use media_type=jpg,gif (gifs are nice 😺).
    • If I needed to squeeze a little bit more of optimization, I'd split the request to chunks and use Promise.all to fetch concurrently.
    • Another optimization would be to add preconnect and dns-prefetch links to the HTML head.

Thanks again for your thoughtful comment!

EDIT: I'll reply to the other comments after the weekend. Have a nice one!

@task-dev-alt
Copy link
Author

Hello @gpositive,

I wanted to write a bit about the favourite cats optimization after giving it some thought.

The API has some limitations when it comes to the favourite cats:

  1. You can't get a favourite cat by image id,
  2. You don't get information about a cat being favourite in the GET images request.

If any of those 2 was possible, we'd be able to paginate the list request and also be able to show the favourite status in each individual cat in the other 2 pages. However, now, it's necessary to fetch all favourite cats without paginating.

Ideally, this would be solved in the backend to allow either of the above.

But, if we really had to solve this on the client side for thousands of records, I'd do one or more of the following:

  1. fetch them once at the start and then on add / delete favourite edit the cache instead of refetching.
  2. store them in a normalized format (e.g. Set / Map ({ image_id: favourite_id })) for faster access.
  3. store them in local storage for faster initial UI and fetch again in the background.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants