上面我们把登录页面的布局写好了,下面我们把登录页面的逻辑实现。
首先,我们在 src/pages/login 下创建文件夹 store,然后在store 下创建四个文件:index.js , ruducer.js, actionCreators.js, actionTypes.js 。
首先 index.js 如下
import reducer from './reducer';
import * as actionCreators from './actionCreators';
import * as actionTypes from './actionTypes';
export {reducer, actionCreators, actionTypes};
然后 reducer.js 如下。
import { fromJS } from 'immutable';
import * as actionTypes from './actionTypes';
const defaultState = fromJS({
login: false
});
export default (state = defaultState, action) => {
switch (action.type) {
default:
return state;
}
}
然后我们在 src/store 下的reducer 中把小的reducer 进行合并,如下。
import { combineReducers } from 'redux-immutable';
import { reducer as headerReducer } from '../common/header/store';
import { reducer as homeReducer } from '../pages/home/store';
import { reducer as detailReducer } from '../pages/detail/store';
import { reducer as loginReducer } from '../pages/login/store';
const reducer = combineReducers({
header: headerReducer,
home: homeReducer,
detail: detailReducer,
login: loginReducer
});
export default reducer;
然后做这样一个功能,如果我们登录了,header 上就显示退出,否则显示登录,并且点击登录会进入登录页面。我们打开src/common/header 下的 index 做下面的修改
import React, { Component } from 'react';
import {connect} from 'react-redux';
import {
HeaderWrapper,
Logo,
Nav,
NavItem,
NavSearch,
Addition,
Button,
SearchWrapper,
SearchInfo,
SearchInfoTitle,
SearchInfoSwitch,
SearchInfoItem
} from './style';
import '../../statics/iconfont/iconfont.css';
import { CSSTransition } from 'react-transition-group';
import { actionCreators } from './store';
import { Link } from "react-router-dom";
class Header extends Component {
render() {
let { focused, handleFocus, handleBlur, list, login } = this.props;
return (
<HeaderWrapper>
<Link to="/">
<Logo/>
</Link>
<Nav>
<NavItem className='left active'>首页</NavItem>
<NavItem className='left'>下载</NavItem>
{
login ?
<NavItem className='right'>退出</NavItem> :
<Link to="/login">
<NavItem className='right'>登录</NavItem>
</Link>
}
<NavItem className='right'>
<span className="iconfont"></span>
</NavItem>
<SearchWrapper>
<CSSTransition
in={focused}
timeout={200}
classNames="slide"
>
<NavSearch
placeholder="搜索"
className={focused ? "focused" : ""}
onFocus={() => { handleFocus(list) } }
onBlur={handleBlur}
></NavSearch>
</CSSTransition>
<span
className={focused ? "focused iconfont zoom" : "iconfont zoom"}
></span>
{this.getListArea()}
</SearchWrapper>
</Nav>
<Addition>
<Button className='writting'>
<span className="iconfont"></span>
写文章
</Button>
<Button className='reg'>注册</Button>
</Addition>
</HeaderWrapper>
)
}
getListArea = () => {
const {focused, list, page, handleMouseEnter,
mouseEnter, handleMouseLeave, handleChangeKeyWord, totalPage
} = this.props;
const jsList = list.toJS();
const pageList = [];
if (jsList.length) {
for (let i = (page-1) * 10; i< page * 10; i++) {
if (i >= jsList.length) {
break;
}
pageList.push(<SearchInfoItem key={jsList[i]}>{jsList[i]}</SearchInfoItem>)
}
}
if (focused || mouseEnter) {
return (
<SearchInfo
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<SearchInfoTitle>
热门搜索
<SearchInfoSwitch onClick={() => handleChangeKeyWord(page, totalPage,this.spinIcon)}>
<i ref={(icon) => {this.spinIcon = icon}} className="iconfont spin"></i>
换一批
</SearchInfoSwitch>
</SearchInfoTitle>
<div>
{pageList}
</div>
</SearchInfo>
)
} else {
return null;
}
};
}
const mapStateToProps = (state) => {
return {
focused: state.get("header").get("focused"),
list: state.get("header").get("list"),
page: state.get("header").get("page"),
totalPage: state.get("header").get("totalPage"),
mouseEnter: state.get("header").get("mouseIn"),
login: state.get("login").get("login")
}
}
const mapDispatchToProps = (dispatch) => {
return {
handleFocus (list) {
if (list.size === 0) {
dispatch(actionCreators.getList());
}
dispatch(actionCreators.searchFocus());
},
handleBlur () {
dispatch(actionCreators.searchBlur());
},
handleMouseEnter () {
dispatch(actionCreators.mouseEnter());
},
handleMouseLeave () {
dispatch(actionCreators.mouseLeave());
},
handleChangeKeyWord (page, totalPage, spin) {
let originAngle = spin.style.transform.replace(/[^0-9]/ig, '');
if (originAngle) {
originAngle = parseInt(originAngle, 10);
} else {
originAngle = 0;
}
spin.style.transform = "rotate(" + (originAngle + 360 ) + "deg)";
let newPage = (page) % totalPage + 1;
dispatch(actionCreators.changekeyword(newPage));
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Header);
下面,我们可以写 登录页面的逻辑了。打开src/pages/login 下的 index.js 如下。
import React, {PureComponent} from 'react';
import { connect } from 'react-redux';
import { LoginWrapper,
LoginBox,
Input,
Button
} from './style';
class Login extends PureComponent {
render () {
return (
<LoginWrapper>
<LoginBox>
<Input placeholder="账号" ref={(input) => {this.account = input}} />
<Input placeholder="密码" type="password" ref={(input) => {this.password = input}} />
<Button onClick={() => this.props.login(this.account, this.password)}>登录</Button>
</LoginBox>
</LoginWrapper>
)
}
}
const mapDispatch = (dispatch) => {
return {
login (accountElem, passwordElem) {
console.log(accountElem.value, passwordElem.value)
}
}
}
export default connect(null, mapDispatch)(Login);
上面我们就可以通过点击登录,获取输入的用户名与密码。
接下来,我们要在点击事件中派发action 。
先,我们在 项目跟目录下的 public 文件夹下新建一个文件夹api,再在api 下新建文件login.json 如下。
{
"success": true,
"data": true
}
然后,再写actionCreators.js 如下。
import axios from 'axios';
import * as actionTypes from './actionTypes';
const chanegLogin = () => {
return {
type: actionTypes.CHANGE_LOGIN,
value: true
}
}
export const login = (account, password) => {
return (dispatch) => {
axios.get('/api/login.json?account=' + account + '&password=' + password).then((res) => {
const result = res.data.data;
console.log(result);
if (result) {
const action = chanegLogin();
dispatch(action);
} else {
alert("登录失败");
}
})
}
}
同目录下的reducer 如下。
import { fromJS } from 'immutable';
import * as actionTypes from './actionTypes';
const defaultState = fromJS({
login: false
});
export default (state = defaultState, action) => {
switch (action.type) {
case actionTypes.CHANGE_LOGIN:
return state.set("login", action.value);
default:
return state;
}
}
好啦。登录成功后,其实应该跳转到首页。下面我们来写这部分内容。
我们打开 login组件的index,修改如下。
import React, {PureComponent} from 'react';
import {Redirect } from 'react-router-dom';
import { connect } from 'react-redux';
import { LoginWrapper,
LoginBox,
Input,
Button
} from './style';
import { actionCreators } from './store';
class Login extends PureComponent {
render () {
const { loginStatus } = this.props;
if (!loginStatus) {
return (
<LoginWrapper>
<LoginBox>
<Input placeholder="账号" ref={(input) => {this.account = input}} />
<Input placeholder="密码" type="password" ref={(input) => {this.password = input}} />
<Button onClick={() => this.props.login(this.account, this.password)}>登录</Button>
</LoginBox>
</LoginWrapper>
)
} else {
return <Redirect to="/" />
}
}
}
const mapState = (state) => {
return {
loginStatus: state.get("login").get("login")
}
}
const mapDispatch = (dispatch) => {
return {
login (accountElem, passwordElem) {
const action = actionCreators.login(accountElem.value, passwordElem.value);
dispatch(action);
}
}
}
export default connect(mapState, mapDispatch)(Login);
这样就好。
下面我们做一下,点击退出的功能。打开src/common/header 下的index
import React, { Component } from 'react';
import {connect} from 'react-redux';
import {
HeaderWrapper,
Logo,
Nav,
NavItem,
NavSearch,
Addition,
Button,
SearchWrapper,
SearchInfo,
SearchInfoTitle,
SearchInfoSwitch,
SearchInfoItem
} from './style';
import '../../statics/iconfont/iconfont.css';
import { CSSTransition } from 'react-transition-group';
import { actionCreators } from './store';
import {actionCreators as loginActionCreators } from '../../pages/login/store';
import { Link } from "react-router-dom";
class Header extends Component {
render() {
let { focused, handleFocus, handleBlur, list, login, logout } = this.props;
return (
<HeaderWrapper>
<Link to="/">
<Logo/>
</Link>
<Nav>
<NavItem className='left active'>首页</NavItem>
<NavItem className='left'>下载</NavItem>
{
login ?
<NavItem className='right' onClick={logout}>退出</NavItem> :
<Link to="/login">
<NavItem className='right'>登录</NavItem>
</Link>
}
<NavItem className='right'>
<span className="iconfont"></span>
</NavItem>
<SearchWrapper>
<CSSTransition
in={focused}
timeout={200}
classNames="slide"
>
<NavSearch
placeholder="搜索"
className={focused ? "focused" : ""}
onFocus={() => { handleFocus(list) } }
onBlur={handleBlur}
></NavSearch>
</CSSTransition>
<span
className={focused ? "focused iconfont zoom" : "iconfont zoom"}
></span>
{this.getListArea()}
</SearchWrapper>
</Nav>
<Addition>
<Button className='writting'>
<span className="iconfont"></span>
写文章
</Button>
<Button className='reg'>注册</Button>
</Addition>
</HeaderWrapper>
)
}
getListArea = () => {
const {focused, list, page, handleMouseEnter,
mouseEnter, handleMouseLeave, handleChangeKeyWord, totalPage
} = this.props;
const jsList = list.toJS();
const pageList = [];
if (jsList.length) {
for (let i = (page-1) * 10; i< page * 10; i++) {
if (i >= jsList.length) {
break;
}
pageList.push(<SearchInfoItem key={jsList[i]}>{jsList[i]}</SearchInfoItem>)
}
}
if (focused || mouseEnter) {
return (
<SearchInfo
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<SearchInfoTitle>
热门搜索
<SearchInfoSwitch onClick={() => handleChangeKeyWord(page, totalPage,this.spinIcon)}>
<i ref={(icon) => {this.spinIcon = icon}} className="iconfont spin"></i>
换一批
</SearchInfoSwitch>
</SearchInfoTitle>
<div>
{pageList}
</div>
</SearchInfo>
)
} else {
return null;
}
};
}
const mapStateToProps = (state) => {
return {
focused: state.get("header").get("focused"),
list: state.get("header").get("list"),
page: state.get("header").get("page"),
totalPage: state.get("header").get("totalPage"),
mouseEnter: state.get("header").get("mouseIn"),
login: state.get("login").get("login")
}
}
const mapDispatchToProps = (dispatch) => {
return {
handleFocus (list) {
if (list.size === 0) {
dispatch(actionCreators.getList());
}
dispatch(actionCreators.searchFocus());
},
handleBlur () {
dispatch(actionCreators.searchBlur());
},
handleMouseEnter () {
dispatch(actionCreators.mouseEnter());
},
handleMouseLeave () {
dispatch(actionCreators.mouseLeave());
},
handleChangeKeyWord (page, totalPage, spin) {
let originAngle = spin.style.transform.replace(/[^0-9]/ig, '');
if (originAngle) {
originAngle = parseInt(originAngle, 10);
} else {
originAngle = 0;
}
spin.style.transform = "rotate(" + (originAngle + 360 ) + "deg)";
let newPage = (page) % totalPage + 1;
dispatch(actionCreators.changekeyword(newPage));
},
logout () {
dispatch(loginActionCreators.logout());
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Header);
然后在 src/pages/login/store 下的 actionCreators.js 做增加 logout 如下。
import axios from 'axios';
import * as actionTypes from './actionTypes';
const chanegLogin = () => {
return {
type: actionTypes.CHANGE_LOGIN,
value: true
}
}
export const login = (account, password) => {
return (dispatch) => {
axios.get('/api/login.json?account=' + account + '&password=' + password).then((res) => {
const result = res.data.data;
if (result) {
const action = chanegLogin();
dispatch(action);
} else {
alert("登录失败");
}
})
}
}
export const logout = (account, password) => {
return {
type: actionTypes.CHANGE_LOGIN,
value: false
}
}
Done.