# [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 作用了 ![](https://i.imgur.com/cTaWcCq.png) <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 了 ![](https://i.imgur.com/XhWR7eT.png) <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)); ``` 現在刪除一筆資料,會顯示成功刪除的資訊 ![](https://i.imgur.com/G8srfv6.png) <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); } }; ... } ... ``` 現在新增一筆資料,會顯示成功新增的資訊 ![](https://i.imgur.com/HhHRcZO.png) <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)