---
# System prepended metadata

title: '[4]Django Rest API + React.js + Redux + webpack 前後端整合'
tags: [redux, Django, webpack, python, React.js, Django Rest framework]

---

# [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)