# [3]Django Rest API + React.js + Redux + webpack 前後端整合
###### tags: `python` `Django` `Django Rest framework` `React.js` `redux` `webpack`
> [time= 2019 11 30 ]
> 原文 & 參考:
> https://www.youtube.com/watch?v=BmL8iaLMnQ0&list=PLillGF-RfqbbRA-CIUxlxkUpbq0IFkX60&index=3
<br>
## Redux & HTTP
在 Chrome 瀏覽器安裝 Redux DevTools

<br><br><br>
安裝 redux react-redux redux-thunk redux-devtools-extension
*終端機目前位置 `~/django_rest_api_react/`*
```=
$ npm i redux react-redux redux-thunk redux-devtools-extension
```
> *redux-devtools-extension@2.13.8*
> *redux-thunk@2.3.0*
> *react-redux@7.1.3*
> *redux@4.0.4*
<br><br><br>
新增一個檔案命名為 `./leadmanager/frontend/src/store.js` 在裡面寫入:
```javascript=
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
```
資料結構
```
django_rest_api_react
├──leadmanager
│ ├──frontend
│ │ ├──migrations
│ │ ├──src
│ │ │ ├──components
│ │ │ ├──index.js
│ │ │ └──store.js
│ │ │
...
```
<br><br><br>
新增一個檔案命名為 `./leadmanager/frontend/src/reducers/index.js` 在裡面寫入:
```javascript=
import { combineReducers } from 'redux';
import leads from './leads';
export default combineReducers({
leads
});
```
資料結構
```
django_rest_api_react
├──leadmanager
│ ├──frontend
│ │ ├──migrations
│ │ ├──src
│ │ │ ├──components
│ │ │ ├──reducers
│ │ │ │ └──index.js
│ │ │ │
│ │ │ ├──index.js
│ │ │ └──store.js
│ │ │
...
```
<br><br><br>
開啟 `./leadmanager/frontend/src/components/App.js` 在裡面加入:
```javascript=
...
import Dashboard from './leads/Dashboard';
import { Provider } from 'react-redux'; // new add
import store from '../store'; // new add
export class App extends Component {
render() {
return (
<Provider store={store}> // new add
<Fragment>
<Header />
<div className="container">
<Dashborad />
</div>
</Fragment>
</Provider> // new add
)
}
}
ReactDom.render(<App />, document.getElementById('app'));
```
<br><br><br>
### GET LEADS
新增一個檔案命名為 `./leadmanager/frontend/src/reducers/leads.js` 在裡面寫入:
```javascript=
import { GET_LEADS } from '../actions/types.js'
const initailState = {
leads: []
};
export default function (state = initailState, action) {
switch (action.type) {
case GET_LEADS:
return {
...state,
leads: action.payload
}
default:
return state;
}
}
```
資料結構
```
django_rest_api_react
├──leadmanager
│ ├──frontend
│ │ ├──migrations
│ │ ├──src
│ │ │ ├──components
│ │ │ ├──reducers
│ │ │ │ ├──index.js
│ │ │ │ └──leads.js
│ │ │ │
│ │ │ ├──index.js
│ │ │ └──store.js
│ │ │
...
```
<br><br><br>
新增一個檔案命名為 `./leadmanager/frontend/src/actions/types.js` 在裡面寫入:
```javascript=
export const GET_LEADS = 'GET_LEADS';
```
資料結構
```
django_rest_api_react
├──leadmanager
│ ├──frontend
│ │ ├──migrations
│ │ ├──src
│ │ │ ├──actions
│ │ │ │ └──types.js
│ │ │ │
│ │ │ ├──components
│ │ │ ├──reducers
│ │ │ ├──index.js
│ │ │ └──store.js
│ │ │
...
```
<br><br><br>
安裝 axios
```=
$ npm i axios
```
> *axios@0.19.0*
<br><br><br>
新增一個檔案命名為 `./leadmanager/frontend/src/actions/leads.js` 在裡面寫入:
```javascript=
import axios from 'axios';
import { GET_LEADS } from './types';
export const getLeads = () => dispatch => {
axios.get('/api/leads')
.then(res => {
dispatch({
type: GET_LEADS,
payload: res.data
});
})
.catch(err => console.log(err));
};
```
資料結構
```
django_rest_api_react
├──leadmanager
│ ├──frontend
│ │ ├──migrations
│ │ ├──src
│ │ │ ├──actions
│ │ │ │ ├──leads.js
│ │ │ │ └──types.js
│ │ │ │
│ │ │ ├──components
│ │ │ ├──reducers
│ │ │ ├──index.js
│ │ │ └──store.js
│ │ │
...
```
<br><br><br>
開啟 `./leadmanager/frontend/src/components/leads/Leads.js` 在裡面加入:
```javascript=
import React, { Component } from 'react'
import { connect } from 'react-redux'; // new add
import PropTypes from 'prop-types'; // new add
import { getLeads } from '../../actions/leads'; // new add
export class Leads extends Component {
// new add
static propTypes = {
leads: PropTypes.array.isRequired,
getLeads: PropTypes.func.isRequired
}
// new add
componentDidMount(){
this.props.getLeads();
}
render() {
return (
<div>
<h1>Leads List</h1>
</div>
)
}
}
// new add
const mapStateToProps = state => ({
leads: state.leads.leads
})
// modify
export default connect(mapStateToProps, { getLeads })(Leads);
```
現在 redux 已經取得資料了

