浅谈Redux
下面将从what, why, how to 三个方面来说说Redux
第一问 what ❓ 什么是Redux
将一个web应用拆分成视图层与数据层, Redux就是保存其数据的一个容器, 其本质就是维护一个存储数据的对象.
const initState = {
count: 0,
}
-
Action : 一个 want to do 的过程 (计划要做一个什么样的操作)
-
ActionType是对Action的描述, 也是连接Action和Reducer的桥梁
-
本质上是一个由ActionType和payload(数据)组成的对象
export const increaseConstant = 'INCREASE' // ActionType
{
type: increaseConstant,
payload,
} // Action
-
Reducer : 一个 to do 的过程 (执行Action计划的操作)
case increaseConstant: // 当 ActionType 为 'INCREASE' 时, 执行count++
return {
...state,
count: payload + 1
}
第二问 why ❓ 为什么要使用Redux
当你不知道是否需要使用Redux的时候, 那就是不需要使用.
下面一组动图很好的描述了一个应用程序的开发过程, 及何时需要Redux.
-
游戏进入中期, 开始有少量非父子组件间需要通讯一些数据
-
此时, 就是Redux的用武之地了, 使用Redux后流程如下
第三问 how to ❓ 怎么使用Redux
在说使用方法之前, 我们先来从头到尾模拟一个Redux的过程, 只要理解原理, 使用起来那不是小菜一碟.
下面我们就用一个简单的计数器的例子来模拟实现一个Redux的过程:
创建一个count组件
import React, { useState } from 'react'
const CountItem = (props) => {
const {
count,
increase,
} = props
return (
<>
{count}
<button onClick={increase}>Count++</button>
</>
)
}
const Count = () => {
const [count, setCount] = useState(0)
const increase = () => {
setCount(count + 1)
}
return (
<CountItem
count={count}
increase={increase}
/>
)
}
export default Count
这样一个简单的count组件就创建好了, 现在, 我们想把对数据的操作单独封装成一个方法名为dispatch, 传递的参数为一个Action 格式: { type: xxx, payload: xxx }
封装一个Dispatch函数
const dispatch = (action) => {
switch(action.type) {
case 'INCREASE':
return action.payload + 1
default:
break
}
}
改写increase方法
const increase = () => {
- setCount(count + 1)
+ setCount(dispatch({type: 'INCREASE', payload: count}))
}
这时, 我们将action对象也抽离出来, 方便复用, 新建action.js.
action.js => 返回action对象
const increaseCount = (payload) => {
return {
type: 'INCREASE',
payload
}
}
改写increase方法
const increase = () => {
- setCount(dispatch({type: 'INCREASE', payload: count}))
+ setCount(dispatch(increaseCount(count)))
}
接下来我们把dispatch中的事件操作抽离到reducer中, 新建reducer.js.
reducer.js => 进行数据操作
const reducer = (state, action) => {
const { type, payload } = action
switch(type) {
case 'INCREASE':
return {
...state,
count: payload + 1
}
default:
return state
}
}
改写dispatch函数
const dispatch = (action) => {
const state = {
count,
}
const newState = reducer(state, action)
return newState
}
改写increase方法
const increase = () => {
- setCount(dispatch(increaseCount(count)))
+ setCount(dispatch(increaseCount(count)).count)
}
接下来, 我们把set方法也拿到dispatch中, 让所有操作都在dispatch中完成.
继续改造dispatch函数, 增加setter做映射
const dispatch = (action) => {
const state = {
count,
}
+ const setter = {
+ count: setCount
+ }
const newState = reducer(state, action)
+ for (let key in newState) {
+ setter[key](newState[key])
+ }
- return newState
}
改写increase方法
const increase = () => {
- setCount(dispatch(increaseCount(count)).count)
+ dispatch(increaseCount(count))
}
这里我们可以看到, action.type是连接action和reducer的桥梁, 我们可以将actionType定义为常量单独保存.
在action中增加actionType
export const increaseConstant = 'INCREASE'
// 替换 action 和 reducer 中的 'INCREASE' 为 increaseConstant
基于现有场景, 如果我们有另一个功能, 而目前的reducer并不能帮助我们很好的把不同的功能划分开来, 我们改造一下reducer, 改造成一个对象, 用对象的key去区分功能.
改写reducer
const reducer = {
count(state, action) {
const { type, payload } = action
switch(type) {
case increaseConstant:
return payload + 1
default:
break
}
},
}
这时我们要遍历reducer, 找到正确的key, 才能让程序正确执行, 我们新建combineReducers.js来完成这步操作.
combineReducers
const combineReducers = (reducer) => {
return (state, action) => {
let ret = {}
for (let key in reducer) {
ret[key] = reducer[key](state[key], action)
}
return {
...state,
...ret,
}
}
}
继续改下dispatch函数, 使其支持当前格式reducer.
改写dispatch
- const newState = reducer(state, action)
+ const newState = combineReducers(reducer)(state, action)
至此, 一个redux的实现过程就完成了, 接下来, 我们实际用一用redux. 其实, 当完成上述操作的时候, 怎么用就已经说的差不多了.
Redux + React 使用
action, reducer, Count组件同上, Count组件需要简单改写下.
import { createStore, combineReducers } from 'redux'
import reducer from './recuder'
const initState = {
count: 0,
}
const store = createStore(
combineReducers(reducer),
initState,
)
export default store
import { Provider } from 'react-redux'
import store from './store'
const App = () => {
return (
<Provider store={store}>
<Count />
</Provider>
)
}
export default App
import React from 'react'
import { connect } from 'react-redux'
import { increaseCount } from './action'
const CountItem = (props) => {
const {
count,
increase,
} = props
return (
<>
{count}
<button onClick={increase}>Count++</button>
</>
)
}
const Count = (props) => {
const {
count,
dispatch,
} = props
const increase = () => {
dispatch(increaseCount(count))
}
return <CountItem count={count} increase={increase} />
}
export default connect(
(state) => {
return state
},
(dispatch) => {
return { dispatch }
}
)(Count)
import React from 'react'
- import { connect } from 'react-redux'
+ import { useSelector, useDispatch } from 'react-redux'
import { increaseCount } from './action'
const CountItem = (props) => {
const {
count,
increase,
} = props
return (
<>
{count}
<button onClick={increase}>Count++</button>
</>
)
}
const Count = () => {
- const {
- count,
- dispatch,
- } = props
+ const count = useSelector(state => state.count)
+ const dispatch = useDispatch()
const increase = () => {
dispatch(increaseCount(count))
}
return <CountItem count={count} increase={increase} />
}
- export default connect(
- (state) => {
- return state
- },
- (dispatch) => {
- return { dispatch }
- }
- )(Count)
+ export default Count
至此, 本篇文章就到此结束了, 大家可以写个复杂一点的组件练习下, 比如todoList, 手动滑稽. 逃了逃了。
点击左下角阅读原文,到 SegmentFault 思否社区 和文章作者展开更多互动和交流,或扫描下方二维码添加“ SF 思否小姐姐 ”,回复“ 入群 ”即可加入我们的技术交流群,收获更多的技术文章~
文章评论