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

Axios

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>

)

}

}