---
# System prepended metadata

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

---

# [6]Django Rest API + React.js + Redux + webpack 前後端整合

###### tags: `python` `Django` `Django Rest framework`  `React.js` `redux` `webpack`
> [time= 2019 12 04 ] 

> 原文 & 參考：
> https://www.youtube.com/watch?v=EmAc4wQikwY&list=PLillGF-RfqbbRA-CIUxlxkUpbq0IFkX60&index=6

<br>

## Auth State & Private Routes
終端機目前位置 `~/django_rest_api_react`
安裝  react-router-dom
```
$ npm i react-router-dom
```
> *react-router-dom@5.1.2*

<br><br><br>

開啟 `./leadmanager/frontend/src/components/App.js` 在裡面加入：
```javascript=
...
import Alerts from './layout/Alerts'
// new add
import { HashRouter as Router, Route, Switch, Redirect } from 'react-router-dom'
import Login from './accounts/Login'; // new add
import Register from './accounts/Register'; // new add

// Alert Options
const alertOptions = {
    ...
}

export class App extends Component {
    render() {
        return (
            <Provider store={store}>
                <AlertProvider template={AlertTemplate} {...alertOptions}>
                    <Router> {/* new add */}
                        <Fragment>
                            <Header />
                            <Alerts />
                            <div className="container">
                                <Switch> {/* new add */}
                                    <Route exact path="/" component={Dashboard} /> {/* modify */}
                                    <Route exact path="/register" component={Register} /> {/* new add */}
                                    <Route exact path="/login" component={Login} /> {/* new add */}
                                </Switch> {/* new add */}
                            </div>
                        </Fragment>
                    </Router> {/* new add */}
                </AlertProvider>
            </Provider>
        )
    }
}

ReactDom.render(<App />, document.getElementById('app'));
```

<br><br><br>

新增一個檔案命名為 `./leadmanager/frontend/src/components/accounts/Register.js` 在裡面寫入：
```javascript=
import React, { Component } from 'react';
import { Link } from 'react-router-dom';

export class Register extends Component {
    state = {
        username: '',
        email: '',
        password: '',
        password2: ''
    };

    onSubmit = e => {
        e.preventDefault();
        console.log("submit");
    }

    onChange = e => this.setState({
        [e.target.name]: e.target.value
    });

    render() {
        const { username, email, password, password2 } = this.state;

        return (
            <div className="col-md-6 m-auto">
                <div className="card card-body mt-5">
                    <h2 className="text-center">Register</h2>
                    <form onSubmit={this.onSubmit}>
                        <div className="form-group">
                            <label>Username</label>
                            <input
                                type="text"
                                className="form-control"
                                name="username"
                                onChange={this.onChange}
                                value={username}
                            />
                        </div>
                        <div className="form-group">
                            <label>Email</label>
                            <input
                                type="email"
                                className="form-control"
                                name="email"
                                onChange={this.onChange}
                                value={email}
                            />
                        </div>
                        <div className="form-group">
                            <label>Password</label>
                            <input
                                type="password"
                                className="form-control"
                                name="password"
                                onChange={this.onChange}
                                value={password}
                            />
                        </div>
                        <div className="form-group">
                            <label>Confirm Password</label>
                            <input
                                type="password"
                                className="form-control"
                                name="password2"
                                onChange={this.onChange}
                                value={password2}
                            />
                        </div>
                        <div className="form-group">
                            <button type="submit" className="btn btn-primary">
                                Register
                            </button>
                        </div>
                        <p>
                            Already have an account? <Link to="/login">Login</Link>
                        </p>
                    </form>
                </div>
            </div>
        )
    }
}

export default Register
```

<br><br><br>

