# [4]Django Rest API + React.js + Redux + webpack 前後端整合
###### tags: `python` `Django` `Django Rest framework` `React.js` `redux` `webpack`
> [time= 2019 11 31 ]
> 原文 & 參考:
> https://www.youtube.com/watch?v=Fia-GGgHpK0&list=PLillGF-RfqbbRA-CIUxlxkUpbq0IFkX60&index=4
<br>
此章節的功能為非必要性的
## Error Handling & Alerts
安裝
*終端機目前位置 `~/django_rest_api_react/`*
```=
$ npm i react-alert react-alert-template-basic react-transition-group
```
> *react-transition-group@4.3.0*
> *react-alert-template-basic@1.0.0*
> *react-alert@6.0.0*
<br><br><br>
開啟 `./leadmanager/frontend/src/components/App.js` 在裡面加入:
```javascript=
...
import store from '../store'
import { Provider as AlertProvider } from 'react-alert'; // new add
import AlertTemplate from 'react-alert-template-basic'; // new add
import Alerts from './layout/Alerts' // new add
// new add
// Alert Options
const alertOptions = {
timeout: 3000,
position: 'top center'
}
export class App extends Component {
render() {
return (
<Provider store={store}>
// new add
<AlertProvider template={AlertTemplate} {...alertOptions}>
<Fragment>
<Header />
<Alerts />
<div className="container">
<Dashboard />
</div>
</Fragment>
</AlertProvider> // new add
</Provider>
)
}
}
...
```
<br><br><br>
新增一個檔案命名為 `./leadmanager/frontend/src/components/layout/Alerts.js` 在裡面寫入:
```javascript=
import React, { Component, Fragment } from 'react'
import { withAlert } from 'react-alert';
export class Alerts extends Component {
componentDidMount(){
this.props.alert.show("It's work.");
}
render() {
return <Fragment />
}
}
export default withAlert()(Alerts);
```
資料結構
```
django_rest_api_react
├──leadmanager
│ ├──frontend
│ │ ├──migrations
│ │ ├──src
│ │ │ ├──actions
│ │ │ ├──components
│ │ │ │ ├──layout
│ │ │ │ │ ├──Alerts.js
│ │ │ │ │ └──Header.js
│ │ │ │ │
│ │ │ │ ├──leads
│ │ │ │ └──App.js
│ │ │ │
│ │ │ ├──reducers
│ │ │ ├──index.js
│ │ │ └──store.js
│ │ │
...
```
現在重新整理頁面,就會看到 react-alert 作用了

