redux状态管理,不光react可以用,vue,angular都可以用。


Redux 本身很简单。当使用普通对象来描述应用的 state 时。这个对象就像 “Model”,区别是它并没有 setter(修改器方法)。因此其它的代码不能随意修改它,造成难以复现的 bug。
要想更新 state 中的数据,你需要发起一个 action。Action 就是一个普通 JavaScript对象(注意到没,这儿没有任何魔法?)用来描述发生了什么。强制使用action来描述所有变化带来的好处是可以清晰
地知道应用中到底发生了什么。如果一些东西改变了,就可以知道为什么变。action就像是描述发生了什么的指示器。最终,为了把action和state串起来,开发一些函数,这就是reducer。再次地,没有任何魔
法,reducer 只是一个接收 state 和 action,并返回新的 state 的函数。 对于大的应用来说,不大可能仅仅只写一个这样的函数,所以我们编写很多小函数来分别管理state的一部分,再开发一个reducer
调用这两个 reducer,进而来管理整个应用的 state,差不多就是 Redux 思想的全部。

状态管理

一张图了解redux工作

redux
redux

例如初始一个状态管理,这是我建好的项目里面的三个js,index.js负责渲染,App.js是个组件,index.redux.js负责状态管理
index.js
1
2
3
4
5
6
7
8
9
10
11
import React from 'react'
import ReactDom from 'react-dom'
import App from './App'
import { createStore } from 'redux'
import { center } from './index.redux'
const store=createStore(center)

ReactDom.render(
<App store={store}/>,
document.getElementById('root')
)

App.js

1
2
3
4
5
6
7
8
9
10
11
12
13
import React,{Component} from 'react'

class App extends Component{
constructor(props){
super(props);
}
render(){
const sTore=this.props.store;
const data=sTore.getState();
return <h1>hellow app,现在有菜鸡${data}只</h1>
}
}
export default App

index.redux.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const ADD_CAI='加菜鸡';
const REMOVE_CAI='减菜鸡'

export function center(state=0,action){
switch(action.type){
case ADD_CAI:
return state+1;
case REMOVE_CAI:
return state-1;
default :
return 10
}
}

//action creator专门创建action
export function addCai(){
return {type:ADD_CAI}
}
export function removeCai(){
return {type:REMOVE_CAI}
}

这是初始的一个状态
这是初始的一个状态

改变状态的话,在App.js里面导入在index.redux.js里头定义的addCai,removeCai方法,然后添加两个按钮用来触发

1
2
3
<h1>hellow app,现在有菜鸡${data}只</h1>
<button onClick={()=>store.dispatch(addCai())}>添加一只菜鸡</button>
<button onClick={()=>store.dispatch(removeCai())}>菜死一只菜鸡</button>

这时候执行的话,其实状态已经改变了但是h1里面的数值没有改变,原因是状态虽然改变了,但是没有刷新,所以页面显示还是年轻的模样,只需要在index.js里面执行一下,store.subs
cribe(render),意思就是把render重新执行一下,这时候状态改变就可以了

1
2
3
4
5
6
7
8
9
function render(){
ReactDom.render(
<App store={store}/>,
document.getElementById('root')
)
}
render()
//状态改变之后执行一下render
store.subscribe(render)

render
render

当然,也可以把App.js里头import的两个方法转移到index.js,然后通过给子组件传值的方式传给App.js,这样代码看起来可能更和谐。

redux异步

redux默认只处理同步,异步需要中间件redux-thunk

1
npm install redux-thunk --save

异步中间件使用,在index.js里头

1
2
3
4
5
6
7
import { createStore,applyMiddleware } from 'redux'
import thunk from 'redux-thunk'

const store=createStore(
center,
applyMiddleware(thunk)
)

这时候就可以异步了,比如在index.redux.js里面模拟一个异步操作testAsync,然后在App.js里面添加一个button用来触发testAsync。

1
<button onClick={()=>store.dispatch(testAsync())}>凌迟处死一只菜鸡</button>

render
render

react连接redux状态管理操作

将render函数改成

1
2
3
4
5
6
7
8
9
import { Provider } from 'react-redux'
ReactDom.render(
(
<Provider store={store} >
<App/>
</Provider>
),
document.getElementById('root'),
)