新增一個檔案命名為 `./leadmanager/frontend/src/components/accounts/Login.js` 在裡面寫入：
```javascript=
import React, { Component } from 'react';
import { Link } from 'react-router-dom';

export class Login extends Component {
    state = {
        username: '',
        password: ''
    };

    onSubmit = e => {
        e.preventDefault();
        console.log("submit");
    }

    onChange = e => this.setState({
        [e.target.name]: e.target.value
    });

    render() {
        const { username, password } = this.state;

        return (
            <div className="col-md-6 m-auto">
                <div className="card card-body mt-5">
                    <h2 className="text-center">Login</h2>
                    <form onSubmit={this.onSubmit}>
                        <div className="form-group">
                            <label>Username</label>
                            <input
                                type="text"
                                className="form-control"
                                name="username"
                                onChange={this.onChange}
                                value={username}
                            />
                        </div>
                        <div className="form-group">
                            <label>Password</label>
                            <input
                                type="password"
                                className="form-control"
                                name="password"
                                onChange={this.onChange}
                                value={password}
                            />
                        </div>
                        <div className="form-group">
                            <button type="submit" className="btn btn-primary">
                                Login
                            </button>
                        </div>
                        <p>
                            Don't have an account? <Link to="/register">Login</Link>
                        </p>
                    </form>
                </div>
            </div>
        )
    }
}

export default Login
```

資料結構
```
django_rest_api_react
├──leadmanager
│  ├──accounts
│  ├──frontend
│  │  ├──migrations
│  │  ├──src
│  │  │  ├──actions
│  │  │  ├──components
│  │  │  │  ├──accounts
│  │  │  │  │  ├──Login.js
│  │  │  │  │  └──Register.js
│  │  │  │  │
│  │  │  │  ├──layout
│  │  │  │  ├──leads
│  │  │  │  └──App.js
│  │  │  │
│  │  │  ├──reducers
│  │  │  ├──index.js
│  │  │  └──store.js
│  │  │
...
```


<br><br><br>

開啟 `./leadmanager/frontend/src/components/layout/Header.js` 在裡面加入：
```javascript=
import React, { Component } from 'react';
import { Link } from 'react-router-dom'; // new add

export class Header extends Component {
    render() {
        return (
                ...
                    <ul className="navbar-nav mr-auto mt-2 mt-lg-0">
                        { /* new add */ }
                        <li className="nav-item">
                            <Link to="/register" className="nav-link">Register</Link>
                        </li>
                        
                        { /* new add */ }
                        <li className="nav-item">
                            <Link to="/login" className="nav-link">Login</Link>
                        </li>
                    </ul>
                ...
        )
    }
}

export default Header
```

<br><br><br>

開啟 `./leadmanager/frontend/src/reducers/index.js` 在裡面加入：
```javascript=
...
import messages from './messages';
import auth from './auth' // new add

export default combineReducers({
    leads,
    errors,
    messages,
    auth // new add
});
```

<br><br><br>

新增一個檔案命名為 `./leadmanager/frontend/src/reducers/auth.js` 在裡面寫入：
```javascript=
const initialState = {
    token: localStorage.getItem('token'),
    isAuthenticated: null,
    isLoading: false,
    ser: null
};

export default function(state = initialState, action) {
    switch(action.type){
        default:
            return state
    }
}
```

<br><br><br>

新增一個檔案命名為 `./leadmanager/frontend/src/components/common/PrivateRoute.js` 在裡面寫入：
```javascript=
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
import { connect } from 'react-redux';
import { PropTypes } from 'prop-types';

const PrivateRoute = ({ component: Component, auth, ...rest }) => (
    <Route {...rest}
        render={props => {
            if (auth.isLoading) {
                return <h2>loading...</h2>;
            }
            else if (!auth.isAuthenticated) {
                return <Redirect to="/login" />
            }
            else {
                return <Component {...props} />
            }
        }} />
)

const mapStateToProps = state => ({
    auth: state.auth
});

export default connect(mapStateToProps)(PrivateRoute);
```

資料結構
```
django_rest_api_react
├──leadmanager
│  ├──accounts
│  ├──frontend
│  │  ├──migrations
│  │  ├──src
│  │  │  ├──actions
│  │  │  ├──components
│  │  │  │  ├──accounts
│  │  │  │  ├──common
│  │  │  │  │  └──PrivateRoute.js
│  │  │  │  │
│  │  │  │  ├──layout
│  │  │  │  ├──leads
│  │  │  │  └──App.js
│  │  │  │
│  │  │  ├──reducers
│  │  │  ├──index.js
│  │  │  └──store.js
│  │  │
...
```

<br><br><br>

