redux 三大原则
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了redux 三大原则相关的知识,希望对你有一定的参考价值。
参考技术A三大原则是 react 提供 可预测化 状态管理的依据。
整个应用的 state 被储存在 一棵 object tree 中,并且这个 object tree 只存在于 唯一一个 store 中。
唯一改变 state 的方法就是触发 action。
这样确保了 视图 和 网络请求 都 不能直接修改 state ,相反它们只能表达想要修改的意图。因为所有的修改都被 集中化 处理,且严格按照一个接一个的顺序执行,因此不用担心 竞态条件 的出现。
为了描述 action 如何改变 state tree ,你需要编写reducers。
ReactReact全家桶Redux
文章目录
1 Redux概述
中文文档: http://www.redux.org.cn/
Github: https://github.com/reactjs/redux
1.1 Redux简介
Redux
是一个专门用于做状态管理
的JS库(不是react插件库)。它专门为React.js这门框架设计,但并不是只能用于react,可以用于任何界面库。 它的作用是集中式管理react应用中多个组件共享
的状态。
安装:
npm i redux
使用场景
:
- 某个组件的状态,需要让其他组件可以随时拿到(共享)
- 一个组件需要改变另一个组件的状态(通信)
总体原则
:能不用就不用,如果不用比较吃力才考虑使用
使用Redux的原因:
React仅仅是一个视图层的框架,解决的是数据与模板的渲染问题,但是并没有提供数据的状态管理方案,这在大型项目中是一个非常大的痛点 。
凡是复杂一些的应用都会有下图左边组件树的结构,非父子组件传递数据需要层层传递,如果项目稍微复杂,即使可以开发,但也变得不可维护。因此Redux应运而生。让Redux统一管理数据,任何组件对数据的操作,都交给redux管理,这样就大大提高了开发和维护的效率。
Redux用与不用的区别
1.2 Redux工作流程
首先React组件会在 Redux 中派发一个 action
方法,通过调用 store.dispatch
方法,将 action
对象派发给 store
,当 store
接收到 action
对象时,会将先前的 state
与传来的 action
一同发送给 reducer
,reducer
在接收到数据后,进行数据的更改,返回一个新的状态给 store
,最后由 store
更改 state
。
2 Redux三大核心概念
2.1 Action
action
是把数据从应用传到 store 的有效载荷
,是 store
中唯一的数据来源,一般我们会通过调用 store.dispatch()
将 action 传到 store
我们需要传递的 action
是一个动作对象,它包含2个属性:
type
:标识属性,值为字符串, 唯一, 必要属性data
:数据属性,,值类型任意, 可选属性
例如,这里我们暴露了一个用于返回一个 action
对象的方法,我们调用它时,会返回一个 action
对象
export const createIncrementAction = data => (
type: INCREMENT,
data
)
2.2 Reducer
在 reducer
中,我们需要指定状态的操作类型,要做怎样的数据更新,reducer还会根据 action 的指示,对 state 进行对应的操作,然后返回操作后的 state 。总结:reducer
用于初始化状态及加工状态( 根据 action 更新 state )
如下,我们对接收的 action 中传来的 type 进行判断,更改数据,返回新的状态
export default function countReducer(preState = initState, action)
const type,data = action;
switch (type)
case INCREMENT:
return preState + data
case DECREMENT:
return preState - data
default:
return preState
2.3 Store
前面我们了解了action
来描述“发生了什么”,以及使用 reducer
来根据 action
更新 state
的用法。 而store
就是将state
、action
、reducer
联系在一起的对象
store
是 Redux 的核心,可以理解为是 Redux 的数据中台,我们可以将任何我们想要存放的数据放在store
中,在我们需要使用这些数据时,我们可以从中取出相应的数据。
Store 的作用如下:
-
维持应用的 state;
-
getState()
(https://www.redux.org.cn/docs/api/Store.html#getState):获取 state -
dispatch(action)
:分发action,触发reducer
调用,产生新的state
-
subscribe(listener)
: 注册监听,当产生了新的state
时, 自动调用 -
具体编码:
store.getState() store.dispatch(type:'INCREMENT', number) store.subscribe(render)
3 Redux API
3.1 createStore()
createStore()
:创建包含指定reducer
的store
对象
3.2 applyMiddleware()
applyMiddleware()
:应用上基于redux
的中间件(插件库)
3.3 combineReducers()
combineReducers()
:合并多个reducer
函数
4 使用redux编写应用
效果图
4.1 代码实现
redux/store.js :该文件专门用于暴露一个store对象,整个应用只有一个store对象
// 引入createStore,专门用于创建redux中最为核心的store对象
import createStore from "redux";
// 引入为Count组件服务的reducer
import countReducer from "./count_reducer.js";
// 暴露store
export default createStore(countReducer);
redux/count_reducer.js:该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
import INCREMENT,DECREMENT from './constant'
const initState = 0 //初始化状态
export default function countReducer(preState=initState,action)
// console.log(preState);
//从action对象中获取:type、data
const type,data = action
//根据type决定如何加工数据
switch (type)
case INCREMENT: //如果是加
return preState + data
case DECREMENT: //若果是减
return preState - data
default:
return preState
redux/count_action.js:该文件专门为Count组件生成action对象
import INCREMENT,DECREMENT from './constant'
export const createIncrementAction = data => (type:INCREMENT,data)
export const createDecrementAction = data => (type:DECREMENT,data)
redux/constant.js:该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止程序员单词写错
//将这两个单词写在 `constant` 文件中,并对外暴露,当我们需要使用时,我们可以引入这个文件,并直接使用它的名称即可,直接使用 `INCREMENT` 即可
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
Count/index.jsx
import React, Component from 'react'
//引入store,用于获取redux中保存状态
import store from '../../redux/store'
//引入actionCreator,专门用于创建action对象
import createIncrementAction,createDecrementAction from '../../redux/count_action'
export default class Count extends Component
/* componentDidMount()
//检测redux中状态的变化,只要变化,就调用render
store.subscribe(()=>
this.setState()
)
*/
//加法
increment = ()=>
const value = this.selectNumber
store.dispatch(createIncrementAction(value*1))
//减法
decrement = ()=>
const value = this.selectNumber
store.dispatch(createDecrementAction(value*1))
//奇数再加
incrementIfOdd = ()=>
const value = this.selectNumber
const count = store.getState()
if(count % 2 !== 0)
store.dispatch(createIncrementAction(value*1))
//异步加
incrementAsync = ()=>
const value = this.selectNumber
setTimeout(()=>
store.dispatch(createIncrementAction(value*1))
,500)
render()
return (
<div>
<h1>当前求和为:store.getState()</h1>
<select ref=c => this.selectNumber = c>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick=this.increment>+</button>
<button onClick=this.decrement>-</button>
<button onClick=this.incrementIfOdd>当前求和为奇数再加</button>
<button onClick=this.incrementAsync>异步加</button>
</div>
)
index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import store from './redux/store'
ReactDOM.render(<App/>,document.getElementById('root'))
// 监测redux中状态的改变,如redux的状态发生了改变,那么重新渲染App组件
store.subscribe(()=>
ReactDOM.render(<App/>,document.getElementById('root'))
)
4.2 总结
store.js:
- 引入redux中的createStore函数,创建一个store
- createStore调用时要传入一个为其服务的reducer
- 记得暴露store对象
count_reducer.js:
- reducer的本质是一个函数,接收:
preState
,action
,返回加工后的状态 - reducer有两个作用:初始化状态,加工状态
- reducer被第一次调用时,是store自动触发的,传递的preState是
undefined
, 传递的action是:type:'@@REDUX/INIT_a.2.b.4
在index.js中监测store中状态的改变,一旦发生改变重新渲染<App/>
备注:redux只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己写。
5 Redux 三大原则
原则一:单向数据流
,整个 Redux 中,数据流向是单向的
UI 组件 —> action —> store —> reducer —> store
原则二:state 只读
,在 Redux 中不能通过直接改变 state ,来控制状态的改变,如果想要改变 state ,则需要触发一次 action。通过 action 执行 reducer
原则三:纯函数执行
,每一个reducer 都是一个纯函数,不会有任何副作用,返回是一个新的 state,state 改变会触发 store 中的 subscribe
6 redux-thunk异步编程
redux默认是不能进行异步处理的,某些时候应用中需要在redux中执行异步任务(ajax, 定时器),那么就需要使用异步中间件redux-thunk
6.1 redux-thunk的使用
redux-thunk的安装:
npm install redux-thunk
count_action.js:该模块专门为Count组件生成action对象
// 异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的
import INCREMENT,DECREMENT from './constant'
//同步action,就是指action的值为Object类型的一般对象
export const createIncrementAction = data => (type:INCREMENT,data)
export const createDecrementAction = data => (type:DECREMENT,data)
//异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的
export const createIncrementAsyncAction = (data,time) =>
return (dispatch)=>
setTimeout(()=>
dispatch(createIncrementAction(data))
,time)
store.js:该模块专门用于暴露一个store对象,整个应用只有一个store对象
//引入createStore,专门用于创建redux中最为核心的store对象
import createStore,applyMiddleware from 'redux'
//引入为Count组件服务的reducer
import countReducer from './count_reducer'
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
//暴露store
export default createStore(countReducer,applyMiddleware(thunk))
6.2 总结
明确
:延迟的动作不想交给组件自身,想交给action
何时需要异步action
:想要对状态进行操作,但是具体的数据靠异步任务返回。
具体编码
:
- npm install redux-thunk,并配置在store中
- 创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务。
- 异步任务有结果后,分发一个同步的action去真正操作数据。
备注
:异步action不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步action。
7 react-redux
react-redux
:一个React插件库,专门用来简化React应用中使用redux
7.1 react-redux中组件分类
UI组件:
- 只负责 UI 的呈现,不带有任何业务逻辑
- 通过props接收数据(一般数据和函数)
- 不使用任何 Redux 的 API
- 一般保存在components文件夹下
容器组件:
- 负责管理数据和业务逻辑,不负责UI的呈现
- 使用 Redux 的 API
- 一般保存在containers文件夹下
react-redux模型图
7.2 react-redux相关API
-
Provider
:让所有组件都可以得到state数据 -
connect
:用于包装 UI 组件生成容器组件 -
mapStateToprops
:将外部的数据(即state对象)转换为UI组件的标签属性 -
mapDispatchToProps
:将分发action的函数转换为UI组件的标签属性
7.3 基本使用
安装react-redux
npm install react-redux
components/Count/index.jsx:UI组件
import React, Component from 'react'
export default class Count extends Component
//加法
increment = ()=>
const value = this.selectNumber
this.props.jia(value*1)
//减法
decrement = ()=>
const value = this.selectNumber
this.props.jian(value*1)
//奇数再加
incrementIfOdd = ()=>
const value = this.selectNumber
if(this.props.count % 2 !== 0)
this.props.jia(value*1)
//异步加
incrementAsync = ()=>
const value = this.selectNumber
this.props.jiaAsync(value*1,500)
render()
//console.log('UI组件接收到的props是',this.props);
return (
<div>
<h1>当前求和为:this.props.count</h1>
<select ref=c => this.selectNumber = c>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick=this.increment>+</button>
<button onClick=this.decrement>-</button>
<button onClick=this.incrementIfOdd>当前求和为奇数再加</button>
<button onClick=this.incrementAsync>异步加</button>
</div>
)
containers/Count/index.js:容器组件
//引入Count的UI组件
import CountUI from '../../components/Count'
//引入action
import
createIncrementAction,
createDecrementAction,
createIncrementAsyncAction
from '../../redux/count_action'
//引入connect用于连接UI组件与redux
import connect from 'react-redux'
/*
1.mapStateToProps函数返回的是一个对象;
2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value
3.mapStateToProps用于传递状态
*/
function mapStateToProps(state)
return count:state
/*
1.mapDispatchToProps函数返回的是一个对象;
2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value
3.mapDispatchToProps用于传递操作状态的方法
*/
function mapDispatchToProps(dispatch)
return
jia:number => dispatch(createIncrementAction(number)),
jian:number => dispatch(createDecrementAction(number)),
jiaAsync:(number,time) => dispatch(createIncrementAsyncAction(number,time)),
//使用connect()()创建并暴露一个Count的容器组件
export default connect(mapStateToProps,mapDispatchToProps)(CountUI)
App.jsx:给容器组件传递store
import React, Component from 'react'
import Count from './containers/Count'
import store from './redux/store'
export default class App extends Component
render()
return (
<div>
/* 给容器组件传递store */
<Count store=store />
</div>
)
7.4 总结
明确两个概念:
- UI组件:不能使用任何 redux 的api,只负责页面的呈现、交互等。
- 容器组件:负责和 redux 通信,将结果交给UI组件。
如何创建一个容器组件:靠 react-redux 的 connect 函数
connect(mapStateToProps,mapDispatchToProps)(UI组件)
mapStateToProps
: 映射状态,返回值是一个对象mapDispatchToProps
: 映射操作状态的方法,返回值是一个对象
备注:
- 容器组件中的store是靠props传进去的,而不是在容器组件中直接引入
- mapDispatchToProps,也可以是一个对象
7.5 代码优化
优化1 :简写
mapDispatchToProps
//使用connect()()创建并暴露一个Count的容器组件
export default connect(
state => (count:state),
//mapDispatchToProps的一般写法
/* dispatch => (
jia:number => dispatch(createIncrementAction(number)),
jian:number => dispatch(createDecrementAction(number)),
jiaAsync:(number,time) => dispatch(createIncrementAsyncAction(number,time)),
) */
//mapDispatchToProps的简写
jia:createIncrementAction,
jian:createDecrementAction,
jiaAsync:createIncrementAsyncAction,
)(CountUI)
优化2: Provider
容器组件可以检测redux中的状态改变,并渲染页面,所以不需要在index.js中检测了,不要在App.jsx中给子组件传递store了
// index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import store from './redux/store'
import Provider from 'react-redux'
ReactDOM.render(
<Provider store=store>
<App/>
</Provider>,
document.getElementById('root')
)
优化3 :整合UI组件和容器组件
每个组件两个文件夹太麻烦了,直接整合在一起就好了
7.6 数据共享版
// containers/Count/index.jsx
import React, Component from 'react'
//引入action
import
createIncrementAction,
createDecrementAction,
createIncrementAsyncAction
from '../../redux/count_action'
//引入connect用于连接UI组件与redux
import connect from 'react-redux'
//定义UI组件
class Count ex以上是关于redux 三大原则的主要内容,如果未能解决你的问题,请参考以下文章