前言
下面代码示例在实战项目里面都可以找到,配合项目食用效果更佳~ 已更新(把部分有人反馈的地方增加了注释以及案例)
环境搭建
npm install -g create-react-app //安装工具
create-react-app + 项目名称 //创建项目
npm start //启动项目
设计思想
在React的官方博客中明确阐述了 React 不是一个 MVC 框架,而是一个用于构建组件化 UI 的库,是一个前端界面开发工具。所以顶多算是 MVC 中的 V(view)。React 并没有重复造轮子,而是有很多颠覆性的创新,具体的特性如下:
JSX写法
JSX就是 Javascript 和 XML 结合的一种格式。React 发明了 JSX,利用 HTML 语法来创建虚拟 DOM。当遇到<,JSX 就当 HTML 解析,遇到{就当 JavaScript 解析。
import './css/index.css'
class App extends Component {
//将来 生命周期
render() {
var myname = 'anna' //不是状态
var stylyobj = {
background: 'red',
fontSize: '30px'
}
return (
<div>
{10 + 20}--{myname}
{10 > 20 ? 1 : 0}
<div style={stylyobj}>111111</div>
<div style={{ background: 'yellow' }}>111111</div>
// 16版本之前 class 不支持 必须写成className
// 16版本之后 class支持 ,但是会报警告
<div className='active' >33333</div>
<div id='box' >33333</div>
</div>
)
}
}
组件写法
1、class 类式组件
import React from 'react'
import { Component} from 'react'
class Hello extends React.Component {
//将来 生命周期
render() {
return (
<div>111111
<ul>
<li>1111</li>
<li>2222</li>
<Child1/>
<Child2/>
<Child3/>
</ul>
</div>
)
}
}
//Component ===>React.Component 下面的 可以直接被引入
class Child1 extends Component{
render(){
return <div>child1</div>
}
}
2、function 函数式组件
React16.8 之前, 函数式组件不支持状态
React16.8 之后, React Hooks 支持状态和属性
function Child2(){
return (<div>child2
<span>22222</span>
</div>)
}
const Child3=()=><div>child3</div>
export default Hello
事件的四种写法
写法一: 代码量少的情况,可直接在标签里面写
// 获取到输入框的value值
<input type = 'text' ref = 'mytext' />
<button onClick={() => {
console.log('onclick', this.refs.mytext)
}}>add</button>
写法二:方便传参,改变this执行
{/* 注意这里不能加小括号 触发的时候会自动调用 。如果加小括号 ,= 函数返回值再调用*/}
<button onClick={this.handleAdd2.bind(this)}>add2</button>
//写在 render 外面
handleAdd2(){
console.log('click22222', this.refs.mytext.value)
}
写法三:箭头函数 无法传参,但是毕竟方便
<button onClick={this.handleAdd3}>add3</button>
handleAdd3=()=>{
console.log(this.refs.mytext.value)
}
写法四:组合写法
<button onClick={() => {
this.handleAdd3('aaa','bbbb')
}}>add4</button>
handleAdd3=(x,y)=>{
console.log(x,y,'click22222', this.refs.mytext.value)
}
输入框改变获取输入框的值:
<div>
<input type="test" onChange={(evt)=>{
console.log(evt.target.value)
}}/>
赋值给输入框
value={this.state.mytext}
改变this指向
bind call apply区别
**call:**可以传入多个参数,改变this指向后立刻调用函数
**apply:**可以传入数组 ,改变this指向后立刻调用函数
**bind:**改变this指向后,可以传入多个参数,返回的是函数 不会立即调用
var obj1={
name:'obj1',
getName(){
console.log(this.name)
}
}
var obj2 = {
name: 'obj2',
getName() {
console.log(this.name)
}
}
// obj1.getName()//obj1
// 改变this指向,立即指向方法
// obj1.getName.call(obj2,'aaa','bbbb','cccc')//obj2
//obj1.getName.apply(obj2,['aaa','bbb','ccc'])//obj2
// 改变this指向,但是需要手动执行方法
//obj1.getName.bind(obj2,'aaa','bbb','ccc')()
初始化状态和修改状态
React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。 React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。
状态的两种写法(state)
第一种完整写法:
export default class App extends Component {
constructor() {
super()
this.state = {
myname:'4567'
}
}
render() {
return (
<div>
{this.state.myname}
</div>
)
}
}
第二种简写:这个写法 state 也是定义在了 constructor 中
export default class App extends Component {
state = {
myname:'4567'
}
render() {
return (
<div>
{this.state.myname}
</div>
)
}
}
修改状态(setState)
同步过程
<button onClick={this.handkeClick}>click</button>
//直接修改状态
handkeClick=()=>{
this.setState({
myname:'xiaoming',
myage:'18'
})
}
异步过程
第一种写法:
接收两个参数:
第一个参数是对象,修改的状态值
第二个参数 能够等待dom树更新完之后执行
this.setState({
myname:'xiaoming'
},()=>{
console.log('1',this.state.myname)
})
之后发生什么?
//1.虚拟dom创建
//2.diff对比
第二种写法:
可以获取到上个状态值(prevState) 必须有返回值
//1. 简写
this.setState((prevState)=>({
count: prevState.count+1
}))
// 2. 完整写法
this.setState((prevState)=>{{
count: prevState.count+1
}})
setState 何时同步,何时异步,为什么会这样, React 如何去控制同步异步?
想了解的可以看这篇React 中setState更新state何时同步何时异步?
遍历
写法一:
{ this.state.datalist.map(item => <li key={item}>{item}</li>) }
写法二:
var newlist = this.state.datalist.map(item => <li key={item}>{item}</li>)
{newlist} //使用变量
通信
父传子 通过属性(props)
父:
//需要 {} 包住才是 js 不然是字符串
<Navbar mytitle='home' myshow={false}></Navbar>
子:可以直接通过this.props.属性名获取
{this.props.mytitle}
属性简写:
var obj={
mytitle:'测试',
myshow:false
}
<Navbar {...obj}></Navbar>
属性验证
Navbar.propTypes 可以访问到
import MyPropTypes from 'prop-types'; //提供验证数据类型的方法,必须交给MyPropTypes模块方法进行处理验证
class Navbar extends Component {
static propTypes = {
myshow: MyPropTypes.bool,
};
}
默认属性
static defaultProps = {
myshow: true
}
子传父 通过事件
父:传了一个回调函数过去
<Navbar onEvent={() => {
this.setState({
isShow: !this.state.isShow
})
}}></Navbar>
子 :收到这个回调函数之后 直接调用
<button onClick={this.handleClick}>hide/show</button>
handleClick = () => {
this.props.onEvent()
}
Ref
Ref 你可以用来绑定到 render() 输出的任何组件上。
这个特殊的属性允许你引用 render() 返回的相应的支撑实例( backing instance )。这样就可以确保在任何时间总是拿到正确的实例。
父组件:可一获取到Input组件的实例,并且对他内部的状态值进行修改
<Input ref='mytext' />
<button onClick={
this.handleClick
}>add</button>
handleClick = () => {
console.log(this.refs.mytext.state.mytext)// 拿到值
this.refs.mytext.reset() // 清空输入框
}
子组件:
class Input extends Component {
state = {
mytext: ''
}
reset = () => {
this.setState({
mytext: ''
})
}
<div>
<div>others input</div>
<input value={this.state.mytext} type='text' style={{ background: 'yellow' }} onChange={(ev) => {
this.setState({
mytext: ev.target.value
})
发布订阅模式
自己写一个发布订阅模式来传递信息
**事件总线:**用来观察订阅者和发布者,如果发现发布者发送了信息,将信息立刻发送给订阅者
const EventChannel = {
list: [],
subscribe(callback) {
this.list.push(callback)
},
dispatch(data) {
this.list.forEach(item => {
item(data)
})
}
}
订阅者:把自身的回调存储在事件总线,事件总线遍历调用 发布者调用发布方法可以传入发布者自身的参数 订阅者即可获取到
class Child3 extends Component {
// 创建成功 ,dom挂载完成
componentDidMount() {
observer.subscribe((data) => {
console.log('child3定义的callback', data)
})
// console.log('componentDidMount', '调用订阅方法', observer.subscribe())
}
render() {
return <div style={{ background: 'blue' }}>我是微信用户</div>
}
}
class Child3 extends Component {
// 创建成功 ,dom挂载完成
componentDidMount() {
observer.subscribe((data) => {
console.log('child3定义的callback', data)
})
// console.log('componentDidMount', '调用订阅方法', observer.subscribe())
}
render() {
return <div style={{ background: 'blue' }}>我是微信用户</div>
}
}
**发布者:**调用事件总线的发布方法
发布方法:把订阅者的回调遍历出来 然后调用 遍历中间可以传入自己的参数
class Child2 extends Component {
render() {
return <div style={{ background: 'red' }}>公众号发布者
<button onClick={this.handleClick}>发布</button>
</div>
}
handleClick=()=>{
EventChannel.dispatch('child2的问候')
}
}
context 通信
**基地:**提供自己的状态 以及修改状态的方法
基地代码:
export default class App extends Component {
state = {
text: '私人服务'
}
changeState=(data)=>{
this.setState({
text: data
})
}
render() {
return (
<GlobalContext.Provider value={{
sms: '短信服务',
call: '电话服务',
text: this.state.text,
changeState:this.changeState
}}>
<div>
<Child1></Child1>
</div>
</GlobalContext.Provider >
)
}
}
**通信者:**调用基地修改的方法,传入自己的信息
class Child2 extends Component {
render() {
return <GlobalContext.Consumer>
{context => (
<div style={{ background: 'blue' }}>child2--{context.call}
<button onClick={() => this.handClick(context)}>child2通信</button>
</div>
)
}
</GlobalContext.Consumer>
}
handClick = (context) => {
context.changeState('来自child2的问候')
console.log(context)
}
}
其他通信者:一旦状态改变了 ,可以马上获取到
class Child1 extends Component {
render() {
return <GlobalContext.Consumer>
{context => (
<div style={{ background: 'yellow' }}>child1--{context.text}</div>
)
}
</GlobalContext.Consumer>
}
}
生命周期
一个组件会按照顺序依次经历以下的三个阶段:初始化阶段、运行中阶段、销毁阶段
其中的三个生命周期即将被废弃,不建议使用,增加了两个新的生命周期替代~
初始化阶段
componentWillMount
render 之前最后一次修改状态的机会,在渲染前调用,在客户端也在服务端,
即将被废弃,不建议使用,17版本之后必须加上 UNSAFE_ 才可以工作(UNSAFE_componentWillMount),
废弃理由:
在 ssr 中这个方法将会被多次调用,所以会重复触发多遍,同时在这里如果绑定事件,将无法解绑,导致内存泄漏,变得不够安全高效逐步废弃
UNSAFE_componentWillMount(){
console.log('componentWillMount','ajax',)
}
render
只能访问 this.props 和 this.state,不允许修改状态和 dom,在生命周期中会被多次调用。
componentDidMount
成功 render 并渲染完成真实 dom 之后触发,可以修改 dom, 一般请求数据会写在这个生命周期
componentDidMount() {
console.log('componentDidMount', 'ajax 绑定')
fetch("/test.json").then(res=>res.json()).then(res=>{
console.log(res.data)
this.setState({
list: res.data.films
})
})
}
运行中阶段
componentWillReceiveProps
父组件修改属性触发(子组件使用) 会走多次 可以在这里获取到id
即将被废弃,不建议使用,17版本之后必须加上 UNSAFE_ 才可以工作(UNSAFE_componentWillReceiveProps)
废弃理由:
外部组件多次频繁更新传入多次不同的props,会导致不必要的异步请求
这个生命周期有新的生命周期替换—— **getDerivedStateFromProps ** 可以看后面的介绍~
// 会走多次 可以在这里获取到id 更新 mount 只会走一次
UNSAFE_componentWillReceiveProps(nextProps) {
console.log('componentWillReceiveProps')
console.log('获取到ajax数据', nextProps.myname)
}
shouldComponentUpdate
**返回 false 会阻止 render 调用 **
可以做性能调优函数 可以获取到新的状态和老的状态然后对比状态,如果状态没有改变不重新渲染
shouldComponentUpdate(nextProps, nextState) {
// 性能调优函数 //新的状态和老的状态
console.log('shouldComponentUpdate',this.state.myname,nextState.myname)
if (this.state.myname !== nextState.myname){
return true //返回 true 会自动渲染 这个是手动自己去对比 dom 是否发生改变再去调用 render
// react有自动优化能力 —— PureComponent(看后面~)
}
return false //false 不会重新渲染
}
componentWillUpdate
不允许修改属性和状态,会被触发多次,即将被废
废弃理由:
更新前记录 DOM 状态,可能会做一些处理,与 componentDidUpdate 相隔时间如果太长,会导致状态不可信,有新的生命周期—— getSnapshotBeforeUpdate 替换
UNSAFE_componentWillUpdate(){
console.log('componentWillUpdate')
}
render
只能访问 this.props 和 this.state,不允许修改状态和 dom,在生命周期中会被多次调用
componentDidUpdate
在组件完成更新后立即调用,在初始化时不会被调用,可以修改dom
销毁阶段
componentWillUnmount
在组件从 DOM 中移除之前立刻被调用,在删除组件之前进行清理操作,比如计时器和事件监听器。
新增加的两个生命周期
getDerivedStateFromProps
同步作用:
1.可以改老状态
2.不管是初始化 或者更新 都可以拿到父组件属性
componentWillReceiveProps(nextProps) //改变才会获取到属性
// 里面不能用this 必须用static
static getDerivedStateFromProps(nextprops, state) {
//初始化属性 和更新属性都可以获取到
document.title = nextprops.mytitle
console.log(nextprops, state)
return null //状态不改变
return {
myname: state.myname.substring(0, 1).toUpperCase()
} //返回一个新的状态 可以在次修改状态
}
异步作用:
在getDerivedStateFromProps 中不可以做ajax请求,必须和其他生命周期配合使用
componentDidMount() {
console.log('发ajax', this.state.myid)
}
// componentWillReceiveProps() {
// console.log('发ajax')
// }
static getDerivedStateFromProps(nextprops, state) {
console.log('getDerivedStateFromProps','获取到id值', nextprops.id)
return {
myid: nextprops.id
}
}
getSnapshotBeforeUpdate
可以在更新之前获取到状态
//data 接收到了getSnapshotBeforeUpdate的返回值
componentDidUpdate(prevProps, prevState,data) {
console.log('componentDidUpdate', data)
}
// 在render 生命之后 在已经更新完之前 可以准确获取 返回之后的状态
// componentDidUpdate第三个参数可以获取到这个值
getSnapshotBeforeUpdate = (prevProps, prevState) => {
console.log('getSnapshotBeforeUpdate','获取滚动条的位置')
return {
y:100
}
}
React 中性能优化的方案
1、shouldComponentUpdate
手动控制组件自身或者子组件是否需要更新,尤其子组件非常多的情况,不适合这个方案
2、PureComponent
PureComponent 是 React 提供的自动优化 ,会帮你比较新的 props 跟 旧的 props ,
取决于值是否相等(值相等,或者对象含有相同的属性、且属性值相等),决定 shouldComponentUpdate 返回true 或者 false,从而决定要不要呼叫 render function
**优点:**可以减少重复生命周期的执行 会自动对比虚拟dom等
**缺点:**如果你的state 或者 props '永远都会变',那 PureComponent 并不会更加快,因为 shallowEqual 也需要花时间,组件如果需要实时更新 可以用 shouldComponentUpdate
import React, { Component, PureComponent } from 'react'
export default class App extends PureComponent {}
插槽
this.props.children 获取到的是内容数组
Child 组件:
const Child = (props)=>{
return <div>
child--{props.children}
</div>
}
使用这个组件:
export default function App() {
return (
<div>
{/* 插槽 */}
<Child>
<li>11111</li>
<li>222222</li>
</Child>
</div>
)
}
路由
安装:
cnpm i --save react-router-dom
HashRouter模式下 多次点相同路径会被警告
解决方案: 换成history模式 : BrowserRouter
写法:
react-router-dom 4.5 版本写法一致:
//
import {
HashRouter as Router, //路由外层需要包裹的组件 hash模式
// BrowserRouter(后端配置 history 模式)
Route ,//每个路由组件都需要此组件
}from 'react-router-dom'
import React from 'react'
import Home from '../views/home/Home'
import Login from '../views/login/Login'
// class BlogRouter extends Comment{
// }
** 函数式组件写法:**
const BlogRouter=()=>(
<Router>
<Route path='/home' component={Home}/>
<Route path='/login' component={Login}/>
</Router>
)
export default BlogRouter
app.js
import BlogRouter from './router'
class App extends Component{
render(){
return(
<div>
<BlogRouter/>
</div>
)
}
}
export default App;
嵌套路由
嵌套路由两种写法:
1.在父组件中直接写:
import { Route} from 'react-router-dom'
import Right from './Right'
import Role from './Role'
export default class Manage extends Component {
render() {
return (
<div>
<ul>
<li>权限列表</li>
<li>角色列表</li>
<Route path='/right-manage/right' component={Right} />
<Route path='/right-manage/roles' component={Role} />
</ul>
</div>
)
}
}
- 在路由中写:
import Manage from '../views/rightmanage/Manage'
import Right from '../views/rightmanage/Right'
import Role from '../views/rightmanage/Role'
<Route path='/right-manage' render={()=>
(<Manage>
<Switch>
<Route path='/right-manage/rights' component={Right} />
<Route path='/right-manage/roles' component={Role} />
<Redirect from='/right-manage' to='/right-manage/roles' />
</Switch>
</Manage>)
}/>
** 在父组件中留坑:**
{this.props.children}
路由重定向
** 一定要包 Switch ** 一旦匹配上就不会再继续匹配 会直接跳出
import {
Route,
Redirect,//重定向
Switch//匹配到第一个符合条件路径的组件,就停止了
} from 'react-router-dom'
export default class DashBorad extends Component {
render() {
return (
<div>
<div>顶部导航栏</div>
<Switch>
{/* 重定向 */}
<Redirect from='/' to='/home' exact />
<Route path='*' component={Notfind} />
</Switch>
</div>
)
}
}
跳转页面
路由渲染跳转:
<Route path='/artic-manege/preview/:myid' component={Prebiew} />
编程式跳转页面:
this.props.history.push(`/artic-manege/preview/${id}`)
获取到传递的值:
this.props.match.params.myid
高阶组件(withRouter)
高阶组件,获取低阶组件,生成高阶组件 可以实现路由包裹跳转清空等
有些组件没有被 router 包围,会获取不到会有 this.props ,可以用高阶组件进行包裹,然后获取其this.props
import {withRouter} from 'react-router' //路由
//获取到 this.props 跳转至 home
this.props.history.push('/home')
export default withRouter(SideMenu)
获取不到 this.props 解决方案
除了用高阶组件的方法,还可以用父组件传递this.props给子组件
父组件:
annahistory={this.props.history}
子组件:
this.props.kerwinhitory.push(obj.key)
Redux
Redux 主要用作应用状态的管理,即 Redux 用一个单独的常量状态树(对象)保持这一整个应用的状态,这个对象不能直接被改变。如果一些数据变化了,一个新的对象就会被创建(使用 action 和 reducers )
Redux 的工作流程:
同步写法
store.js 文件:
import {createStore} from 'redux'//createStore 方法创建一个store回想
// 创建一个reducer ,
//'修改状态'(接收老状态,修改的值,深复制之后,再返回一个新的状态)
const reducer=(prevState={
// 设置一个初始值
iscollapsed:false
},action)=>{
console.log(action)
// 深复制一份新的把action里面获取的值 返回出去
var newstate={...prevState}
newstate.iscollapsed=payload
return newstate
}//只要状态已返回,会自动更新
const store=createStore(reducer)
export default store
发布者(发布自己的状态)
store.dispatch({
type:'mysideMenuCollapsed',
payload: iscollapsed
});//store 在action里面可以获取到发布者的值
订阅者(做出改变者,要获取新的状态)
componentDidMount() {
// 订阅 注意一定要取消订阅
this.unscribe= store.subscribe(()=>{
//store.getState() 这个可以获取到新的状态
console.log('有人通知我更新了',store.getState())
this.setState({
collapsed: store.getState().iscollapsed
})
})
}
componentWillUnmount(){
// 取消订阅
this.unscribe()
}
异步写法
1、redux-Thunk
redux-thunk可以在actionCreator中返回一个函数,将函数执行,并传入dispatch和getState两个参数给这个函数,我们可以在任意时候dispatch
store.js:
import { createStore ,applyMiddleware} from 'redux'//createStore 方法创建一个store回想
import reduxThunk from 'redux-thunk'
// 创建一个reducer ,
//'修改状态'(接收老状态,修改的值,深复制之后,再返回一个新的状态)
const reducer = (prevState = {
// 设置一个初始值
roleList:[]//角色侧边导航数据
}, action) => {
let { type, payload } = action
switch (type) {
case 'setRoleList':
// 深复制 不能在prevState(老状态上直接修改)需要返回一个新的状态
var newstate = { ...prevState }
newstate.roleList= payload
return newstate
default :
return prevState
}
}//只要状态已返回,会自动更新
// 默认 action 只能是普通对象{type:''}
// 创建store 顺便应用中间件thunk 如果action是函数,我来处理
const store = createStore(reducer,applyMiddleware(reduxThunk))
export default store
订阅者和发布者案例:
role.js:
actionCreater = () => {
// middleware 解决异步处理redux-thunk redux-promise
return (dispatch) => {
axios.get("http://localhost:8000/roles").then(res => {
console.log(res.data)
// 自己决定什么时候发送
dispatch({
type: 'setRoleList',
payload: res.data
})
})
}
}
componentDidMount() {
if (store.getState().roleList.length == 0) {
//发ajax
store.dispatch(this.actionCreater())
} else {
console.log('使用缓存', store.getState().roleList)
this.setState({
datalist: store.getState().roleList
})
}
//数据改变了 订阅获取到新的数据
this.unscribe = store.subscribe(() => {
console.log("请求数据结束", store.getState().roleList)
this.setState({
datalist: store.getState().roleList
})
})
}
componentWillUnmount() {
// 取消订阅
this.unscribe()
}
2、redux-promise
redux-promise可以在actionCreator中返回一个promise对象,他会等待成功后将成功后的结果派发出去
store.js
import reduxPromise from 'redux-promise'
import { createStore, applyMiddleware } from 'redux'//createStore 方法创建一个store回想
const reducer = (prevState = {
// 设置一个初始值
rightList: [],//角色侧边导航数据
}, action) => {
console.log(action)
// 深复制
let { type, payload } = action
switch (type) {
case 'setRightsList':
var newstate = { ...prevState }
newstate.rightList = payload
return newstate
default:
return prevState
}
}//只要状态已返回,会自动更新
const store = createStore(reducer, applyMiddleware(reduxPromise))
export default store
订阅者和发布者案例:
role.js:
actionCreater = () => {
// 返回一个promise对象
return axios.get("http://localhost:8000/rights").then(res => {
return {
type: 'setRightsList',
payload: res.data
}
})
}
componentDidMount() {
if (store.getState().rightList.length == 0) {
//发ajax
store.dispatch(this.actionCreater()).then(data=>{
this.setState({
datalist: store.getState().rightList
})
})
} else {
console.log('使用缓存', store.getState().rightList)
this.setState({
datalist: store.getState().rightList
})
}
}
核心API-Reducer
Reducer保证是纯函数
纯函数
1.对外界没有副作用的函数
2.同样的输入,得到同样的输出
var myname='anna'
function test(myname){
myname='xiaoming'
}
test(myname)
非纯函数:
var myname='anna'
function test(){
myname='xiaoming'
}
test()
拆分
可以将 Reduer 按照业务模块去拆分
store.js
import { createStore, applyMiddleware ,combineReducers} from 'redux'//createStore 方法创建一个store回想
import reduxThunk from 'redux-thunk'
import reduxPromise from 'redux-promise'
import collapseReducer from './reducers/collapseReducer'
import rightListReducer from './reducers/rightListReducer'
import roleListReducer from './reducers/roleListReducer'
const reducer = combineReducers({
iscollapsed: collapseReducer,
roleList: roleListReducer,
rightList: rightListReducer
})
const store = createStore(reducer, applyMiddleware(reduxThunk, reduxPromise))
export default store
collapseReducer
const collapseReducer = (prevState = false, action) => {
let { type, payload } = action
switch (type) {
case 'sideMenuShow':
return payload
default:
return prevState
}
}
export default collapseReducer
roleListReducer.js
const roleListReducer = (prevState =[], action) => {
let { type, payload } = action
switch (type) {
case 'setRoleList':
var newstate = { ...prevState }
newstate = payload
return newstate
default:
return prevState
}
}//只要状态已返回,会自动更新
export default roleListReducer
React-Redux
同步写法
app.js
import { Provider } from 'react-redux'
import store from './redux/store
<Provider store={store}>
<BlogRouter/>
</Provider >
发布者
把方法映射成属性用
第一个参数是商量好的那个属性传给孩子
第二个参数把方法映射成属性用
import { connect } from 'react-redux'
const mapStateToprops=()=>{
return {
}
} //state 映射成属性用
const mapDispathToProps={
actionCreator:(iscollapsed)=>{
return {
type: 'sideMenuShow',
payload: iscollapsed
}
}
}
export default withRouter(connect(mapStateToprops,mapDispathToProps)(TopHeader))
订阅者接收
import { connect } from 'react-redux'
const mapStateTopprops=(state)=>{
return {
iscollapsed:state.iscollapsed
}//约定isCollapsed 属性
}
export default withRouter(connect(mapStateTopprops)(SideMenu))
异步
if (this.props.datalist.length == 0) {
//直接调用改方法 会把状态传递给redux
this.props.setList()
}
//订阅者 state 中可以获取到redux中的状态
const mapStateToprops = (state) => {
return {
datalist:state.rightList
}
} //state 映射成属性用
//会自动传递给redux
const mapDispathToProps = {
setList : () => {
// 返回一个promise对象
return axios.get("http://localhost:8000/rights").then(res => {
// 自己决定什么时候发送
return {
type: 'setRightsList',
payload: res.data
}
})
}
}
// } //把方法映射成属性用
export default connect(mapStateToprops,mapDispathToProps)(Right)
Redux和React-Redux关系
mobx
1、box方法
只能观察 简单数据类型
store.js
import { observable } from 'mobx'
const store = observable.box(true)
export default store
// 传播者
import store from '../../mobx/store'
store.set(false)
// 接收者
import store from '../../mobx/store'
import { autorun } from 'mobx'
autorun(() => {
console.log(store.get())
})
2、map方法
观察复杂数据类型
const store = observable.map({
isshow:true,
list:[],
roleList:[],
rightList:[]
})
store.set('isshow',false)
mobx 优点:
- mobox写法上更偏向于oop
- mobox 对一份数据直接进行修改操作,不需要始终返回一个新的数据
- mobox 并非单一的 store。可以多 store
- redux 默认以 javaScript 原生对象形式存储数据,而 mobx 可以用来观察对象
mobx 缺点:
mobx提供的约定及模板代码很少,代码编写很自由,如果不做一些约定,比较容易导致团队代码风格不统一
相关中间件很少,逻辑层业务整合式问题
遇到的bug
第一个bug:
**
**
解决方案:
取消观察
this.cancel = autorun(() => {
this.setState({
code: store.get('isshow')
}
//取消观察
componentWillUnmount() {
this.cancel()//取消观察
}
第二个bug:
**
**
解决方案:
网速很慢的时候数据没有回来 ajax请求的数据没有回来
componentWillUnmount() {
this.setState=()=>{}
console.log('列表销毁','取消ajax')
}
React Hooks
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
useState写法
import React,{useState}from 'react'
export default function App() {
const [name, setName] = useState('anna')//初始值[状态,改变状态的方法]
const [age, setAge] = useState('12')//初始值[状态,改变状态的方法]
return (
<div>
app-{name}-{age}
<button onClick={()=>{
setName('xiaoming')
setAge('18')
}}>click</button>
</div>
)
}
获取ref
import React, { useState, useRef }from 'react'
const mytext = useRef(null)
<input type='text' onChange={(ev)=>{
settext(ev.target.value)
}} ref={mytext}/>
点击事件
<button onClick={() => handleDeleClick(index)}>dele</button>
const handleDeleClick=(index)=>{
console.log(index)
var newlist=[...list]
newlist.splice(index,1)
setlist(newlist)
}
替代复杂的生命周期(useEffect)
格式: useEffect(处理函数,[依赖])
如果依赖传的是一个空数组,相当于 componentWillMount , 挂载前,只执行一次
如果第二个参数不传,代表任何状态改变,都会重新执行
useEffect(()=>{
},[])
** 更新: [依赖] 只有依赖改变的时候 才会执行一次**
// age 更新会重新执行
useEffect(()=>{
console.log('创建或更新')
},[age])
创建/销毁
useEffect(() => {
var id=setInterval(() => {
console.log(111)
}, 1000);
console.log('创建')
return () => {
// cleanu
clearInterval(id)
console.log('销毁')
}
}, [])
获取props
export default function Prebiew(props) {}
useCallback 提高运行效率
防止因为组件重新渲染,导致方法被重新创建,提高性能
const test=useCallback(
() => {
console.log(text)
},
[text]
)//闭包,缓存函数,提高性能
test()
useReducer和useContext
在 hooks 中提供了的 useReducer 功能,可以增强 ReducerDemo 函数提供类似 Redux 的功能,引入 useReducer 后,useReducer 接受一个 reducer 函数作为参数,reducer 接受两个参数一个是 state 另一个是 action 。然后返回一个状态 count 和 dispath,count 是返回状态中的值,而 dispatch 是一个可以发布事件来更新 state 的
reducer.js
const reducer = (prevstate, action) => {
let { type, payload } = action
switch (type){
case "Change_text":
// 深复制
return {
...prevstate, text: payload
}
case "Change_list":
// 深复制
return {
...prevstate, list: payload
}
}
return prevstate
}
export default reducer
index.js (GlobalContext )
index.js (GlobalContext )
import React from 'react'
const GlobalContext = React.createContext()
export default GlobalContext
app.js
import GlobalContext from './store/index'
import reducer from './store/reducer'
import React ,{useReducer,useContext}from 'react'
const App=() =>{
// 表示reducer 传入初始值 代表reducer管理这些状态
const [state, dispatch] = useReducer(reducer, {
isShow: true,
list: [],
text: "我是公共的状态"
}) //[公共的状态,改变公共状态的方法]
return <GlobalContext.Provider value={{
state,
dispatch
}}>
<Child1/>
</GlobalContext.Provider>
}
//获取到app传来的信息 state可以直接获取到 dispatch 可以进行修改状态
const Child1=()=>{
let { state, dispatch } = useContext(GlobalContext) //不需要consumer
// console.log(useContext(GlobalContext))
return <div>
//同步:
child1-{state.text}<button onClick={()=>{
dispatch({type:'Change_text',
payload:'child1111111'
})
}}>click</button>
</div>
// 异端:
axios.get("http://localhost:8000/users").then(res=>{
console.log(res.data)
dispatch({
type: 'Change_list',
payload: res.data
})
})
}
自定义Hooks
** 当我们想在两个函数之间共享逻辑时,可以把它提取到第三个函数中**
必须以'use'开头吗?
必须如此。这个约定非常重要,不遵循的话,由于无法判断某个函数是否包含其内部 Hook 的调用,React 将无法自动检测你的Hooks 是否违反了Hook的规则
//为preview 组件提供数据
const usePrebiewDate = (props)=>{
const [title, settitle] = useState('')
const [content, setcontent] = useState('')
const [category, setcategory] = useState([])
useEffect(() => {
axios.get(`http://localhost:8000/articles/${props.match.params.myid}`).then(res => {
console.log(res.data)
let { title, category, content } = res.data
settitle(title);
setcontent(content);
setcategory(category);
})
return () => {
}
}, [props])
return {
title,
content,
category
}
}
let { title, content, category}= usePrebiewDate(props)
实战
文章管理的后台管理系统——地址
关于项目一些逻辑会后续再更新~ 以上内容如果存在一些疑问的地方可以联系我~ 看到会及时回复