React系列实战篇:增长登陆(四)

快来加入咱们吧!

"小和山的菜鸟们",为前端开发者提供技术相关资讯以及系列基础文章。为更好的用户体验,请您移至咱们官网小和山的菜鸟们 ( xhs-rookies.com/ ) 进行学习,及时获取最新文章。javascript

"Code tailor" ,若是您对咱们文章感兴趣、或是想提一些建议,微信关注 “小和山的菜鸟们” 公众号,与咱们取的联系,您也能够在微信上观看咱们的文章。每个建议或是赞同都是对咱们极大的鼓励!css

实战案例(四):完善留言版登陆

咱们此次学了一些新内容,咱们须要将以前的改版。html

首先咱们须要登陆页面,而且经过HOC(高阶组件)添加鉴权功能。加上路由跳转,完善页面。前端

增长路由

yarn add react-router-dom
复制代码

咱们先加入react-routerjava

修改路由配置

咱们须要修改index.js,以前的index.js中一直只有App.js一个,咱们将路由的配置添加到index.js中。react

ReactDOM.render(
  <React.StrictMode> <BrowserRouter> <Switch> <Route path="/login" component={Login} /> <Route path="/home" component={App} /> <Redirect path="/" to="/login" exact /> </Switch> </BrowserRouter> </React.StrictMode>,
  document.getElementById('root'),
)
复制代码

默认状况下,咱们让页面指向Login页面。web

Login 页面

登陆状态维护

若是咱们登陆成功后,咱们应该须要有一个地方用于存放是否登陆成功的信息,为后面鉴权作准备,咱们采用localstorage作数据持久化处理。浏览器

this.props.history.replace('/home')
window.localStorage.islogin = '1'
复制代码

鉴权跳转

咱们须要在登陆页面鉴权,咱们让login页面在加载完成的时候判断,若是已经登陆过了,那么咱们就跳转到home主页,这里咱们采用 react 组件生命周期中的 componentDidMount()方法,在加载完成后进行判断。bash

componentDidMount() {
    let localStorage = window.localStorage
    if (localStorage.islogin === '1') {
      this.props.history.replace("/home")
    }
  }
复制代码

最终组合

class Login extends PureComponent {
  componentDidMount() {
    let localStorage = window.localStorage
    if (localStorage.islogin === '1') {
      this.props.history.replace('/home')
    }
  }

  constructor(props) {
    super(props)
    this.state = {
      username: '',
      password: '',
    }
  }

  render() {
    return (
      <div className="login"> <h2>欢迎来到XXX博客区</h2> <form className="form"> <div className="formItem"> <label htmlFor="username">用户名:</label> <input type="text" id="username" value={this.state.username} onChange={(e) => { this.setState({ username: e.target.value }) }} /> </div> <div className="formItem"> <label htmlFor="password">密码:</label> <input type="password" id="password" value={this.state.password} onChange={(e) => { this.setState({ password: e.target.value }) }} /> </div> <div className="loginBtn" onClick={() => { this.handleLogin() }} > 登陆 </div> </form> </div>
    )
  }

  handleLogin() {
    if (this.state.username && this.state.password) {
      this.props.history.replace('/home')
      window.localStorage.islogin = '1'
      alert('欢迎!')
    } else {
      alert('请输入用户名和密码!')
    }
  }
}

export default Login
复制代码

不须要组件微信

上次咱们将InputCompoent输入框组件和EvaluateCompoent列表展现组件抽象出来放置于 component 文件夹中,咱们先将这两个组件直接放置于App.js中。

咱们只须要抽象一个comment组件,给上次的EvaluateCompoent列表展现组件加上咱们的点赞功能,每一个列表中的评论咱们均可以进行点赞。

所以咱们将首页App.js修改成以下:

import React, { PureComponent } from 'react'
import Comment from './comment'
import './App.css'

class App extends PureComponent {
  constructor() {
    super()
    this.state = {
      title: 'Hello React',
      desc: '你知道有这么一个团队吗?他们怀揣梦想,艰苦奋斗,做为一群大学生菜鸟,放弃了平时娱乐的时间,选择一块儿学习,一块儿成长,将平时学习的笔记,心得总结为文章,目的很简单,但愿能够帮助向他们同样的菜鸟们?你想了解更多吗?快搜索微信公众号:小和山的菜鸟们,加入他们吧!',
      comments: [
        {
          headPortrait: 'https://xhs-rookies.com/img/rookie-icon.png',
          time: new Date(2021, 4, 14, 21, 2, 30),
          nickName: '小菜鸟',
          detail: '这是一个即将推出系列文章的团队,咱们一块儿期待他们的做品吧!',
          liked: true,
          likeNum: 23,
        },
      ],
      text: '',
    }
  }

  render() {
    const { title, desc, comments, text } = this.state
    return (
      <div className="App"> <h2>{title}</h2> <div className="desc">{desc}</div> <div style={{ width: '100%' }}> <p className="commentsTitle">评论</p> {comments.map((item, index) => { return ( <Comment key={item.time.getTime()} changeLike={() => { this.changeLike(index) }} {...item} /> ) })} </div> <div className="newComment"> <div style={{ display: 'flex' }}> <img src="https://xhs-rookies.com/img/rookie-icon.png" className="" alt="" /> <textarea value={text} onChange={(e) => this.changeText(e)} placeholder="请输入评论" /> </div> <div className="submit" onClick={() => { this.addComment() }} > 发表 </div> </div> </div>
    )
  }

  changeText(e) {
    this.setState({ text: e.target.value })
  }

  changeLike(index) {
    let newArray = [...this.state.comments]
    let newItem = { ...newArray[index] }
    if (newItem.liked) {
      newItem.liked = false
      newItem.likeNum -= 1
    } else {
      newItem.liked = true
      newItem.likeNum += 1
    }
    newArray[index] = newItem
    this.setState({
      comments: newArray,
    })
  }