App.js里面导入connect以及index.redux里头的方法,这时候button里面的onClick事件处理函数直接可以写成{函数名}的形式,因为它这时候所有的数据都是直接从外部传进来的,所有的数据都是由index.react.js控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import { connect } from 'react-redux'
import {addCai,removeCai,testAsync} from './index.redux'
class App extends Component{
constructor(props){
super(props);
}
render(){
const data=this.props.num;
const addCai=this.props.addCai;
const removeCai=this.props.removeCai;
const testAsync=this.props.testAsync;
return (
<div>
<h1>hellow app,现在有菜鸡{data}只</h1>
<button onClick={addCai}>添加一只菜鸡</button>
<button onClick={removeCai}>菜死一只菜鸡</button>
<button onClick={testAsync}>凌迟处死一只菜鸡</button>
</div>
)
}
}

const mapStateToProps=(state)=>{
return { num: state}
}

const actionCreators= {addCai,removeCai,testAsync}

App=connect(mapStateToProps,actionCreators)(App)
export default App

connect的第一个参数,把state给到props,第二个参数也给到props,也就是属性和方法

react-redux
react-redux

装饰器

用装饰器之前先装一下这个插件

1
npm install babel-plugin-transform-decorators-legacy --save

然后在项目的package.json里面添加

1
2
3
4
5
{
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }],
]
}

然后就可以做出这样的改变

1
2
3
4
5
6
7
8
9
10
const mapStateToProps=(state)=>{
return { num: state}
}
const actionCreators= {addCai,removeCai,testAsync}
App=connect(mapStateToProps,actionCreators)(App)
//==》
@connect(
state=>({num:state}),//属性
{addCai,removeCai,testAsync} //方法
)

合并所有reducer

1
2
3
4
import combineReducers from 'redux'
import AA from aa.redux.js
import BB from aa.redux.js
combineReducers({AA,BB})

react-router4

react-router4是全新的版本,和之前的版本不兼容,浏览器和react-native均兼容和vue一样,React开发单页应用必备,践行路由即组件的概念比如:动态路由,Route,Link,Switch

安装

1
npm install react-router-dom --save

react-router-dom作为浏览器的路由
简单的路由例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import React from 'react'
import ReactDom from 'react-dom'
import { createStore,applyMiddleware,compose } from 'redux'
import thunk from 'redux-thunk'
import { Provider } from 'react-redux'
import App from './App'
import { center } from './index.redux'

import { BrowserRouter,Route,Link } from 'react-router-dom'

const store=createStore(center,compose(
applyMiddleware(thunk),
window.devToolsExtension? window.devToolsExtension():()=>{}
))//异步中间件

function Cai2(){
return <h1>hwllo this is Cai2</h1>
}
function Cai3(){
return <h1>hello this is Cai3</h1>
}

ReactDom.render(
(
<Provider store={store} >
<BrowserRouter>
<div>
<ul>
<li>
<Link to="/">第一只菜鸡</Link>
</li>
<li>
<Link to="cai2">第二只菜鸡</Link>
</li>
<li>
<Link to="cai3">第三只菜鸡</Link>
</li>
</ul>
<Route path="/" component={App}></Route>
<Route path="/cai2" component={Cai2}></Route>
<Route path="/cai3" component={Cai3}></Route>
</div>
</BrowserRouter>
</Provider>
),
document.getElementById('root'),
)

run
run

但是有个问题,就是点Cai2的时候,组件App也渲染出来,是因为它path匹配的时候根目录是/,而/cai2,/cai3也是有/
,所以都会显示,它其实是个正则匹配,这时候可以选择加一个exact进行完全匹配,就可以了。


假如要添加一个地址跳转错误的友好404,就像这样

1
<Route path="/:localtion" component={Wrong}></Route>

Redirect:默认页面打开加载某个组件或者强制加载某个组件

1
<Redirect to="/"></Redirect>

Switch:只渲染命中的第一个组件

1
2
3
4
5
6
<Switch>
<Route path="/" component={App}></Route>
<Route path="/:localtion" component={Wrong}></Route>
<Route path="/cai2" component={Cai2}></Route>
<Route path="/cai3" component={Cai3}></Route>
</Switch>

React-Router中的HashRouter与BrowserRouter

以下解释的前提是你要懂hash地址,假如有一个 Link 标签,跳转到 /hhardyy

1
2
BrowserRouter: http://localhost:8080/hhardyy
HashRouter: http://localhost:8080/#/hhardyy

如果有服务器端的动态支持,建议使用 BrowserRouter,否则建议使用 HashRouter。原因在于,如果是单纯的静态文件,假如路径从 / 切换到 /hhardyy 后,此时刷新页面,页面将无法正常访问。
二者的替换方法很简单,在import的时候,将 BrowserRouter 修改为 HashRouter 就可以了,不需要修改其他东西。