如何处理在渲染组件后出现axios响应的情况?

时间:2019-06-16 07:13:55

标签: reactjs promise axios

我正在用rest框架和reactjs创建一个博客应用程序。在主页上的componentDidMount下,我使用axios发送API调用以获取所有文章和文章的setState返回。正如我所研究的那样,如果未为特定组件获取API,则axios会基于promise的想法,使代码不会继续进行。如果我错了,请告诉我。

然后,我发送GET调用以获取作者的姓名,该作者由ID撰写文章。虽然,我以为axios是可以实现的。但是,它不能那样工作。现在,我不确定该如何前进。

这是一个代码段。因此,在mainBody.js中,我将api调用为:

class MainBody extends Component {
state = {};

componentDidMount () {
    this.get_all_articles();
  };

get_writer_name (id) {
    let authstr = 'Bearer ' + window.localStorage.token;
    let writer_url = "http://localhost:8000/api/writer/" + id.toString() + "/";
    axios.get(writer_url, { headers: { Authorization: authstr }})
      .then(response => {
        console.log(response.data['name'])
        return response.data['name'];
      })
      .catch(err => {
        console.log("Got error")
      })
  };

get_all_articles () {
    let authstr = 'Bearer ' + window.localStorage.token;
    axios.get("http://localhost:8000/api/articles/", { headers: { Authorization: authstr }})
      .then(response => {
        this.setState({articles: response.data});
      })
      .catch(err => {
        console.log("Got error")
      })
  }

render () {
return (
{this.state.articles.map((article, key) =>
                          <ArticleView key={article.id} article={article} writer_name={this.get_writer_name(article.created_by)} />
                        )}
)
}
}

在articleview2中,我将打印每篇文章中存在的所有数据以及作者的姓名。

我的articleview类是:

class ArticleView extends Component {
state = {article: this.props.article};
componentDidMount() {
console.log(this.props.writer_name;
}
render () {
return (
<React.Fragment>
<h2>{article.title}</h2>
<p>{article.body}</p>
<span>{this.props.writer_name}</span>
</React.Fragment>
)
}
}

如果仔细观察,我写了两个console.log语句来获取作者名称。根据该顺序,首先运行articleview类中存在的控制台日志(未定义),然后从API调用中获取数据,然后运行控制台日志,该控制台日志将返回正确的编写器名称。

我想知道,错误在哪里?另外,正如我注意到的那样,对于所有列出的文章,进行了太多的API调用以多次获得作者的名字。这些案例的行业最佳做法是什么?

2 个答案:

答案 0 :(得分:1)

由于您的API调用有很多共同点,因此您应该首先设置一个axios实例,以重新使用这些通用功能:

const api = axios.create({
  baseURL: 'http://localhost:8000/api/',
  headers: { Authorization: `Bearer ${localStorage.token}` }
});

现在,由于您的MainBody需要异步从API提取资源,因此将有很短的时间无法使用数据。有两种方法可以处理此问题。 MainBody可能负责进行所有调用,或者仅负责进行调用以获取所有文章,然后每个ArticleView组件都可以负责获取作者的姓名。我将在下面演示第一种方法:

class MainBody extends Component {
  state = { articles: null, error: null, isLoading: true };

  async componentDidMount () {
    try {
      const response = await api.get('articles/');
      const articles = await Promise.all(
        response.data.map(async article => {
          const response = await api.get(`writer/${article.created_by}/`);
          return { ...article, writer_name: response.data.name };
        })
      );

      this.setState({ articles, isLoading: false });
    } catch (error) {
      this.setState({ error, isLoading: false });
    }
  }

  render () {
    const { articles, error, isLoading } = this.state;

    return isLoading ? 'Loading...' : error
      ? `Error ${error.message}`
      : articles.map(article => (
        <ArticleView
          key={article.id}
          article={article}
          writer_name={article.writer_name}
        />
      )
    );
  }
}

答案 1 :(得分:0)

  

我想知道错误在哪里。

在编写this.state.articles.map()时,意味着您正在使用Array文章的属性映射,这些属性映射在获取数据之前可能是不确定的,这将导致错误Cannot read property map of undefined

  

解决方案

现在,由于API请求是异步的,这意味着render方法将不等待数据来临。因此,您可以做的是在状态中使用loader变量,并在发出请求时将其设置为true,然后在响应到来时将其设置为false,并在this.state时在render中显示加载器。 loader为true,如果为false,则显示文章。
或者,您也可以使用不会引起错误的空数组初始化this.state.articles。

  

此外,正如我注意到的那样,对于所有列出的文章,进行了太多的API调用,无法多次获得作者的名字。这些案例的行业最佳做法是什么?

在循环中发出API请求是非常糟糕的做法。一旦在公司做完,甚至我自己都会被责骂。

  

解决方案

您已经告诉后端工程师为您提供过滤器,以便在文章的每个对象中包括作者的姓名。我们在后端使用Loopback,它提供了一个过滤器,用于在内部将相关模型包括在每个对象中。