  addComment() {
    if (!this.state.text) {
      alert('请输入留言内容')
      return
    }
    let detail = this.state.text
    this.setState({ text: '' })
    let newComment = {
      headPortrait: 'https://xhs-rookies.com/img/rookie-icon.png',
      time: new Date(),
      nickName: '小菜鸟',
      detail,
      liked: false,
      likeNum: 0,
    }
    this.setState({
      comments: [newComment, ...this.state.comments],
    })
  }
}

App.propTypes = {}

export default App
复制代码

主页修改

主页鉴权

这里咱们也须要鉴权,也就是说,若是在浏览器内直接输入home的时候,若是没有登陆,咱们则须要让其跳转至login登陆页面.

咱们是否可使用和登陆同样的时候,在加载完成后,判断而后跳转呢?

componentDidMount() {
    let localStorage = window.localStorage
    if (localStorage.islogin === '1') {
      this.props.history.replace("/home")
    }
  }
复制代码

这里其实有问题,咱们若是在加载完成直接判断后跳转,是否每一个页面都要加入呢?

可是这个鉴权实际上是一个通用功能,若是如今还有一个我的信息页面的话,是否是也须要这么一个功能呢?

高阶组件鉴权

咱们采用高阶组件进行鉴权,那么之后每一个页面若是须要鉴权,都只须要经过高阶组件包裹便可。

import React from 'react'
import { Redirect } from 'react-router-dom'

export default function checkRole(WrapperComponent) {
  let localStorage = window.localStorage
  return (props) => {
    if (localStorage.islogin === '1') {
      return <WrapperComponent {...props} />
    } else {
      return <Redirect to="/" />
    }
  }
}
复制代码

而后咱们将Home主页(App.js)进行包裹

export default checkRole(App)
复制代码

登出选项

咱们登陆成功后,那么咱们天然在主页面须要加入登出的选项,再登出后,再次进入主页面就会被鉴权断定跳转至登陆页面。

handleLogout() {
    window.localStorage.islogin = '0'
    this.props.history.replace("/login")
  }
复制代码

最终的主页面以下:

import React, { PureComponent } from 'react'
import Comment from './comment'
import checkRole from './checkRole'
import './App.css'

class App extends PureComponent {
  constructor() {
    super()
    this.state = {
      title: 'Hello React',
      desc: '你知道有这么一个团队吗?他们怀揣梦想,艰苦奋斗,做为一群大学生菜鸟,放弃了平时娱乐的时间,选择一块儿学习,一块儿成长,将平时学习的笔记,心得总结为文章,目的很简单,但愿能够帮助向他们同样的菜鸟们?你想了解更多吗?快搜索微信公众号:小和山的菜鸟们,加入他们吧!',
      comments: [
        {
          headPortrait: 'https://xhs-rookies.com/img/rookie-icon.png',
          time: new Date(2021, 4, 14, 21, 2, 30),
          nickName: '小菜鸟',
          detail: '这是一个即将推出系列文章的团队,咱们一块儿期待他们的做品吧!',
          liked: true,
          likeNum: 23,
        },
      ],
      text: '',
    }
  }

  render() {
    const { title, desc, comments, text } = this.state
    return (
      <div className="App"> <span className="logout" onClick={() => { this.handleLogout() }} > 退出登陆 </span> <h2>{title}</h2> <div className="desc">{desc}</div> <div style={{ width: '100%' }}> <p className="commentsTitle">评论</p> {comments.map((item, index) => { return ( <Comment key={item.time.getTime()} changeLike={() => { this.changeLike(index) }} {...item} /> ) })} </div> <div className="newComment"> <div style={{ display: 'flex' }}> <img src="https://xhs-rookies.com/img/rookie-icon.png" className="" alt="" /> <textarea value={text} onChange={(e) => this.changeText(e)} placeholder="请输入评论" /> </div> <div className="submit" onClick={() => { this.addComment() }} > 发表 </div> </div> </div>
    )
  }

  handleLogout() {
    window.localStorage.islogin = '0'
    this.props.history.replace('/login')
  }

  changeText(e) {
    this.setState({ text: e.target.value })
  }

  changeLike(index) {
    let newArray = [...this.state.comments]
    let newItem = { ...newArray[index] }
    if (newItem.liked) {
      newItem.liked = false
      newItem.likeNum -= 1
    } else {
      newItem.liked = true
      newItem.likeNum += 1
    }
    newArray[index] = newItem
    this.setState({
      comments: newArray,
    })
  }

  addComment() {
    if (!this.state.text) {
      alert('请输入留言内容')
      return
    }
    let detail = this.state.text
    this.setState({ text: '' })
    let newComment = {
      headPortrait: 'https://xhs-rookies.com/img/rookie-icon.png',
      time: new Date(),
      nickName: '小菜鸟',
      detail,
      liked: false,
      likeNum: 0,
    }
    this.setState({
      comments: [newComment, ...this.state.comments],
    })
  }
}

App.propTypes = {}

export default checkRole(App)
复制代码

总结

到这里咱们整个React实战案例就结束了,虽然就是一个简简单单的留言案例,可是却包含了不少知识点,从开始的HTML到脚手架建立。

加入了首页鉴权,使用PropTypes检查传进来的数据是否符合要求,经过HOC(高阶组件)来增长通用组件功能。

直接预览

咱们建议采用 codesanbox 的形式能够在线快速访问当前实战案例。

CodeSandBox

下节预告

到这里,咱们 React 相关基础知识已学习完毕,下一节咱们将向更高的山峰前进 —— Hooks,敬请期待吧!

相关文章
相关标签/搜索