開啟 `./leadmanager/frontend/src/components/App.js` 在裡面加入：
```javascript=
...
import Register from './accounts/Register';
import PrivateRoute from './common/PrivateRoute'; // new add

// Alert Options
const alertOptions = {
    timeout: 30000,
    position: 'top center'
}

export class App extends Component {
    render() {
        return (
                            ...
                                <Switch>
                                    { /* modify */}
                                    <PrivateRoute exact path="/" component={Dashboard} /> 
                                    <Route exact path="/register" component={Register} />
                                    <Route exact path="/login" component={Login} />
                                </Switch>
                            ...
        )
    }
}

ReactDom.render(<App />, document.getElementById('app'));
```

<br><br><br>

開啟 `./leadmanager/frontend/src/actions/types.js` 在裡面加入：
```javascript=
...
export const CREATE_MESSAGE = 'CREATE_MESSAGE';
export const USER_LOADING = 'USER_LOADING'; // new add
export const USER_LOADED = 'USER_LOADED'; // new add
export const AUTH_ERROR = 'AUTH_ERROR'; // new add
```

<br><br><br>

開啟 `./leadmanager/frontend/src/reducers/auth.js` 在裡面加入：
```javascript=
// new add
import { USER_LOADED, USER_LOADING, AUTH_ERROR } from '../actions/types';

const initialState = {
...
};

export default function (state = initialState, action) {
    switch (action.type) {
        // new add
        case USER_LOADING:
            return {
                ...state,
                isLoading: true
            };
            
        // new add
        case USER_LOADED:
            return{
                ...state,
                isAuthenticated: true,
                isLoading: false,
                user: action.payload
            };
            
        // new add
        case AUTH_ERROR:
            localStorage.removeItem('token');
            return {
                ...state,
                token: null,
                user: null,
                isAuthenticated: false,
                isLoading: false
            }
        default:
            return state
    }
}
```

<br><br><br>


新增一個檔案命名為 `./leadmanager/frontend/src/actions/auth.js` 在裡面寫入：
```javascript=
import axios from 'axios';
import { returnErrors } from './messages';
import { USER_LOADED, USER_LOADING, AUTH_ERROR } from './types';

// CHECK TOKEN & LOAD USER
export const loadUser = () => (dispatch, getState) => {
    // User Loading
    dispatch({ type: USER_LOADING });

    // Get token from state
    const token = getState().auth.token;

    const config = {
        headers: {
            'Content-Type': 'application/json'
        }
    }

    // If token, add to headers config
    if (token) {
        config.headers['Authorization'] = `Token ${token}`;
    }

    axios.get("/api/auth/user", config).then(res => {
        dispatch({
            type: USER_LOADED,
            payload: res.data
        })}).catch(err => {
            dispatch(returnErrors(err.response.data, err.response.status));

            dispatch({ type: AUTH_ERROR });
        })
}
```

資料結構
```
django_rest_api_react
├──leadmanager
│  ├──accounts
│  ├──frontend
│  │  ├──migrations
│  │  ├──src
│  │  │  ├──actions
│  │  │  │  ├──auth.js
│  │  │  │  ├──leads.js
│  │  │  │  ├──messages.js
│  │  │  │  └──types.js
│  │  │  │
│  │  │  ├──components
│  │  │  ├──reducers
│  │  │  ├──index.js
│  │  │  └──store.js
│  │  │
...
```

<br><br><br>

開啟 `./leadmanager/frontend/src/components/App.js` 在裡面加入：
```javascript=
...
import PrivateRoute from './common/PrivateRoute';
import { loadUser } from '../actions/auth'; // new add

// Alert Options
const alertOptions = {
    ...
}

export class App extends Component {
    
    // new add
    componentDidMount() {
        store.dispatch(loadUser());
    }

    render() {
        ...
    }
}

ReactDom.render(<App />, document.getElementById('app'));
```

<br><br><br>

[[7]Django Rest API + React.js + Redux + webpack 前後端整合](https://hackmd.io/@RoyChen/rkzbP8K6r)
[[5]Django Rest API + React.js + Redux + webpack 前後端整合](https://hackmd.io/@RoyChen/rya_kmZTH)