<br><br><br>
開啟 `./leadmanager/frontend/src/reducers/index.js` 在裡面加入:
```javascript=
import { combineReducers } from 'redux';
import leads from './leads';
import errors from './errors'; // new add
export default combineReducers({
leads ,
errors // new add
});
```
<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';
export const GET_ERRORS = 'GET_ERRORS'; // new add
```
<br><br><br>
新增一個檔案命名為 `./leadmanager/frontend/src/reducers/errors.js` 在裡面寫入:
```javascript=
import { GET_ERRORS } from '../actions/types';
const initialState = {
msg: {},
status: null
}
export default function (state = initialState, action) {
switch (action.type) {
case GET_ERRORS:
return {
msg: action.payload.msg,
status: action.payload.status
}
default:
return state
}
}
```
資料結構
```
django_rest_api_react
├──leadmanager
│ ├──frontend
│ │ ├──migrations
│ │ ├──src
│ │ │ ├──actions
│ │ │ ├──components
│ │ │ ├──reducers
│ │ │ │ ├──errors.js
│ │ │ │ ├──index.js
│ │ │ │ └──leads.js
│ │ │ │
│ │ │ ├──index.js
│ │ │ └──store.js
│ │ │
...
```
<br><br><br>
開啟 `./leadmanager/frontend/src/actions/leads.js` 在裡面加入:
```javascript=
import axios from 'axios';
// modify
import { GET_LEADS, DELETE_LEAD, ADD_LEAD, GET_ERRORS } from './types';
export const getLeads = () => dispatch => {
...
};
export const deleteLead = (id) => dispatch => {
...
};
export const addLead = lead => dispatch => {
axios.post('/api/leads/', lead)
.then(res => {
dispatch({
type: ADD_LEAD,
payload: res.data
});
})
// modify
.catch(err => {
const errors = {
msg: err.response.data,
status: err.response.status
};
dispatch({
type: GET_ERRORS,
payload: errors
});
});
};
```
<br><br><br>
開啟 `./leadmanager/frontend/src/components/layout/Alerts.js` 在裡面加入:
```javascript=
import React, { Component, Fragment } from 'react'
import { withAlert } from 'react-alert';
import { connect } from 'react-redux'; // new add
import PropTypes from 'prop-types'; // new add
export class Alerts extends Component {
// new add
static propTypes = {
error: PropTypes.object.isRequired
};
// delete componentDidMout()
// new add
componentDidUpdate(prevProps) {
const { error, alert } = this.props;
if(error !== prevProps.error ){
if (error.msg.name) alert.error(`Name: ${error.msg.name.join()}`);
if (error.msg.email) alert.error(`Email: ${error.msg.email.join()}`);
if (error.msg.message) alert.error(`Message: ${error.msg.message.join()}`);
}
};
...
}
// new add
const mapStateToProps = state => ({
error: state.errors
});
// modify
export default connect(mapStateToProps)(withAlert()(Alerts));
```
現在發送空的表單或新增已存在的 email,就會得到我們寫的 react-alert 了

<br><br><br>
### DELETE ALERT
新增一個檔案命名為 `./leadmanager/frontend/src/reducers/messages.js` 在裡面寫入:
```javascript=
import { CREATE_MESSAGE } from '../actions/types';
const initialState = {};
export default function (state = initialState, action) {
switch (action.type) {
case CREATE_MESSAGE:
return (state = action.payload);
default:
return state;
}
}
```
資料結構
```
django_rest_api_react
├──leadmanager
│ ├──frontend
│ │ ├──migrations
│ │ ├──src
│ │ │ ├──actions
│ │ │ ├──components
│ │ │ ├──reducers
│ │ │ │ ├──errors.js
│ │ │ │ ├──index.js
│ │ │ │ ├──leads.js
│ │ │ │ └──messages.js
│ │ │ │
│ │ │ ├──index.js
│ │ │ └──store.js
│ │ │
...
```
<br><br><br>
開啟 `./leadmanager/frontend/src/actions/types.js` 在裡面加入:
```javascript=
...
export const GET_ERRORS = 'GET_ERRORS';
export const CREATE_MESSAGE = 'CREATE_MESSAGE'; // new add
```
<br><br><br>
開啟 `./leadmanager/frontend/src/reducers/index.js` 在裡面加入:
```javascript=
...
import errors from './errors';
import messages from './messages'; // new add
export default combineReducers({
leads,
errors,
message,// new add
});
```
<br><br><br>
新增一個檔案命名為 `./leadmanager/frontend/src/actions/messages.js` 在裡面寫入:
```javascript=
import { CREATE_MESSAGE } from './types';
export const creatMessage = msg => {
return {
type: CREATE_MESSAGE,
payload: msg
}
}
```
資料結構
```
django_rest_api_react
├──leadmanager
│ ├──frontend
│ │ ├──migrations
│ │ ├──src
│ │ │ ├──actions
│ │ │ │ ├──leads.js
│ │ │ │ ├──messages.js
│ │ │ │ └──types.js
│ │ │ │
│ │ │ ├──components
│ │ │ ├──reducers
│ │ │ ├──index.js
│ │ │ └──store.js
│ │ │
...
```
<br><br><br>
開啟 `./leadmanager/frontend/src/actions/leads.js` 在裡面加入:
```javascript=
import axios from 'axios';
import { GET_LEADS, DELETE_LEAD, ADD_LEAD, GET_ERRORS } from './types';
import { creatMessage } from './messages'; // new add
export const getLeads = () => dispatch => {
...
};
export const deleteLead = (id) => dispatch => {
axios.delete(`/api/leads/${id}`)
.then(res => {
// new add
dispatch(creatMessage({deleteLead: "Lead Deleted"}));
dispatch({
type: DELETE_LEAD,
payload: id
});
})
.catch(err => console.log(err));
};
export const addLead = lead => dispatch => {
...
};
```
<br><br><br>
開啟 `./leadmanager/frontend/src/components/layout/Alerts.js` 在裡面加入:
```javascript=
...
export class Alerts extends Component {
static propTypes = {
error: PropTypes.object.isRequired,
message: PropTypes.object.isRequired // new add
};
componentDidUpdate(prevProps) {
const { error, alert, message } = this.props; // modify
if (error !== prevProps.error) {
...
}
// new add
if (message !== prevProps.message) {
if (message.deleteLead) alert.success(message.deleteLead);
}
};
...
}
const mapStateToProps = state => ({
error: state.errors,
message: state.messages // new add
});
export default connect(mapStateToProps)(withAlert()(Alerts));
```
現在刪除一筆資料,會顯示成功刪除的資訊

<br><br><br>
### ADD ALERT
開啟 `./leadmanager/frontend/src/actions/leads.js` 在裡面加入:
```javascript=
...
export const getLeads = () => dispatch => {
...
};
export const deleteLead = (id) => dispatch => {
...
};
export const addLead = lead => dispatch => {
axios.post('/api/leads/', lead)
.then(res => {
dispatch(creatMessage({addLead: "Lead Added"})); // new add
dispatch({
type: ADD_LEAD,
payload: res.data
});
})
.catch(err => {
...
});
};
```
<br><br><br>
開啟 `./leadmanager/frontend/src/components/layout/Alerts.js` 在裡面加入:
```javascript=
...
export class Alerts extends Component {
...
componentDidUpdate(prevProps) {
const { error, alert, message } = this.props;
if (error !== prevProps.error) {
...
}
if (message !== prevProps.message) {
if (message.deleteLead) alert.success(message.deleteLead);
// new add
if (message.addLead) alert.success(message.addLead);
}
};
...
}
...
```
現在新增一筆資料,會顯示成功新增的資訊

<br><br><br>
在按新增後,刪除 Add Lead 輸入內容
開啟 `./leadmanager/frontend/src/components/leads/Form.js` 在裡面加入:
```javascript=
...
export class Form extends Component {
...
onSubmit = e => {
e.preventDefault();
const { name, email, message } = this.state;
const lead = { name, email, message };
this.props.addLead(lead);
// new add
this.setState({
name: "",
email: "",
message: ""
});
}
...
}
...
```
<br><br><br>
[[5]Django Rest API + React.js + Redux + webpack 前後端整合](https://hackmd.io/@RoyChen/rya_kmZTH)
[[3]Django Rest API + React.js + Redux + webpack 前後端整合](https://hackmd.io/@RoyChen/rksvPD6nB)