# [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 ![](https://i.imgur.com/EijeTgJ.png) <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 已經取得資料了 ![](https://i.imgur.com/qETBoZN.png) <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> ) } } ... ``` ![](https://i.imgur.com/YeREV7X.png) <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); ``` 現在你可以刪除項目了 ![](https://i.imgur.com/OEOXl7b.png) <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 表單加入新的資料了 ![](https://i.imgur.com/NgVfo0Q.png) <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)