data fetched with auth token
//pages/albums
import '../App.css';
import { Table } from 'reactstrap';
import { Redirect } from 'react-router-dom';
import { connect } from 'react-redux';
import { getAlbums } from '../redux/actions/getAlbums'
class Albums extends Component {
constructor(props) {
super(props);
this.state = {
};
}
componentDidMount() {
if (this.props.loggedin) {
this.props.dispatch(getAlbums(this.props.token));
}
}
render() {
if (!this.props.loggedin) {
return <Redirect to='/login' />
}
const tableContent = [];
if (this.props.gotAlbums) {
this.props.albums.forEach(album => {
tableContent.push(
<tr key={album.id}>
<td>{album.id}</td>
<td>{album.album_title}</td>
<td>{album.artist}</td>
<td>{album.genre}</td>
<td>{album.author}</td>
<td>{album.date_posted}</td>
</tr>
)
});
}
return (
<div style={{ padding: '10px' }}>
{this.props.error}
{
this.props.gotAlbums ?
<div>
<Table bordered>
<thead>
<tr>
<th>ID</th>
<th>Title</th>
<th>Artist</th>
<th>Genre</th>
<th>Author</th>
<th>Posted</th>
</tr>
</thead>
<tbody>
{tableContent}
</tbody>
</Table>
</div>
: null
}
</div>
);
}
}
export default connect(
(store) => {
return {
token: store.login.token,
loggedin: store.login.fetched,
error: store.albums.error,
albums: store.albums.albums,
gotAlbums: store.albums.fetched
};
}
)(Albums);
--------------------------------------------
//redux/actions/getAlbums
import axios from 'axios';
export function getAlbums(token) {
return {
type: "fetch_albums",
payload: axios({
method: 'get',
url: 'http://127.0.0.1:8000/api/album_list/',
headers: {
Authorization: 'Token ' + token
},
})
}
}
-----------------------------------------
//redux/reducers/albumReducer
export default function reducer(
state = {
albums: [],
fetching: false,
fetched: false,
error: ''
},
action
) {
switch (action.type) {
case "fetch_albums_PENDING": {
return { ...state, fetching: true, fetched: false }
}
case "fetch_albums_FULFILLED": {
return {
...state,
fetching: false,
fetched: true,
albums: action.payload.data,
error: ''
}
}
case "fetch_albums_REJECTED": {
return {
...state,
fetching: false,
error: action.payload.toString()
}
}
default:
break;
}
return state;
}
-------------------------------------
//redux/reducers/index
import { combineReducers } from 'redux';
import login from "./loginReduder";
import albums from "./albumReducer";
export default combineReducers(
{
login, albums
})
---------------------------------
//partials/header
import React, { Component } from 'react';
import '../App.css';
import { Menu, Input } from 'antd';
import { Link } from 'react-router-dom'
import { FaCompactDisc, FaPlus, FaEdit, FaBarcode, FaUser, FaSearch } from "react-icons/fa";
import { GiLoveSong } from "react-icons/gi";
import { IoIosLogOut, IoIosLogIn } from "react-icons/io";
import { connect } from 'react-redux';
import { reset } from '../redux/actions/logoutAction'
class Header extends Component {
constructor(props) {
super(props);
this.state = {
menu_mode: null,
current: 'albums',
};
}
componentWillMount() {
//change menu mode based on screen width
const mode = window.innerWidth > 434 ? "horizontal" : "inline";
this.setState({ menu_mode: mode });
}
handleClick = (e) => {
this.setState({
current: e.key,
});
}
logout = () => {
this.props.dispatch(reset())
}
render() {
return (
<Menu
style={{ backgroundColor: '#fcefe5', position: 'sticky', top: 0, zIndex: 2 }}
onClick={this.handleClick}
selectedKeys={[this.state.current]}
mode={this.state.menu_mode}
>
<Menu.Item key="home">
<Link to='/'><span className='Nav-Brand'>Django</span></Link>
</Menu.Item>
<Menu.Item key="albums">
<Link to='/'> <FaCompactDisc /> Albums</Link>
</Menu.Item>
<Menu.Item key="songs">
<Link to='/'> <GiLoveSong /> Songs</Link>
</Menu.Item>
<Menu.Item key="search">
<Input type='text' size='small'
suffix={<FaSearch style={{ color: 'rgba(0,0,0,.25)', cursor: 'text' }} />}
placeholder='Search'
style={{ width: 150 }}></Input>
</Menu.Item>
{
this.props.loggedin ?
<Menu.SubMenu title={<span><FaUser /> {this.props.username}</span>} style={{ float: 'right' }}>
<Menu.Item key="0" onClick={() => this.logout()}>
<Link to='/logout'> <IoIosLogOut /> Logout</Link>
</Menu.Item>
<Menu.Item key="1">
<Link to='/'> <FaEdit /> Update Profile</Link>
</Menu.Item>
<Menu.Item key="2">
<Link to='/'> <FaBarcode /> Change Password</Link>
</Menu.Item>
</Menu.SubMenu>
:
<Menu.Item key="login" style={{ float: 'right' }}>
<Link to='/login'> <IoIosLogIn /> Login</Link>
</Menu.Item>
}
<Menu.Item key="add" style={{ float: 'right' }}>
<Link to='/'> <FaPlus /> Add Album</Link>
</Menu.Item>
</Menu>
);
}
}
export default connect(
(store) => {
return {
username: store.login.username,
token: store.login.token,
loggingin: store.login.fetching,
loggedin: store.login.fetched,
error: store.login.error
};
}
)(Header);
--------------------------------------------
//App.css
@import url(//fonts.googleapis.com/css?family=Srisakdi);
.Nav-Brand{
font-family: 'Srisakdi', serif;
font-weight: bold;
text-shadow: 4px 4px 4px #aaa;
font-size: 20px;
}
.Body-Background{
height: 100vh;
background-image: url("images/background.jpg");
background-size: 100%;
overflow: auto;
}
-----------------------------------------
//partials/body
import React, { Component } from 'react';
import '../App.css';
import { Switch, Route } from 'react-router-dom';
import Albums from '../pages/albums';
import Login from '../pages/login';
import Logout from '../pages/logout';
export default class Body extends Component {
render() {
return (
<main className='Body-Background'>
<Switch>
<Route exact path='/' component={Albums} />
<Route path='/login/' component={Login} />
<Route path='/logout/' component={Logout} />
</Switch>
</main>
);
}
}
reference:
http://chuanshuoge2.blogspot.com/2019/06/django-40-django-react-redux-ftech-token.html
https://chuanshuoge2.blogspot.com/2019/05/django-38-rest-framework-authentication.html
No comments:
Post a Comment