Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Render search results #28

Open
1 of 3 tasks
pete-murphy opened this issue Jul 18, 2019 · 8 comments
Open
1 of 3 tasks

Render search results #28

pete-murphy opened this issue Jul 18, 2019 · 8 comments

Comments

@pete-murphy
Copy link
Member

pete-murphy commented Jul 18, 2019

  • Construct GraphQL query from form inputs
  • Add GitHub API key to env variables
  • Render first page of results
@VinceGrilli
Copy link
Contributor

I'm going to start working on some Queries next

@pete-murphy
Copy link
Member Author

pete-murphy commented Jul 21, 2019

I started something here if you want to build off that: #30
Let me know if you have questions

@pete-murphy
Copy link
Member Author

So thinking this through, haven't had time to try implementing any of it, but the flow of data will be a little tricky/complicated.

Step 1 (JS object)

From the form page (the Home view) we'll end up with an object looking like:

{
  keywords: "react",
  labels: ["good first issue", "help wanted"],
  language: "JavaScript"
}

These are the relevant bits of our state. Each of these fields could possibly be empty: keywords might be an empty string, selectedLabels might be an empty array, and selectedLanguage might be Any. I can't remember off the top of my head how the GitHub search API parses the search string (i.e., what is considered a valid string for the query variable on the GraphQL query), but I think that each of those empty states will have to be handled as special cases, and it might fail in the case where all three are empty.

Step 2 (query string)

In any case, we'll want to take that JS object, and convert it to a valid query string, so the above would become:

"keywords=react&labels[]=\"good first issue\"&labels[]=\"help wanted\"&language=JavaScript"

(or something like that, there doesn't really seem to be a strict standard around how this gets formatted). That query string gets URI encoded and tagged on the end of the URL when we navigate to the /results route on form submit.

Step 3 (back to JS object and to GraphQL query)

The Results component will be responsible for de-serializing/parsing this string into JS object, and construct a valid search string from that object. (It will actually be an array of search strings, since the GitHub API doesn't have a way of searching for possible sets of multiple labels.) At that point we'll be ready to kick off the GitHub API request.

Steps 1 & 2 (serializing/deserializing between JS objects to query strings) is a solved problem, so I think we'd be fine going with the query-string library to help with this (though we could implement our own version for practice). I have something for Step 3 here. The tricky part will be wiring this all up in the right way. I'll add more info when I have a chance.

@VinceGrilli
Copy link
Contributor

Cool. Yeah I'm definitely a little lost when it comes to our custom state management and how that all gets passed around. I've been spending some time going through the custom hooks tutorial on Level Up Tuts. It's helping as I look over it all again this morning but it's not all clicking yet. Everything you wrote above makes sense but I wouldn't know where to start wiring it all up. So I'm just going to keep plugging away and breaking down the pieces here until I can follow it all without getting lost

@pete-murphy
Copy link
Member Author

pete-murphy commented Jul 28, 2019

No worries, I'll see if I can add some documentation on how to use the state management (I'm a little fuzzy on how I set it up myself). But from what I remember I don't think we'll really need to add any more state, though we will need to do the action creator/action/dispatch dance. A rough draft at hooking it all up without doing it in top level state would just be to do it in SearchForm.js, and just pass the final form "state" as data in the URL query string on form submit. It could look something like

// in SearchForm.js
import { useSearch } from "App";  // This gives access to our context where state is stored
import { stringify } from "query-string";
import { navigate } from "@reach/router";

...

// This is a little weird looking, but it's how we have our state management set up.
// The hook returns search state as first element of array and a `dispatch` function
// as second element. Not using `dispatch` here so we're just grabbing what we need
// from first element.
const [{ keywords, selectedLabels, selectedLanguage }] = useSearch();

const handleSubmit = e => {
  e.preventDefault();
  const str = stringify({
    keywords,
    labels: selectedLabels,
    language: selectedLanguage
  });
  navigate(`/results?${str}`); // Our state can now be read from the query string
};

Then inside of Results we can read the URL query string. (This approach, as opposed to passing around as React state, gives us state persistence on browser refresh and shareable links.)

I haven't tested the above, but just sketching it out so you have a better idea of how state management is wired up. The more proper way to do it might be to create actions and dispatch them, but that action would need a reference to navigate and I don't remember offhand a good way of doing that.

Edit: just realized I had a ResultsProvider context in App.js, but if we go with the approach outlined above, we really won’t need that (that state will be handled by the query string and Apollo client)

@VinceGrilli
Copy link
Contributor

So after a very crazy and busy week I'm finally diving back in here. It's crazy how much I can forget in a week 😬 Anyway I just branched off to mess around with this and see if I could get it working but I'm running into this error: "ReferenceError: can't access lexical declaration `useSearch' before initialization" which after some googling seems to be a "circular dependency" issue but I'm not sure why I can't access useSearch in out SeachForm component. It all looks like its in order to me. It could have to do with a missing value prop in the SeachProvider, but I'm not totally sure.
Anyway, no pressure to jump on this, I know its a busy time. I just ran out of time tonight and wanted to give an update. I'll take another crack at it tomorrow night

@pete-murphy
Copy link
Member Author

pete-murphy commented Aug 6, 2019

Ah, right, that hadn't occurred to me. I was throwing together this state management solution kinda willy-nilly without thinking it through too much but there would indeed be a circular dependency problem if we're importing SearchForm from SearchForm.jsApp.js and then importing useSearch from App.jsSearchForm.js. I'll try to think of another solution, though I guess we could always just use Redux proper or something like this?
A quick fix might be to move the context provider from App.js to index.js, I think that would take care of the circular dependency if I'm thinking about it correctly.

I've also had a hectic past few weeks, not sure when I'll get much time to put towards this, but I'll probably pick away at a few things here and there.

Edit: Actually, I think this line might be the problem:

const [{ keywords, selectedLabels, selectedLanguage }] = useSearch();

This works for me if I move this down a few lines, so that it's inside the SearchForm component. Its a hook so it should be called inside the component, normally React gives better error messages about that so this was a bit strange. Let me know if there are further issues.

@VinceGrilli
Copy link
Contributor

Of course! That makes sense thanks man. I'll keep plugging away and see how far I can get tonight. And no worries on the time. It's kinda nice to have something I can just chip away at. And that medium link was real helpful

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

No branches or pull requests

2 participants