前端入坑(四)--------react(如何从API获取数据)

Hey! Welcome to day 4.

Hopefully you've had time to follow along and do the exercises from the last few days. And if I've done my job right, you're starting to understand how React works -- maybe even thinking about how to use it in your next project.

But there's one glaring question, and it's one of the first things React newcomers ask:

How do you get data from an API?

A fancy front end is no good without data! So today we're going to tackle that head-on by fetching some real data from Reddit and displaying it with React.

How to Fetch Data

Before we dive in, there's one thing you need to know: React itself doesn’t have any allegiance to any particular way of fetching data. In fact, as far as React is concerned, it doesn’t even know there’s a “server” in the picture at all. React is UI-only, baby.

You've already learned what makes React tick: it's props and state. And that's pretty much it. There is no HTTP library built into React. So the way to make this work is to use React's lifecycle methods to fetch data at the appropriate time, and store the response in the component's state. You can complicate this process with services and data models (er, “build abstractions”) as much as you desire, but ultimately it all boils down to components rendering props and state.

To fetch data from the server, we'll need an HTTP library. There are a ton of them out there. Fetch, Axios, and Superagent are probably the 3 most popular -- and Fetch is actually part of the JS standard library now. My favorite is Axios because of how simple it is, so that's what we'll use today. If you already know and prefer another one, go ahead and use that instead.

Create the Project

Once again we're going to start with a fresh project in CodeSandbox.

1. Go to https://codesandbox.io/s/new
2. Erase everything in index.js
3. Replace it with this:

import React from "react";
import ReactDOM from "react-dom";

class Reddit extends React.Component {
  state = {
    posts: []
  }
  render() {
    return (
      <div>
        <h1>/r/reactjs</h1>
      </div>
    );
  }
}

ReactDOM.render(<Reddit />, document.getElementById("root"));

We're creating a class component called `Reddit` to display posts from the /r/reactjs subreddit. It's not fetching anything yet, though.

By now the code probably looks familiar -- the imports at the top and the render at the bottom are the same as we've written the last few days. We're using a class component because it needs to have state to hold the posts that we fetch from the server (and function components can't hold state, remember). We're also initializing the state with an empty array of `posts`, which will soon be replaced by live data.

Render the List

Next, let's add some code to render the posts we have so far (which is an empty array). Change the `render` function to look like this:

render() {
  return (
    <div>
      <h1>/r/reactjs</h1>
      <ul>
        {this.state.posts.map(post => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
}

Let's talk about what this is doing, because it probably looks a bit weird.

Inside the <ul> the JS expression is wrapped in single braces (because, remember, that's what you gotta do in JSX). `this.state.posts` is the empty array of posts we initialized above, and the `map` function loops over the posts and returns an <li> for each item in the array.

This is how you render a list in React.

Other libraries have a special template syntax, like Angular's "ngFor" or Vue's "v-for", that essentially make a copy of the element for each item in the array.

React, on the other hand, leans on JavaScript to do the heavy lifting. "this.state.posts.map" is not a React thing. That's calling the `map` function that already exists on JS arrays, and it transforms (a.k.a, "maps") each item in the array into a new thing. In this case, each array item is being turned into a JSX <li> element with a `key` prop and the post's title, and the resulting array of <li>'s is what gets rendered inside the <ul>.

Fetch the Data

Let's see it in action with real data. First we'll import Axios, so add this new import at the top:

import axios from 'axios';

Because we haven't installed the library yet, CodeSandbox will throw up an error saying "Could not find dependency: 'axios'" but it also provides a nice "Suggested solution" in the form of a button that says "Add axios as a dependency." Click that button, and the library will be added to the project, and the app will refresh.

Then, above the render() method, type in this code:

componentDidMount() {
  axios.get(`https://www.reddit.com/r/reactjs.json`)
    .then(res => {
      const posts = res.data.data.children.map(obj => obj.data);
      this.setState({ posts });
    });
}

We're using `axios.get` to fetch the data from Reddit's API, which returns a promise, and the `.then` handler will get called once the API call is finished.

Reddit's API returns the posts in a pretty deeply-nested structure, so the `res.data.data.children.map...` is picking out the individual posts from the nested mess.

Finally, it updates the posts in the component state (remember that `this.setState({ posts })` is just ES6 shorthand for `this.setState({ posts: posts })`)

If you want to learn how to reverse-engineer API responses yourself, go to https://www.reddit.com/r/reactjs.json and copy the result into a pretty-printer like http://jsonprettyprint.com/, then look for something you recognize like a "title", and trace back upwards to figure out the JSON path to it. In this case, I looked for the "title", and then discovered posts were at res.data.data.children[1..n].data.

Once that change is made, you'll see the app re-render with real data from /r/reactjs!

If you had trouble getting it working, the full working example is here: https://codesandbox.io/s/84x08lwl09 (but hey! don't just skip the work and click the working code example -- the knowledge really sticks better when you write it out yourself).

Your Turn

Take a few seconds and run through the quick exercises below. It'll help the concepts stick! ?

  • Did you notice that the first thing we did was initialize the state with an empty array of posts? Before we even fetched the data? Try taking out the initialization and watch what happens. Burn the resulting error message into your memory: when you see this again, remember to check if you've initialized state properly, and check for typos.
  • Extend the UI to include more data from the Reddit posts, like their score and the submitting user. Make the title into a link to the actual post.

Tomorrow, we'll finally be free of the sandbox, and we'll finish up by deploying the app to the real live internet.

Cheers,
Dave

P.S. The fastest way to wrap your head around the "React way" of doing things (like rendering lists) is to practice.

It's the best method I know. But you need focused exercises.

My Pure React book comes with 26 exercises to hammer home everything you learn. Y'Eli said "I think your book is amazing. I'm really learning how to use react! I almost gave up several times using other books!"

To make sure you keep getting these emails, please add dave@daveceddia.com to your address book or whitelist us. Want out of the loop? Unsubscribe.

My postal address: P.O. Box 248, Nutting Lake, MA

我的代码实例 :)

import React from "react";
import ReactDOM from "react-dom";
import axios from 'axios';
class Reddit extends React.Component {
  state = {
    posts: []
  };
  componentDidMount(){
    axios.get(`https://www.reddit.com/r/reactjs.json`)
    .then(res =>{
      const posts = res.data.data.children.map(obj => obj.data);
      this.setState({posts})
    })
  }
  render() {
    return (
      <div>
        <h1>/r/reactjs</h1>
        <ul>
          {this.state.posts.map(post =>(
            <li key = {post.id}>{post.title}</li>
          ))}
        </ul>
      </div>
    );
  }
}

ReactDOM.render(<Reddit />, document.getElementById("root"));

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值