实战登陆界面实现

164 阅读3分钟

1. 布局和样式实现

首先我们实现最基本的布局和样式

// Login.jsx
import React, { PureComponent } from 'react';
import { connect } from 'react-redux';
import { Input, LoginBox, LoginWrapper } from './styles';
import { Button } from './styles';
class Login extends PureComponent {
  render() {
    return (
      <LoginWrapper>
        <LoginBox>
          <Input placeholder="账号" />
          <Input placeholder="密码" />
          <Button>登录</Button>
        </LoginBox>
      </LoginWrapper>
    );
  }
}

const mapStateToProps = (state) => ({});

const mapDispatchToProps = (dispatch) => ({});

export default connect(mapStateToProps, mapDispatchToProps)(Login);

// styles

import styled from 'styled-components';

export const LoginWrapper = styled.div`
  z-index: 0;
  position: absolute;
  left: 0;
  right: 0;
  top: 56px;
  bottom: 0;
  background: #eee;
`;

export const LoginBox = styled.div`
  width: 400px;
  height: 180px;
  margin: 100px auto;
  padding-top: 20px;
  background: #fff;
  box-shadow: 0 0 8px rgba(0, 0, 0, 0.1);
`;

export const Input = styled.input`
  display: block;
  width: 200px;
  height: 30px;
  line-height: 30px;
  margin: 10px auto;
  padding: 0 10px;
  color: #777;
`;

export const Button = styled.div`
  display: block;
  width: 220px;
  height: 30px;
  line-height: 30px;
  margin: 10px auto;
  color: #fff;
  text-align: center;
  background: #3194d0;
  border-radius: 15px;
`;

// App中添加到路由中
function App() {
  return (
    <Provider store={store}>
      <BrowserRouter>
        <div>
          <Header />
          <Route path="/" exact component={Home}></Route>
          {/*添加:id,访问detail下要求传递一个id参数,后面link跳转时候进行拼接*/}
          <Route path="/detail/:id" exact component={Detail}></Route>
          <Route path="/login" exact component={Login}></Route>
        </div>
      </BrowserRouter>
      <GlobalIconfont />
      <GlobalBody />
    </Provider>
  );
}

export default App;

2. 逻辑实现

2.1 实现登录

首先实现登录逻辑。当用户尚未登录时,Header组件中会显示登录按钮。点击登录按钮后,跳转到登录页面进行用户信息的填写,验证成功后跳转到首页,此时Header组件中的“登录”会变成“退出”。

让Header组件中的“登录”文字根据是否登录进行变化:

class Header extends Component {
  render() {
    const { focused, handleInputFocus, handleInputBlur, list, login } = this.props;
    return (
      <HeaderWrapper>
        <Link to={'/'}>
          <Logo/>
        </Link>
        <Nav>
          <NavItem className='left active'>首页</NavItem>
          <NavItem className='left'>下载App</NavItem>
          {
            login ?
              <NavItem className='right'>退出</NavItem> :
              <Link to='/login'><NavItem className='right'>登录</NavItem></Link>
          }
          <NavItem className='right'>
            <i className='iconfont'>&#xe636;</i>
          </NavItem>
          {/* ... */}
        </Nav>
      </HeaderWrapper>
    )
  }
}

接下来,实现登录账号的逻辑,使用两个input组件进行账号输入:

import React, { PureComponent } from 'react';
import { connect } from 'react-redux';
import { Input, LoginBox, LoginWrapper } from "./styles";
import { Button } from "./styles";
import * as actionCreators from "./store/actionCreators";
import { Redirect } from "react-router-dom";

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 mapStateToProps = (state) => ({
  loginStatus: state.getIn(['login', 'login'])
});

const mapDispatchToProps = (dispatch) => ({
  login(accountElem, passwordElem) {
    dispatch(actionCreators.login(accountElem.value, passwordElem.value));
  }
});

export default connect(mapStateToProps, mapDispatchToProps)(Login);

使用dispatch派发一个异步action,调用ajax发送请求:

import axios from "axios";
import * as constants from "./constants";

const changeLogin = () => ({
  type: constants.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) {
          dispatch(changeLogin());
        } else {
          alert('登录失败');
        }
      });
  }
};

reducer解析:

image.png

2.2 退出逻辑

接下来,我们需要实现退出逻辑。我们需要在 Header 组件中添加一个退出按钮,然后触发 logout 的 action,但是需要注意的是,这个 action 是在 login 组件的 store 中创建的。

代码如下:

{
  login ?
    <NavItem onClick={logout} className='right'>退出</NavItem> :
    <Link to='/login'><NavItem className='right'>登录</NavItem></Link>
}

注意为了区别引用,我们需要在 logout 方法中使用 loginActionCreators 而不是 actionCreators,因为 logout 是在 login 组件的 store 中创建的。

代码如下:

import {actionCreators} from './store/index';
import {actionCreators as loginActionCreators} from '../../pages/login/store/index';

logout(){
  dispatch(loginActionCreators.logout());
}

接着,我们需要创建一个 logout 的 action creator:

export const logout = () => ({
  type: constants.CHANGE_LOGIN,
  value:false
});

最后,我们需要在 reducer 中处理 LOGOUT 的 action:

cCopy code
export default (state = defaultState, action) => {
  switch (action.type) {
    case constants.CHANGE_LOGIN:
      return state.set('login', action.value);
    case constants.LOGOUT:
      return state.set('login', action.value);
    default: {
      return state;
    }
  }
}

2.3 注意事项

注意,如果我们使用styledComponent,我们会发现使用ref获取到的并不是原始DOM元素。这时我们需要使用styledComponent另一种使用方式——innerRef:

const Button = styled.button.attrs({
  className: 'red-button'
})`
  background: red;
`;

// 在组件中使用innerRef
<Button innerRef={button => this.button = button}>Click me!</Button>

然而,目前的版本已经不再使用innerRef这种调用方式,而是直接兼容ref。因此,我们可以直接使用ref即可:

<Button ref={button => this.button = button}>Click me!</Button>

3. 登录鉴权实现

这里我们实现一个登录鉴权功能,即点击“写笔记”进入写界面需要先登录才能进行。否则,就会进入登录界面。

在Write组件中,我们首先获取login状态:

const {loginStatus} = this.props;

如果已登录,我们渲染写文章页面:

if (loginStatus) {
  return (
    <div>写文章页面</div>
  );
}

否则,我们使用重定向功能将页面更新到登录页面:

return <Redirect to='/login'/>

为了添加“写笔记”按钮,我们在Header组件中添加Link:

<Addition>
  <Link to='/write'>
    <Button className='writing'>
      <i className='iconfont'>&#xe615;</i>
      写文章
    </Button>
  </Link>
  <Button className='reg'>注册</Button>
</Addition>

同时,我们还需要将login状态映射到Write组件的props中:

const mapStateToProps = (state) => ({
  loginStatus: state.getIn(['login', 'login'])
});

export default connect(mapStateToProps, null)(Write);