Redux是一个用于JavaScript状态容器,提供可与预测化的状态管理。
Redux可以让你构建一致化的应用,运用于不同的环境(客户端,服务器,原生应用),并且易于测试。
Redux除了和React一起使用外,还支持其他界面库,而且它体小精悍。
随着JavaScript单页面开发日趋复杂,JavaScript需要管理更多的state,这些state可能包括服务器响应、缓存数据、本地生成服务器的数据,也包括UI状态等。
管理不断变化的state非常麻烦,如果一个model的变化会引起另一个model变化,那么view变化时,就可能引起对应model以及另一个model的变化,依次可能引起另一个view的变化。所以就会产生混乱。而redux就是为了解决这个问题。
单一数据源
整个应用的state被存储在一颗object tree中,并且这个object tree只存在唯一一个store中。
state是只读的
唯一改变state的方法就是触发action,action是一个描述一发生事件的普通对象
这样确保了视图和网络请求都不能直接去修改state,相反,它们只能表达想要修改的意图,因为所有的修改都被集中化处理,并且按照一个接一个的顺序执行。
使用纯函数来执行修改
为了描述action如何改变state tree,你需要去编写reducers
reducers只是一些纯函数,它接收先前的state和action,并且返回新的state。
可以复用、可以控制顺序、传入附加参数。
就是我们传递的数据,那么我们在用React开发项目的时候,大致可以把state分为三类
Action是把数据从应用传到store的载体,它是store数据的唯一来源,一般来说,我们可以通过store.dispatch()将action传递给store
Action特点
Reducer本质就是一个函数,它用来响应发送过来的action,然后经过处理,把state发送给Store的
注意:在Reducers函数中,需要return返回值,这样Store才能接收到数据
函数会接收两个参数,第一个参数是初始化state,第二个参数是action
Store就是把action与reducers联系到一起的对象
主要职责
npx create-react-app redux-demo
删除多余文件
在src下创建pages文件夹,在pages文件夹下创建home文件夹,在home文件夹下创建Home组件
编写一个简单的结构样式
import React, { Component } from 'react';
class Home extends Component {
render() {
return (
<button>点我点我,发送一个action</button>
);
}
}
export default Home;
在App.js中引入这个组件
import React from 'react';
import Home from './pages/home'
function App() {
return (
<div className="App">
<Home />
</div>
);
}
export default App;
安装redux
在src目录底下创建一个文件夹action
在该目录下创建一个index.js文件用来构建Action
在action创建函数里面利用return返回一个action对象,注意需要携带type属性
把这个action创建函数进行导出
const sendAction = () => {
return {
type: 'send_type',
value: '我是一个action'
}
}
module.exports = {
sendAction
}
在src目录下创建一个文件夹reducers
在该目录下创建一个index.js文件用来创建reducers,注意reducers要接收两个参数
第一个参数是state,我们可以定义一个初始化state,然后进行赋值
在函数里面判断第二个参数action的type值是否是我们发送的
如果是的话,我们可以通过return返回新的state
把reducer进行导出
const initState = {
value: '默认值'
}
const reducer = (state = initState, action) => {
switch(action.type) {
case 'send_type':
return Object.assign({}, state, action);
default:
return state
}
}
module.exports = {
reducer
}
在src目录下创建一个文件夹store
在该目录下创建一个index.js文件用来构建store,注意createStore函数里面第一个参数接收的是reducers
我们需要导入刚刚创建的reducers,然后设置到函数里面去
createStore的返回值就是我们构建好的store,然后进行导出
import {createStore} from 'redux';
import {reducer} from '../reducer';
const store = createStore(reducer);
export default store;
在页面中的button按钮绑定一个点击事件
在组件加载完毕的时候我们通过store来进行 监听器 的注册,返回值可以用来注销监听
在点击事件处理函数中,通过store.dispatch来发送一个action
import React, { Component } from 'react';
import store from '../../store';
import {sendAction} from '../../action';
class Home extends Component {
handClick = () => {
const action = sendAction();
store.dispatch(action);
}
componentDidMount() {
store.subscribe(() => {
this.setState({})
})
}
render() {
return (
<>
<button onClick={this.handClick}>点我点我,发送一个action</button>
<div>{store.getState().value}</div>
</>
);
}
}
export default Home;
Redux与React之间是没有关系的,Redux支持React, Angular,JQuery甚至是JavaScript
Redux与React这库搭配起来更好用
react-redux就是Redux官方出的用于配合React的绑定库
react-redux能够使你的React组件从Redux store中很方便的读取数据,并且向store中分发Actions以此来更新数据
react-redux不是官方提供的,所以当我们构建react项目之后,需要进行安装
react-redux还需要依赖于redux中的store,所以我们还需要安装redux
npm install redux react-redux
创建reducer/index.js文件,构建reducer来响应actions
exports.reducer = (state,action) => {
return state;
}
创建store/index.js文件,通过createStore方法把我们的reducer传入进行
import {createStore} from 'redux';
import {reducer} from '../reducer';
const store = createStore(reducer);
export default store;
在app.js中引入store
import React from 'react';
import store from './store'
function App() {
return (
<div className="App">
</div>
);
}
export default App;
创建一个组件,名字为ComA,里面放一个button按钮。src/pages/ComA/index.js
import React, { Component } from 'react';
class ComA extends Component {
render() {
return (
<button> + </button>
);
}
}
export default ComA;
创建另一个组件,名字叫ComB,里面放一个div,用来显示数字。src/pages/ComB/index.js
import React, { Component } from 'react';
class ComB extends Component {
render() {
return (
<div> 1 </div>
);
}
}
export default ComB;
在app.js中引入两个组件
import ComA from './pages/ComA';
import ComB from './pages/ComB';
function App() {
return (
<div className="App">
<ComA />
<ComB />
</div>
);
}
export default App;
在app.js中导入Provider组件
利用Provider组件将我们整个结构进行包裹,并且传递store
import React from 'react';
import store from './store';
import { Provider } from 'react-redux'
import ComA from './pages/ComA';
import ComB from './pages/ComB';
function App() {
return (
<Provider store={store}>
<div className="App">
<ComA />
<ComB />
</div>
</Provider>
);
}
export default App;
导入connect方法
import { connect } from ‘react-redux’;
调用connect方法
connect(...)(Component)
connect方法会有一个返回值,这个返回值就是加强之后的组件
参数名 | 类型 | 说明 |
---|---|---|
mapStateToProps(state, ownProps) | Function | 这个函数允许我们将store中的数据作为props绑定到组件上 state: redux中store ownProps:自己的props |
mapDispatchToProps(dispatch, ownProps) | Function | 将action作为props绑定到我们自己的函数中 dispatch: 就是store.dispatch ownProps: 自己的props |
mergeProps(stateProps, dispatchProps, ownProps) | Function | 不管是stateProps还是dispatchProps,都需要和ownProps merge之后才会被赋给我们的组件,通常情况下,你可以不传这个参数,connect就会使用Object.assign替代该方法 |
options | Object | 可以定制connector的行为 |
导入connect
利用connect对组件进行加强
connect(要接受数组的函数, 要发送action的函数)(嵌入要加强的组件)
这里需要实现connect的第二个参数
构建一个函数mapDispatchToProps(dispatch)
dispatch用来发送给action
在这个函数中返回一个对象,key是方法名,value:调用dispatch去发送action
在组件的内容中就可以通过this.props拿到这个方法
import React, { Component } from 'react';
import {connect} from 'react-redux';
class ComA extends Component {
handleClick = () =>{
this.props.sendAction()
}
render() {
return (
<button onClick={this.handleClick}> + </button>
);
}
}
const mapDispatchToProps = (dispatch) => {
return {
sendAction: () => {
dispatch({
type: 'add_action'
})
}
}
}
export default connect(null, mapDispatchToProps)(ComA)
导入connect方法
利用connect对组件进行加强
ComB属于接收方,就需要实现connect的第一个参数
mapStateToProps里面的第一个参数就是我们state
将这个state进行return才能在组件的内部获取到最新的数据
//reducer
const initState = {
count: 0
}
exports.reducer = (state = initState, action) => {
switch(action.type) {
case 'add_action':
return {
count: state.count + 1
}
default:
return state;
}
}
//ComB
import React, { Component } from 'react';
import { connect } from 'react-redux';
class ComB extends Component {
render() {
return (
<div> {this.props.count} </div>
);
}
}
const mapStateToProps = state => {
return state;
}
export default connect(mapStateToProps)(ComB);
npx create-react-app redux-demo
npm install redux react-redux
删除多余文件
创建两个组件boy、girl
引入素材资源
组件内部导入对应的素材资源,注意react中图片不要在src中引入,要先导入图片
搭建组件内部结构
import React from 'react';
import defaultImg from '../../assets/image/发射.png';
import sendImg from '../../assets/image/发射.gif';
class Boy extends React.Component {
state = {
isSend: false
}
handleClick = () => {
let { isSend } = this.state;
this.setState({
isSend: !isSend
})
}
render() {
return (
<img src={this.state.isSend ? sendImg : defaultImg} alt="" />
<div>
<button onClick={this.handleClick}>{this.state.isSend ? "停止发射" : "发射爱心"}</button>
</div>
)
}
}
export default Boy
import React from 'react';
import defaultImg from '../../assets/image/接收.png';
import reciveImg from '../../assets/image/接收.gif';
class Girl extends React.Component {
render() {
return (
<div>
<img src={defaultImg} alt="" />
</div>
)
}
}
export default Girl
//App.js
import React from 'react';
import Boy from './pages/Boy';
import Girl from './pages/Girl';
function App() {
return (
<div className="App">
<Boy />
<Girl />
</div>
);
}
export default App;
创建reducer,在函数里面判断action的type属性,然后返回状态/
// src/reducer/index.js
const initState = {
state:false
}
exports.loveReducer = (state = initState, action) => {
return state
}
利用createStore来构建store
// src/store/index.js
import { createStore } from 'redux';
import { loveReducer } from '../reducer';
export default createStore(loveReducer);
在App.js中导入store
在App.js中导入Provider,在根组件上对整个结构进行包裹,然后设置store属性,来统一对store进行管理
//App.js
import React from 'react';
import store from './store';
import { Provider } from 'react-redux';
import Boy from './pages/Boy';
import Girl from './pages/Girl';
function App() {
return (
<Provider store={store} >
<div className="App">
<Boy />
<Girl />
</div>
</Provider>
);
}
export default App;
Boy组件属于发送方,所以需要通过dispatch来发送action
导入connect方法
利用connect来对Boy组件进行一层加强,然后导出
connect第一个参数属于要接收,我们这个组件不需要接收,所以可以写null
实现第二个参数,在返回对象的时候,定义两个属性,一个是发送了爱心的action,一个是停止发送爱心的action
在组件内部就可以通过this.props来拿到对应的触发action的函数对象
判断当前状态是否发送爱心,发送对应的action
import React from 'react';
import defaultImg from '../../assets/image/发射.png';
import sendImg from '../../assets/image/发射.gif';
class Boy extends React.Component {
state = {
isSend: false
}
handleClick = () => {
let { isSend } = this.state;
isSend ? this.props.stopLove() : this.props.sendLove();
this.setState({
isSend: !isSend
})
}
render() {
return (
<img src={this.state.isSend ? sendImg : defaultImg} alt="" />
<div>
<button onClick={this.handleClick}>{this.state.isSend ? "停止发射" : "发射爱心"}</button>
</div>
)
}
}
const mapDispatchToProps = dispatch => {
return {
sendLove: () => {
dispatch({ type: 'send_love' })
},
stop_love: () => {
dispatch({ type: 'stop_love' })
}
}
}
export default connect(null, mapDispatchToProps)(Boy)
这里发送的有两个action,所以可以对action的type来进行判断
根据不同的type,我们来返回不同的值
// src/reducer/index.js
const initState = {
state:false
}
exports.loveReducer = (state = initState, action) => {
switch(action.type) {
case 'send_love':
return status: true;
case 'stop_love':
return status: false;
default:
return state
}
}
Girl组件属于接收方,所以我们需要通过connect第一个参数来获取到发送来的state
导入connect方法
利用connect来对Girl组件进行一层加强,然后导出
实现第一个参数,直接把传递过来的数据返回给组件内部
在组件内部就可以通过this.props来拿到对应的数据
判断一下传递过来的数据里面的value值是否为true,来展示对应图片
import React from 'react';
import { connect } from 'react-redux'
import defaultImg from '../../assets/image/接收.png';
import reciveImg from '../../assets/image/接收.gif';
class Girl extends React.Component {
render() {
return (
<div>
<img src={this.props.state ? reciveImg : defaultImg} alt="" />
</div>
)
}
}
const mapStateToProps = state => {
return state;
}
export default connect(mapStateToProps)(Girl)
学习自:
redux全局状态管理学习路线之一 : redux&react-redux
文档:
Redux 中文文档
相关博客:
React 项目结构和组件命名规范
react入门之 Redux与flux的比较学习
Redux & 与vuex的对比
vuex和redux
Flux、Redux、Vuex、MobX 总结-概念篇