"小和山的菜鸟们",为前端开发者提供技术相关资讯以及系列基础文章。为更好的用户体验,请您移至咱们官网小和山的菜鸟们 ( xhs-rookies.com/ ) 进行学习,及时获取最新文章。javascript
"Code tailor" ,若是您对咱们文章感兴趣、或是想提一些建议,微信关注 “小和山的菜鸟们” 公众号,与咱们取的联系,您也能够在微信上观看咱们的文章。每个建议或是赞同都是对咱们极大的鼓励!css
咱们此次学了一些新内容,咱们须要将以前的改版。html
首先咱们须要登陆页面,而且经过HOC
(高阶组件)添加鉴权功能。加上路由跳转,完善页面。前端
yarn add react-router-dom
复制代码
咱们先加入react-router
java
咱们须要修改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
若是咱们登陆成功后,咱们应该须要有一个地方用于存放是否登陆成功的信息,为后面鉴权作准备,咱们采用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
的形式能够在线快速访问当前实战案例。
到这里,咱们 React
相关基础知识已学习完毕,下一节咱们将向更高的山峰前进 —— Hooks
,敬请期待吧!