<br><br><br>
開啟 `./leadmanager/frontend/src/components/leads/Leads.js` 在裡面加入:
```javascript=
import React, { Component, Fragment } from 'react' // modify
import { connect } from 'react-redux';
...
export class Leads extends Component {
...
render() {
return (
// modify
<Fragment>
<h2>Leads</h2>
<table className="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Message</th>
<th />
</tr>
</thead>
<tbody>
{this.props.leads.map(lead => (
<tr key={lead.id}>
<td>{lead.id}</td>
<td>{lead.name}</td>
<td>{lead.email}</td>
<td>{lead.message}</td>
<td>
<button className="btn btn-danger btn-sm">
Delete
</button>
</td>
</tr>
))}
</tbody>
</table>
</Fragment>
)
}
}
...
```

<br><br><br>
### DELETE LEAD
開啟 `./leadmanager/frontend/src/actions/leads.js` 在裡面加入:
```javascript=
...
import { GET_LEADS, DELETE_LEAD } from './types'; // modify
export const getLeads = () => dispatch => {
...
};
// new add
export const deleteLead = (id) => dispatch => {
axios.delete(`/api/leads/${id}`)
.then(res => {
dispatch({
type: DELETE_LEAD,
payload: id
});
})
.catch(err => console.log(err));
};
```
<br><br><br>
開啟 `./leadmanager/frontend/src/actions/types.js` 在裡面加入:
```javascript=
export const GET_LEADS = 'GET_LEADS';
export const DELETE_LEAD = 'DELETE_LEAD'; // new add
```
<br><br><br>
開啟 `./leadmanager/frontend/src/reducers/leads.js` 在裡面寫入:
```javascript=
import { GET_LEADS, DELETE_LEAD } from '../actions/types.js' // modify
...
export default function (state = initailState, action) {
switch (action.type) {
case GET_LEADS:
...
//new add
case DELETE_LEAD:
return {
...state,
leads: state.leads.filter(lead => lead.id !== action.payload)
}
default:
...
}
}
```
<br><br><br>
開啟 `./leadmanager/frontend/src/components/leads/Leads.js` 在裡面加入:
```javascript=
...
import { getLeads, deleteLead } from '../../actions/leads'; // modify
export class Leads extends Component {
static propTypes = {
leads: PropTypes.array.isRequired,
getLeads: PropTypes.func.isRequired,
deleteLead: PropTypes.func.isRequired // new add
}
...
render() {
return (
<Fragment>
<h2>Leads</h2>
<table className="table table-striped">
...
<tbody>
{this.props.leads.map(lead => (
<tr key={lead.id}>
...
<td>
<button className="btn btn-danger btn-sm"
// new add
onClick={this.props.deleteLead.bind(this, lead.id)}>
Delete
</button>
</td>
</tr>
))}
</tbody>
</table>
</Fragment>
)
}
}
...
// modify
export default connect(mapStateToProps, { getLeads, deleteLead })(Leads);
```
現在你可以刪除項目了

<br><br><br>
### ADD LEAD
開啟 `./leadmanager/frontend/src/actions/leads.js` 在裡面加入:
```javascript=
...
import { GET_LEADS, DELETE_LEAD, ADD_LEAD } from './types'; // modify
export const getLeads = () => dispatch => {
...
};
export const deleteLead = (id) => dispatch => {
...
};
// new add
export const addLead = lead => dispatch => {
axios.post('/api/leads/', lead)
.then(res => {
dispatch({
type: ADD_LEAD,
payload: res.data
});
})
.catch(err => console.log(err));
};
```
<br><br><br>
開啟 `./leadmanager/frontend/src/actions/types.js` 在裡面加入:
```javascript=
export const GET_LEADS = 'GET_LEADS';
export const DELETE_LEAD = 'DELETE_LEAD';
export const ADD_LEAD = 'ADD_LEAD'; // new add
```
<br><br><br>
開啟 `./leadmanager/frontend/src/reducers/leads.js` 在裡面寫入:
```javascript=
...
import { GET_LEADS, DELETE_LEAD, ADD_LEAD } from '../actions/types.js' // modify
...
export default function (state = initailState, action) {
switch (action.type) {
case GET_LEADS:
...
case DELETE_LEAD:
...
//new add
case ADD_LEAD:
return {
...state,
leads: [...state.leads, action.payload]
}
default:
...
}
}
```
<br><br><br>
開啟 `./leadmanager/frontend/src/components/leads/Form.js` 複製下列內容整個貼上:
```javascript=
import React, { Component } from 'react'
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { addLead } from '../../actions/leads';
export class Form extends Component {
state = {
name: '',
email: '',
message: ''
};
static propTypes = {
addLead: PropTypes.func.isRequired
};
onChange = e => this.setState({
[e.target.name]: e.target.value
});
onSubmit = e => {
e.preventDefault();
const { name, email, message } = this.state;
const lead = { name, email, message };
this.props.addLead(lead);
}
render() {
const { name, email, message } = this.state;
return (
<div className="card card-body mt-4 mb-4">
<h2>Add Lead</h2>
<form onSubmit={this.onSubmit}>
<div className="form-group">
<label>Name</label>
<input
className="form-control"
type="text"
name="name"
onChange={this.onChange}
value={name}
/>
</div>
<div className="form-group">
<label>Email</label>
<input
className="form-control"
type="email"
name="email"
onChange={this.onChange}
value={email}
/>
</div>
<div className="form-group">
<label>Message</label>
<textarea
className="form-control"
type="text"
name="message"
onChange={this.onChange}
value={message}
/>
</div>
<div className="form-group">
<button type="submit" className="btn btn-primary">
Submit
</button>
</div>
</form>
</div>
)
}
}
export default connect(null, { addLead })(Form);
```
現在可以使用 add 表單加入新的資料了

<br><br><br>
[[4]Django Rest API + React.js + Redux + webpack 前後端整合](https://hackmd.io/@RoyChen/B1Wg2Egpr)
[[2]Django Rest API + React.js + Redux + webpack 前後端整合](https://hackmd.io/@RoyChen/rJaW62chH)