FLUX是一种架构的思想
redux与react无关,是基于flux和函数式编程的,其它地方也可以用
react尽量不更改原数据
npm i redux -S
createStore的参数必须是一个reducer,但只能接收一个reducer,故需要用combineReducers方法合并多个reducer
Redux核心思想(原理)
单一,复用,不修改原值(通过更改状态再渲染的方式改变)
- 需要修改的值用状态存起来,
- 渲染写一个方法(renderCount),
- 更改状态写一个方法(changeState),
- 操作写一个方法(dispatch),方法里根据传入的参数(action)进行不同的操作(修改状态,加或减要根据action来判断),修改状态之后调用渲染的方法;
- 传入的action为一个对象,有type(操作类型)和其它参数
- 修改状态的方法需要传入action,根据action的type来进行不同的修改,用switch(action.type)来进行不同的操作
- 创建一个管理状态相关的store(createStore),比如状态本身和修改状态的方法,需要将state和changeState传入store(const store = createStore(state, changeState)
- 定义获取状态的方法(getState),返回当前状态
- 由于修改状态需要根据不同的操作(dispatch)来修改,故应该将dispatch的方法定义到createStore的内部,dispatch里面调用changeState
- 为了达到复用,要求多个状态只使用一个dispatch来分发操作(action参数可以做到),故渲染模板就不应该在这里写死,可以创建一个数组listeners来保存那些需要自动执行的方法(如渲染模板)
- listeners是一个store内部的数组,故需要在内部定义方法subscribe来改变,其参数为自动执行的方法,push进listeners数组里
- 在store外部使用store.subscribe将renderCount存入listeners
- 将store内部的方法return出去以便在外部调用
- 为了遵从redux的思维——不可变数据源,因此在更改状态时可以返回一个新的状态,即将state作为参数传入changeState,每次需要修改时就返回一个新的state
- 新的state在store内部定义(let state = null),不再需要作为参数传入,第一次给state赋值在changeState里进行(需要在createStore时在内部自调用一次dispatch({})),如果state为空就赋初始值
reducers相当于改变状态的方法,也就是传给createStore的参数。多个reducer需要在reducers/index.js中用combineReducers合并导出
store在APP.js中作为<App />的属性传递给子组件,子组件this.props接收store并渲染
action目录下的actionType定义各个action的type,其它文件就用来导出不同组件的操作所需要的参数(type和payload)
在触发action的组件引入操作,调用dispatch(action),执行dispatch后会自动执行reducer,并把触发的action传给reducer的参数
reducer执行变化之后需要subscribe订阅它的变化来渲染到页面上(需要在组件里的componentDIdMount阶段订阅)
步骤
1.先建立reducers文件夹,在各个reducer里面定义初始的状态,然后把初始的状态export出去
目录结构
combineReducers
在actions目录里定义action
redux触发了action之后会自动执行reducer,reducer执行变化之后使用subscribe来渲染变化的结果
步骤:
1.安装redux
2.建立文件及文件夹
reducers目录用来管理各个状态
3.创建store.js
4.把reducer的内容渲染到界面上
Redux三大原则
-
唯一的数据源 single source of truth:
整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。
-
状态是只读的 state is read-only:
唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。
-
数据的改变必须通过纯函数完成 changes are made with pure function:
为了描述 action 如何改变 state tree ,你需要编写 reducers。
纯函数:函数的返回结果只依赖于它的参数;函数执行过程里面没有副作用。
重要函数
dispatch(action):分发action
store.getState
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Redux principle 04</title>
</head>
<body>
<h1>redux principle</h1>
<div class="counter">
<span class="btn" onclick="store.dispatch({type: 'COUNT_DECREMENT', number: 10})">-</span>
<span class="count" id="count"></span>
<span class="btn" id="add" onclick="store.dispatch({type: 'COUNT_INCREMENT', number: 10})">+</span>
</div>
<script>
// 定义一个方法,用于集中管理state和dispatch, changeState改名了,专业的叫法是reducer
const createStore = (reducer) => {
// 定义一个初始的state
let state = null
// getState用于获取状态
const getState = () => state
// 定义一个监听器,用于管理一些方法
const listeners = []
const subscribe = (listener) => listeners.push(listener)
// 定义一个dispatch方法,让每次有action传入的时候返回reducer执行之后的结果
const dispatch = (action) => {
// 调用reducer来处理数据
state = reducer(state, action)
// 让监听器里的所有方法运行
listeners.forEach(listener => listener())
}
// 初始化state
dispatch({})
return {
getState,
dispatch,
subscribe
}
}
// 定义一个计数器的状态
const countState = {
count: 10
}
// 定一个方法叫changeState,用于处理state的数据,每次都返回一个新的状态
const changeState = (state, action) => {
// 如果state是null, 就返回countState
if (!state) return countState
switch(action.type) {
// 处理减
case 'COUNT_DECREMENT':
return {
...state,
count: state.count - action.number
}
// 处理加
case 'COUNT_INCREMENT':
return {
...state,
count: state.count + action.number
}
default:
return state
}
}
// 创建一个store
const store = createStore(changeState)
// 定义一个方法用于渲染计数器的dom
const renderCount = () => {
const countDom = document.querySelector('#count')
countDom.innerHTML = store.getState().count
}
// 初次渲染数据
renderCount()
// 监听,只要有dispatch,renderCount就会自动运行
store.subscribe(renderCount)
</script>
</body>
</html>
基本流程:
1.创建reducers
2.合并reducers
3.createStore
4.Provider store={store}
5.connect(mapStateToProps, { actionCreators })(YourComponent)
6.actionCreators
7.修改reducers
默认自动把action的部分dispatch了,可以手动延迟dispatch做成异步的