在 react js 中进行 API 调用的正确方法是啥?
Posted
技术标签:
【中文标题】在 react js 中进行 API 调用的正确方法是啥?【英文标题】:what is right way to do API call in react js?在 react js 中进行 API 调用的正确方法是什么? 【发布时间】:2016-12-09 02:25:11 【问题描述】:我最近从 Angular 转到了 ReactJs。我正在使用 jQuery 进行 API 调用。我有一个 API,它返回要打印在列表中的随机用户列表。
我不确定如何编写我的 API 调用。这方面的最佳做法是什么?
我尝试了以下方法,但没有得到任何输出。如有必要,我愿意实施替代 API 库。
下面是我的代码:
import React from 'react';
export default class UserList extends React.Component
constructor(props)
super(props);
this.state =
person: []
;
UserList()
return $.getJSON('https://randomuser.me/api/')
.then(function(data)
return data.results;
);
render()
this.UserList().then(function(res)
this.state = person: res;
);
return (
<div id="layout-content" className="layout-content-wrapper">
<div className="panel-list">
this.state.person.map((item, i) =>
return(
<h1>item.name.first</h1>
<span>item.cell, item.email</span>
)
)
<div>
</div>
)
【问题讨论】:
我取决于你使用的是什么状态管理库。如果您没有使用任何,您可以将您的 api 调用移动到单独的文件,并在componentDidMount
回调中调用您的情况下的 api 函数。
如果你只用jQuery做Ajax请求,可以用fetch()
代替jQuery。
为什么要使用 Jquery? jquery 是一个庞大的库,没有必要
只是在这里添加,目前useEffect
可能是现在放置 api 调用的地方。见btholt.github.io/complete-intro-to-react-v5/effects
【参考方案1】:
这种情况下,你可以在componentDidMount
里面做ajax调用,然后更新state
export default class UserList extends React.Component
constructor(props)
super(props);
this.state = person: [];
componentDidMount()
this.UserList();
UserList()
$.getJSON('https://randomuser.me/api/')
.then(( results ) => this.setState( person: results ));
render()
const persons = this.state.person.map((item, i) => (
<div>
<h1> item.name.first </h1>
<span> item.cell , item.email </span>
</div>
));
return (
<div id="layout-content" className="layout-content-wrapper">
<div className="panel-list"> persons </div>
</div>
);
【讨论】:
它成功了,谢谢.. 你能建议我“哪个是更好的状态管理的最佳库” @Raj Rj 这些天我认为是 Redux Redux 在当今比较流行,它的风格来自函数式编程。如果你来自 OOP 风格,Mobx (mobxjs.github.io/mobx) 是一个优秀的状态管理库,它可以让你专注于编写业务代码并最终减少你的样板代码【参考方案2】:我想让你看看 redux http://redux.js.org/index.html
他们有非常明确的方式来处理异步调用,即 API 调用,而不是使用 jQuery 进行 API 调用,我想推荐使用 fetch 或 request npm软件包,现代浏览器目前支持 fetch,但服务器端也可以使用 shim。
还有另一个很棒的包superagent,它在发出 API 请求时有很多选择,而且非常易于使用。
【讨论】:
【参考方案3】:您可能想查看Flux Architecture。我还建议查看React-Redux Implementation。将您的 api 调用放入您的操作中。比全部放在组件里要干净得多。
操作是一种帮助方法,您可以调用它来更改应用程序状态或进行 api 调用。
【讨论】:
特罗珀 谢谢你。那么,我应该将与 API 相关的调用保存在单独的文件中吗?以及如何在我的“组件类”中调用它们?我应该遵循什么文件夹结构?最佳做法是什么? P.S.-我是新手,所以问这个基本问题。 在redux实现中,action方法被注入到组件中。这些方法现在将成为您可以调用的组件的道具。您可以查看react-redux-starter-kit 的结构。【参考方案4】:render 函数应该是纯的,也就是说它只使用 state 和 props 来渲染,从不尝试修改 render 中的 state,这通常会导致丑陋的 bug 并显着降低性能。如果您在 React 应用程序中分离数据获取和渲染关注点,这也是一个好点。我建议你阅读这篇文章,它很好地解释了这个想法。 https://medium.com/@learnreact/container-components-c0e67432e005#.sfydn87nm
【讨论】:
【参考方案5】:这个讨论已经有一段时间了,@Alexander T. 的回答为像我这样的新 React 提供了一个很好的指导。并且我将分享一些关于多次调用同一个 API 以刷新组件的额外知识,我认为这可能是初学者的常见问题。
componentWillReceiveProps(nextProps)
,来自official documentation:
如果您需要更新状态以响应道具更改(例如 例如,要重置它),您可以比较 this.props 和 nextProps 和 在此方法中使用 this.setState() 执行状态转换。
我们可以得出结论,这里是我们处理来自父组件的 props、进行 API 调用和更新状态的地方。
基于@Alexander T. 的示例:
export default class UserList extends React.Component
constructor(props)
super(props);
this.state = person: [];
componentDidMount()
//For our first load.
this.UserList(this.props.group); //maybe something like "groupOne"
componentWillReceiveProps(nextProps)
// Assuming parameter comes from url.
// let group = window.location.toString().split("/")[*indexParameterLocated*];
// this.UserList(group);
// Assuming parameter comes from props that from parent component.
let group = nextProps.group; // Maybe something like "groupTwo"
this.UserList(group);
UserList(group)
$.getJSON('https://randomuser.me/api/' + group)
.then(( results ) => this.setState( person: results ));
render()
return (...)
更新
componentWillReceiveProps()
将被弃用。
这里只有一些方法(全部在Doc中)在生命周期中我认为它们与一般情况下部署API有关:
参考上图:
将 API 部署到 componentDidMount()
此处调用 API 的正确场景是该组件的内容(来自 API 的响应)将是静态的,componentDidMount()
仅在组件挂载时触发一次,即使是从父组件传递的新道具或有行动带领re-rendering
。
该组件会检查差异以重新渲染,但不检查重新安装。
引用doc:
如果您需要从远程端点加载数据,这是一个很好的地方 实例化网络请求。
将 API 部署到
static getDerivedStateFromProps(nextProps, prevState)
我们应该注意到有两种组件更新,setState()
在当前组件中会不会触发这个方法,而是重新父组件的渲染或新道具会。
我们可以发现这个方法在挂载时也会触发。
这是部署 API 的合适位置如果我们想使用当前组件作为模板,并且进行 API 调用的新参数是来自父组件的 props。
我们收到来自 API 的不同响应,并在此处返回一个新的state
以更改此组件的内容。
例如: 我们在父组件中有一个不同汽车的下拉列表,该组件需要显示所选汽车的详细信息。
将 API 部署到
componentDidUpdate(prevProps, prevState)
与static getDerivedStateFromProps()
不同,此方法在每次渲染后立即调用,初始渲染除外。我们可以在一个组件中进行 API 调用和渲染差异。
扩展前面的例子:
显示汽车详细信息的组件可能包含该汽车的系列列表,如果我们要查看 2013 年生产的系列,我们可以单击或选择或...列表项引导第一个 setState()
以反映此行为(例如在此组件中突出显示列表项),并在下面的componentDidUpdate()
中发送带有新参数(状态)的请求。得到响应后,我们再次setState()
用于渲染汽车细节的不同内容。为了防止后面的componentDidUpdate()
引起无限循环,我们需要在这个方法的开头使用prevState
比较状态,来决定是否发送API并渲染新的内容。
这个方法确实可以像static getDerivedStateFromProps()
带props一样使用,但是需要通过prevProps
来处理props
的变化。并且我们需要配合componentDidMount()
来处理初始的API调用。
引用doc:
...这也是一个做网络请求的好地方,只要你 比较当前的道具和以前的道具...
【讨论】:
【参考方案6】:React v16 文档中的这一部分将回答您的问题,请继续阅读有关 componentDidMount() 的内容:
componentDidMount()
componentDidMount() 在组件被调用后立即调用 安装。需要 DOM 节点的初始化应该放在这里。如果你 需要从远程端点加载数据,这是一个很好的地方 实例化网络请求。这种方法是设置的好地方 任何订阅。如果您这样做,请不要忘记取消订阅 组件WillUnmount()。
如您所见,componentDidMount 被认为是进行 api 调用 的最佳位置和循环,同时访问节点,这意味着此时进行调用是安全的,更新视图或在文档准备好时您可以做的任何事情,如果您使用的是 jQuery,它应该以某种方式提醒您 document.ready() 函数,您可以确保一切准备就绪,可以在代码中执行任何操作。 ..
【讨论】:
【参考方案7】:使用componentDidMount
中的fetch
方法更新状态:
componentDidMount()
fetch('https://randomuser.me/api/')
.then(( results ) => this.setState( person: results ));
【讨论】:
【参考方案8】:1) 您可以使用 Fetch API 从 Endd Points 获取数据:
为用户获取所有Github
repose 的示例
/* Fetch GitHub Repos */
fetchData = () =>
//show progress bar
this.setState( isLoading: true );
//fetch repos
fetch(`https://api.github.com/users/hiteshsahu/repos`)
.then(response => response.json())
.then(data =>
if (Array.isArray(data))
console.log(JSON.stringify(data));
this.setState( repos: data ,
isLoading: false);
else
this.setState( repos: [],
isLoading: false
);
);
;
2) 其他选择是 Axios
使用 axios 可以省去传递结果的中间步骤 对 .json() 方法的 http 请求。 axios 只是返回数据 您期望的对象。
import axios from "axios";
/* Fetch GitHub Repos */
fetchDataWithAxios = () =>
//show progress bar
this.setState( isLoading: true );
// fetch repos with axios
axios
.get(`https://api.github.com/users/hiteshsahu/repos`)
.then(result =>
console.log(result);
this.setState(
repos: result.data,
isLoading: false
);
)
.catch(error =>
this.setState(
error,
isLoading: false
)
);
现在您可以选择使用componentDidMount
中的任何一种策略来获取数据
class App extends React.Component
state =
repos: [],
isLoading: false
;
componentDidMount()
this.fetchData ();
同时您可以在数据加载时显示进度条
this.state.isLoading && <LinearProgress />
【讨论】:
我使用 axios 工作正常【参考方案9】:一种简洁的方法是使用 try/catch 函数在 componentDidMount 内部进行异步 API 调用。
当我们调用 API 时,我们会收到响应。然后我们对其应用 JSON 方法,将响应转换为 javascript 对象。然后我们从该响应对象中仅获取名为“results”(data.results)的子对象。
一开始我们将 state 中的“userList”定义为一个空数组。一旦我们进行 API 调用并从该 API 接收数据,我们就会使用 setState 方法 将“结果”分配给 userList。
在渲染函数中,我们告诉 userList 将来自 state。由于 userList 是一个对象数组,我们通过它进行映射,以显示每个对象“用户”的图片、姓名和电话号码。为了检索这些信息,我们使用点表示法(例如 user.phone)。
注意:根据您的 API,您的响应可能会有所不同。 Console.log 整个“响应”以查看您需要从中获取哪些变量,然后在 setState 中分配它们。
用户列表.js
import React, Component from "react";
export default class UserList extends Component
state =
userList: [], // list is empty in the beginning
error: false
;
componentDidMount()
this.getUserList(); // function call
getUserList = async () =>
try //try to get data
const response = await fetch("https://randomuser.me/api/");
if (response.ok) // ckeck if status code is 200
const data = await response.json();
this.setState( userList: data.results);
else this.setState( error: true )
catch (e) //code will jump here if there is a network problem
this.setState( error: true );
;
render()
const userList, error = this.state
return (
<div>
userList.length > 0 && userList.map(user => (
<div key=user>
<img src=user.picture.medium />
<div>
<div>user.name.firstuser.name.last</div>
<div>user.phone</div>
<div>user.email</div>
</div>
</div>
))
error && <div>Sorry, can not display the data</div>
</div>
)
【讨论】:
【参考方案10】:外部 API 调用的最佳位置和实践是 React 生命周期方法 componentDidMount(),在执行 API 调用后,您应该更新本地状态以触发 new render( ) 方法调用,那么更新后的本地状态的变化将应用到组件视图上。
作为 React 中 initial 外部数据源调用的其他选项,指向类的 constructor() 方法。构造函数是组件对象实例初始化时执行的第一个方法。您可以在 Higher-Order Components 的文档示例中看到这种方法。
方法 componentWillMount() 和 UNSAFE_componentWillMount() 不应用于外部 API 调用,因为它们打算被弃用。 Here 你可以看到常见的原因,为什么这个方法会被弃用。
无论如何,您必须永远不要使用 render() 方法或直接从 render() 调用的方法作为外部 API 调用的点。如果您这样做,您的应用程序将被阻止。
【讨论】:
【参考方案11】:你也可以在你的函数组件中fetch data with hooks
完整的 api 调用示例:https://codesandbox.io/s/jvvkoo8pq3
第二个例子:https://jsfiddle.net/bradcypert/jhrt40yv/6/
const Repos = (user) =>
const [repos, setRepos] = React.useState([]);
React.useEffect(() =>
const fetchData = async () =>
const response = await axios.get(`https://api.github.com/users/$user/repos`);
setRepos(response.data);
fetchData();
, []);
return (
<div>
repos.map(repo =>
<div key=repo.id>repo.name</div>
)
</div>
);
ReactDOM.render(<Repos user="bradcypert" />, document.querySelector("#app"))
【讨论】:
【参考方案12】:对于支持取消、拦截器等的 api 请求使用 axios 会很棒。与 axios 一起,我使用 react-redux 进行状态管理,使用 redux-saga/redux-thunk 进行副作用。
【讨论】:
虽然这不是错误的,因为使用 axios 和 redux 是获取数据和管理状态的有效方法,但它并不能真正回答问题,它更接近于评论。【参考方案13】:作为对 Oleksandr T. 出色答案的补充/更新:
如果您使用类组件,后端调用应该发生在componentDidMount
。
如果您改用 hooks,则应使用 effect hook
例如:
import React, useState, useEffect from 'react';
useEffect(() =>
fetchDataFromBackend();
, []);
// define fetchDataFromBackend() as usual, using Fetch API or similar;
// the result will typically be stored as component state
进一步阅读:
Using the Effect Hook 在官方文档中。 How to fetch data with React Hooks? by Robin Wieruch【讨论】:
以上是关于在 react js 中进行 API 调用的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
无法调用 React JS webpack-dev-server api