3-6 登录功能实现

上面我们把登录页面的布局写好了,下面我们把登录页面的逻辑实现。

首先,我们在 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">&#xe636;</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"}
            >&#xe623;</span>
            {this.getListArea()}
          </SearchWrapper>
        </Nav>
        <Addition>
        <Button className='writting'>
          <span className="iconfont">&#xe63a;</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">&#xe614;</i>&nbsp;
              换一批
            </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">&#xe636;</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"}
            >&#xe623;</span>
            {this.getListArea()}
          </SearchWrapper>
        </Nav>
        <Addition>
        <Button className='writting'>
          <span className="iconfont">&#xe63a;</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">&#xe614;</i>&nbsp;
              换一批
            </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.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值