REST
React連接REST API
2020/07/15 (增加連結)
2020/07/21 (更新內容)
React
React的元件有兩種形式,第一種是以Class Component的方式開發,最近,由於React Hooks,也讓Functional Component的開發方式成為另一種選項。建議初學者先了解Functional Component的開發方式,因為Functional Component的使用比較簡潔,如果要了解運作原理,可以再深入了解Class Component的使用。
React連接REST API的方式也不少,我們推薦Axios。由於連接REST API是非同步的呼叫方式,在Class Component的範例中,我們使用的是Promise的呼叫方式,在Functional Component的範例中,我們使用的是async/await的呼叫方式。
Functional Component
先新增一個新的元件
src/person/PersonList.js
import React from 'react';
export default function PersonList() {
return (
<ul>
<li>Ben</li>
<li>Mary</li>
<li>Lisa</li>
</ul>
)
}
首先,延續Router & Material UI 的結果,加入新的元件。將src/index.js改成:
import React from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter, Switch, Route} from 'react-router-dom';
import { createMuiTheme } from '@material-ui/core/styles';
import { ThemeProvider } from '@material-ui/styles';
import './index.css';
//import App from './App';
import ReactApp from './ReactApp.js';
import EmployeeList from './employee/EmployeeList.js';
import ProductList from './product/ProductList.js';
import PersonList from './person/PersonList.js';
import * as serviceWorker from './serviceWorker';
const theme = createMuiTheme({
palette: {
primary: {
//main: '#f44336',
main: '#1f618d',
},
},
});
ReactDOM.render(
<ThemeProvider theme={theme}>
<React.StrictMode>
<BrowserRouter>
<Switch>
<Route path="/product" component={ProductList}/>
<Route path="/employee" component={EmployeeList}/>
<Route path="/person" component={PersonList}/>
<Route path="/" component={ReactApp}/>
</Switch>
</BrowserRouter>
</React.StrictMode>
</ThemeProvider>
, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.unregister();
也要修改一下src/AppMenu.js
import React from 'react';
import {Link} from 'react-router-dom';
import AppBar from '@material-ui/core/AppBar';
import Button from '@material-ui/core/Button';
import Toolbar from '@material-ui/core/Toolbar';
export default function AppMenu() {
return (
<AppBar position="sticky">
<Toolbar>
MyApp
<Button component={Link} to='/product' color="inherit">product</Button>
<Button component={Link} to='/employee' color="inherit">employee</Button>
<Button component={Link} to='/person' color="inherit">person</Button>
</Toolbar>
</AppBar>
)
}
React + REST / Functional Component
我們來試試看如何在react裡存取線上的restful web services (詳參: Using Axios with React),先將要顯示的內容放在State裡。
useState()
如果我們想要把內容儲存在persons,利用react hooks的useState()。(詳參: 新一代 React API — React Hooks ),在return()中再去利用javascript的map (詳參: Lists and Keys)取得persons的內容,以list的方式展現在網頁上。
src/person/PersonList.js
import React, {useState} from 'react';
export default function PersonList() {
const [persons] = useState(['Ben','Mary','Lisa']);
return (
<ul>
{ persons.map(person => <li key={person}>{person}</li>)}
</ul>
)
}
useEffect()
如果我們要去讀一個API ( https://jsonplaceholder.typicode.com/users),API會回傳 (節錄部分內容):
[
{
"id": 1,
"name": "Leanne Graham",
"username": "Bret",
"email": "Sincere@april.biz",
"address": {
"street": "Kulas Light",
"suite": "Apt. 556",
"city": "Gwenborough",
"zipcode": "92998-3874",
"geo": {
"lat": "-37.3159",
"lng": "81.1496"
}
},
"phone": "1-770-736-8031 x56442",
"website": "hildegard.org",
"company": {
"name": "Romaguera-Crona",
"catchPhrase": "Multi-layered client-server neural-net",
"bs": "harness real-time e-markets"
}
}
]
在useEffect裡,先產生一個非同步(async)的函數: fetchData(),useEffect的第二個參數是個空陣列,這個空陣列很重要,有了空陣列,useEffect只執行一次,缺了空陣列,內容只要被更動就會執行useEffect一次 (詳參: React | 為了與 Hooks 相遇 - Function Components 升級記)。
useEffect(() => {
async function fetchData () {
}
fetchData();
},[]);
利用axios的get去呼叫API (詳參: Using Axios with React),因為要等待API資料回傳,所以,在axios.get前,加了await,在await後的指令(setPersons)就會等待資料回傳後才會繼續執行。(詳參: 鐵人賽:JavaScript Await 與 Async)
useEffect(() => {
async function fetchData () {
const result = await axios.get(`https://jsonplaceholder.typicode.com/users/`);
setPersons(result.data);
}
fetchData();
},[]);
使用axios前要先安裝:
npm install axios
取得資料之後,呼叫setPersons()去更新persons的內容。
useEffect(() => {
async function fetchData () {
const result = await axios.get(`https://jsonplaceholder.typicode.com/users/`);
setPersons(result.data);
}
fetchData();
},[]);
完整的src/person/PersonList.js
import React, {useState,useEffect} from 'react';
import axios from 'axios';
export default function PersonList() {
const [persons, setPersons] = useState([]);
useEffect(() => {
async function fetchData () {
const result = await axios.get(`https://jsonplaceholder.typicode.com/users/`);
setPersons(result.data);
}
fetchData();
},[]);
return (
<ul>
{ persons.map(person => <li key={person.name}>{person.name}</li>)}
</ul>
)
}
本範例以react hooks改寫
**注意 ** react hook是16.8之後的新功能,如果package.json裡 react及react-dom的版本太舊,需要更新版本並執行npm install
{
"name": "test-react",
"version": "0.1.0",
"private": true,
"dependencies": {
"@material-ui/core": "^4.2.1",
"@material-ui/icons": "^4.2.1",
"airtable": "^0.7.0",
"axios": "^0.18.0",
"bootstrap": "^4.2.1",
"jquery": "^3.3.1",
"popper.js": "^1.14.6",
"react": "^16.8.0",
"react-dom": "^16.8.0",
"react-router-dom": "^5.0.1",
"react-scripts": "2.1.2",
"reactstrap": "^7.0.2"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"proxy": "http://localhost:8080/",
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}
參考資料
React hooks
How to fetch data with React Hooks?
Data Fetching with React Hooks
How to trigger a hook programmatically/manually?
Loading Indicator with React Hooks
Error Handling with React Hooks
Fetching Data with Forms and React
Custom Data Fetching Hook
Reducer Hook for Data Fetching
Abort Data Fetching in Effect Hook
Axios
將axios的使用方法改為async/await
async/await
axios + async/await
PostMan
React + REST / Class Component
2019/07/15
我們來試試看如何在react裡存取線上的restful web services (詳參: Using Axios with React),首先,將index.js改成
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import PersonList from './PersonList';
//import App from './App';
//import registerServiceWorker from './registerServiceWorker'; //react 2.0
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<PersonList />, document.getElementById('root'));
//registerServiceWorker(); //react 2.0
serviceWorker.unregister();
State & Axios
這裡會使用到一個新的類別,PersonList (儲存為PersonList.js),首先,先利用Component的state ( 詳參: Component State)物件,去設定會有一個persons的陣列。.除了render()以外,Component裡有另一個方法componentDidMount(),可以在componentDidMount()裡,利用axios的get去呼叫web services(詳參: Using Axios with React),然後在得到資料之後,將資料利用setState放到剛剛設定的persons陣列。然後,在render()中再去利用javascript的map (詳參: Lists and Keys)取得persons的內容,以list的方式展現在網頁上。
import React from 'react';
import axios from 'axios';
export default class PersonList extends React.Component {
state = {
persons: []
}
componentDidMount() {
axios.get(`https://jsonplaceholder.typicode.com/users`)
.then(res => {
const persons = res.data;
this.setState({ persons });
})
}
render() {
return (
<ul>
{ this.state.persons.map(person => <li>{person.name}</li>)}
</ul>
)
}
}
你會發現,只要儲存檔案,就會重新編譯程式,重新編譯程式之後,會看到:
Failed to compile.
./src/PersonList.js
Module not found: Can't resolve 'axios' in 'C:\Users\USER\test-cra\src'
這是因為沒有安裝axios的套件,要安裝axios就要,另外再開一個終端機,並在終端機下執行下這個指令:
npm install axios
跟Using Axios with React的內容有點不同,因為在npm 5.0.0之後,不需要加"--save"選項。
執行之後,就可以在瀏覽器中看到所有的使用者。不過,如果打開chrome的開發人員工具,會在看到一個錯誤訊息:
Warning: Each child in an array or iterator should have a unique "key" prop.
把key(詳參: Lists and Keys)加上去就可以了!
import React from 'react';
import axios from 'axios';
export default class PersonList extends React.Component {
state = {
persons: []
}
componentDidMount() {
axios.get(`https://jsonplaceholder.typicode.com/users`)
.then(res => {
const persons = res.data;
this.setState({ persons });
})
}
render() {
return (
<ul>
{ this.state.persons.map(person => <li key={person.name}>{person.name}</li>)}
</ul>
)
}
}