Skip to content

Ch.8 GitHubUser Component with localStorage - works by accident? #198

@falloutphil

Description

@falloutphil

Hi,

I think this component only succeeds by accident because the camel-case string that comes back from the GitHub API ("MoonHighway") differs in case from the lowecase string the book passes as the prop ("moonhighway"). If you, more correctly, pass the the exact casing that GitHub returns as the prop to the GitHubUser component , then nothing is ever cached?

So the caching strategy silently fails for any user who types the exact case GitHub uses (or for any API that preserves case). The guard:

if (data.login === login) return;

is the culprit: it blocks the very moment we actually want to write - on the second triggering of the [data] useEffect, by setData being called post fetch - here we will return before saving if data.login === login which is true if we set login==="MoonHighway", rather than using the subtley different login==="moonhighway" given in the example which will fail to match "MoonHighway".

I only tested it quickly but I think a valid fix that achieves the desired result is to check for a valid key existing using loadJSON. The text quotes:

Also, if the current login and data.login are equal to each other, then we already have saved data for that user.

My proposed fix:

  useEffect(() => {
    if (!data) return;
    if (loadJSON(`user:${login}`)) return; // already cached → skip
    const { name, avatar_url, location } = data;
    saveJSON(`user:${login}`, {
      name,
      login,
      avatar_url,
      location
    });
  }, [data]);

Logins differ on first run - highlighted in code and output in pink:

Image

Then on refresh they match because we store the props login not the data.login:

Image

But if I correct the login hardcoded in the code to match exactly the case provided by GitHub, and pass this as a prop, then no matter how often I refresh it always fetches the full remote json:

Image

Below are text copies of the initial and subsequent result.

Inital result with CamelCase login - MoonHighway:

{
  "login": "MoonHighway",
  "id": 5952087,
  "node_id": "MDEyOk9yZ2FuaXphdGlvbjU5NTIwODc=",
  "avatar_url": "https://avatars.githubusercontent.com/u/5952087?v=4",
  "gravatar_id": "",
  "url": "https://api.github.com/users/MoonHighway",
  "html_url": "https://github.com/MoonHighway",
  "followers_url": "https://api.github.com/users/MoonHighway/followers",
  "following_url": "https://api.github.com/users/MoonHighway/following{/other_user}",
  "gists_url": "https://api.github.com/users/MoonHighway/gists{/gist_id}",
  "starred_url": "https://api.github.com/users/MoonHighway/starred{/owner}{/repo}",
  "subscriptions_url": "https://api.github.com/users/MoonHighway/subscriptions",
  "organizations_url": "https://api.github.com/users/MoonHighway/orgs",
  "repos_url": "https://api.github.com/users/MoonHighway/repos",
  "events_url": "https://api.github.com/users/MoonHighway/events{/privacy}",
  "received_events_url": "https://api.github.com/users/MoonHighway/received_events",
  "type": "Organization",
  "user_view_type": "public",
  "site_admin": false,
  "name": "Moon Highway",
  "company": null,
  "blog": "http://www.moonhighway.com",
  "location": "United States of America",
  "email": "[email protected]",
  "hireable": null,
  "bio": "Web Development classroom training materials.",
  "twitter_username": "moonhighway",
  "public_repos": 88,
  "public_gists": 0,
  "followers": 181,
  "following": 0,
  "created_at": "2013-11-15T23:34:26Z",
  "updated_at": "2025-03-28T17:07:53Z"
}

An example of the stored version with lowercase username moonhighway:

{
  "name": "Moon Highway",
  "login": "moonhighway",
  "avatar_url": "https://avatars.githubusercontent.com/u/5952087?v=4",
  "location": "United States of America"
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions