GithubHelp home page GithubHelp logo

react-dynamic-data-workshop's Introduction

React with dynamic data workshop for Founders & Coders

The purpose of this workshop is to learn one of the most common usecases for modern frontend frameworks (React in this case). That usecase is fetching some data from an API and then rendering it.

For this workshop we'll use the GitHub API since it's well documented and familiar to most students.

Set up

Start by opening the workshop folder, don't peek into solution just yet!

# clone the repo, ssh or HTTPS, whatever you usually do!
git clone [email protected]:sofiapoh/react-dynamic-data-workshop.git

cd react-dynamic-data-workshop

cd workshop

npm i

# or

yarn

#and finally

npm start

# or

yarn start

You should now see the following message:

Server running at http://localhost:1234
✨  Built in 1.22s.

If you do not see this message make sure you have a version of node which is above 8 and npm or yarn installed. Make sure you remember to install dependencies.

If you're a Linux user you might get a following error when starting the dev server:

events.js:137
throw er; // Unhandled 'error' event^

Running this command should fix it:

echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p

Getting an access token

Next you'll need a GitHub auth token so you won't get rate limited!

Go to: Settings > Developer Settings > Personal access tokens > Generate new token

For this workshop you'll need to select repo and user scopes.

When you get your access token remember to save it somewhere! (But don't put it on GitHub). For example, create a file called token.js (already in the .gitignore) in the root of the workshop folder and put your token there.

export const token = "yourAccessToken";

What we'll be building?

A GitHub user card!

Walktrough

Before you start I'd like you to take a deep breath, you'll get errors through this workshop and they are annoying but they most likely are quite cryptic at start but you'll learn how to read them. If you get stuck try to get someone else to look at your code. Sometimes the bundler is going to be a bit funny with restarting the file watchers on the dev server so if you don't see changes and feel like you should, restart your server.

Mostly: don't fall into despair, it's just code and you got this!

Let's start by breaking our user card into some top level components. Based on our design it looks like our two top level components are going to be:

  1. <UserHeader/> with your Github avatar, name, username and follower count
  2. <RepoList/> With lists of your repositories with their name, descriptions, links and star count.

We'll start with <UserHeader/>

Creating UserHeader

Start by creating a new file for our UserHeader component.

Going back to the design, create the skeleton for how your component will come together, add the elements with some hardcoded values first to check something is rendering. We'll also need to hold the data we'll get back from the api somewhere so let's also create a state value called userData with the initial value of null.

Don't forget to import this component to your <App/> component!

Now let's fill this component with some real user data! I personally like to separate functions that are not directly related to rendering outside the component, into another folder which is commonly called utils, create this new folder inside se src folder of your project and create a file called getUserData.js.

Get the data

Now we'll need a function that gets your github user data. Create a function that makes a request to https://api.github.com/users/{{your username}}?access_token={{your access token}}

You can use any of your preferred method to create an API request but I'll give an example with the fetch API. Don't forget to import the access token you created earlier, it's needed to not get rate limited. Try not to copy paste, you'll learn more if you don't!

import { token } from "../../token";

const checkResponse = response => {
  if (response.status !== 200) {
    console.log(`Error with the request! ${response.status}`);
    return;
  }
  return response.json();
};

export const getUserData = username => {
  return fetch(`https://api.github.com/users/${username}?access_token=${token}`)
    .then(checkResponse)
    .catch(err => {
      throw new Error(`fetch getUserData failed ${err}`);
    });
};

Note: this function returns a Promise

Now that we have our getUserData function, let's import it to our component which will be rendering the data.

Lifecycle and rendering

We've heard about React effects already, they are a place to do side-effects such as fetching data. In our case we'll want to run a data fetching function once the component mounts into the dom. Sounds familiar? Let's create an effect with React.useEffect() where we'll call getUserData.

React.useEffect(() => {
  const username = "sofiapoh";
  getUserData(username).then(data => console.log(data));
}, []);

Don't forget the second argument to useEffect—the empty array. This will tell React to only run the effect once. Without this your component will re-render every time you update your state, which will re-run the effect, which will update state, which will re-render your component, which will re-run the effect... (you don't want to make thousands of requests in a row and get rate limited by Github).

Hopefully you're seeing something in your console by now! That alone is not enough for us to get rendering, we need to set this data in our components userData state variable we defined earlier in order for us to consume it outside the effect.

getUserData(username).then(data => setUserData(data));

Let's take this data and use it to finally render some dynamic content.

Before your components return statement destructure following keys out of userData :

const { avatar_url, html_url, name, followers, repos_url } = userData;

And pass them on your components like so:

return (
  <div>
    <img src={avatar_url} />
    {/*More stuff here!*/}
  </div>
);

Now you might be seeing something! If not, that's fine, you've followed instructions correctly.

When the data isn't there

You might be seeing something like this now:

Uncaught TypeError: Cannot read property 'avatar_url' of null

Usually data over network gets to us slower than DOM renders content, this is when we need to provide a loading state so we're not trying to render content that is not there yet. In our case we'll just add a small safeguard before we destructure:

if (!userData) {
  return <h3>...Loading</h3>;
}

This will also help you to avoid UI errors and provide helpful error messages to your user. Note that unless the payload of the made request is particularly heavy you might want to skip loading state and just return null to defer rendering until the content is ready. This way you won't get a janky looking flash of loading state before the component finishes loading.

If you feel fairly comfortable continuing ahead I'd like you to take some time to style your <UserHeader/> component to roughly match the design. You can use regular css, just create a file (a common convention is to name the css file to match the js filename) and import it at the top of your file:

import "./userHeader.css";

This import is handled by your bundler (Parcel in this case) and doesn't transform CSS into Javascript.

Note: in jsx attributes are camelCase and some are slightly different, for example: class => className.

What's next?

Hopefully you'll now have a nice looking header component for our Github user card! Next we'll tackle creating a list of your repositories.

You might have noticed from the response we got from getUserData didn't include your repositories so we'll create another stateful component which fetches it's own data. We did however get a repos_url from the response which we'll pass down as a prop to our new component <RepoList/>.

Creating your repo list component

Your next steps are:

  1. Create a new file for your <RepoList/> component.
  2. Add some mock data to the component
  3. Import it into the UserHeader file.
  4. Refactor your getUserData function to take a url as an argument so you can use it on both of the components.
  5. Fetch data in the <RepoList/> component with useEffect similarly to before.

There is a small gotcha! If you try to render <RepoList/> before the parent component has repos_url you're going to run into errors. One way to handle this is to use a ternary statement to render the component only when the data is fully loaded:

return (
  <div>
    <img src={avatar_url} />
    <h2>{name}</h2>
    {repos_url ? <RepoList url={repos_url} /> : null} // Could render a loading
    component here instead
  </div>
);

When your <RepoList/> is rendering correctly we can start to render some real data. You've probably noticed the data we have is an Array. A very common pattern is to create a functional component for the data that you want to render and then map over your data dynamically creating a list of the components. That's what we'll do next:

// new file: repo.js for rendering a single repository

const Repo = ({ name, stargazers_count, description, html_url }) => {
  return <li>{"Your jsx here!"}</li>;
};

export default Repo;

Don't forget to import your Repo component into the RepoList!

// In RepoList
return (
  <ul>
    {repos.map(repo => (
      <Repo key={repo.id} {...repo} />
    ))}
  </ul>
);

All dynamically rendered components like our Repo here need a key prop so React can keep track of the correct elements being added/removed from the DOM.

When you have a list of repos rendering with the data which matches the designs, add some styles! CSS is underrated!

Great job! ✨

Stretch goals

  • Refactor the children of both stateful components into separate files and pass the state values down to them
  • More CSS
  • Create a form input to dynamically create a Github user card from any username

Feedback? Improvements? Clarification?

Create a pull request or an issue!

react-dynamic-data-workshop's People

Contributors

oliverjam avatar sofiapoh avatar sarahyjja avatar

Watchers

James Cloos avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.