仓库地址:仓库
前言
完全没想到,竟然会有第二集!第一集在这里:我厌倦了Redux,那就造个轮子:Rectx
我们在前面的第一集中讲到了我如何厌倦 Redux
以及,我如何对这个问题进行了规避。利用了新的 Context API
,我在此之上,进行了一系列的封装,封装出了一个还算可行的状态容器:Controller
,用于保管我们的组件状态和书写管理状态的业务逻辑。
Rectx 的痛点还有哪些?
## 只支持 16.3 版本 React
Rectx **[发音类似 rick text]** 有一个最大的毛病就是,我们对 `Context API` 的支持非常的严重,也就是说,想要玩 `Rectx` 的玩家,不得不升级到 `16.3` 版本才能玩耍,这是俨然成为最大痛点之一。
因此,我引入了一个 polyfill
使得我们的 Rectx
能够在 React > 15
版本 都可以使用。
你没有看错,我们在React > 15 版本 都可以使用!
你没有看错,我们在React > 15 版本 都可以使用!
你没有看错,我们在React > 15 版本 都可以使用!
没有单元测试
在第一集中,我向大家介绍了 Rectx 的使用以及其简单的概念。但是一个库,没有单元测试,谁都不敢用在生产环境中。于是,我给 Rectx 加上了大约 400 行的测试代码,保证了其期望功能的完整性和正确性。
Immer 的引入
Immer 是一个非常巧的库,他的作用实际上是使得 immutable 概念的库( Redux 之流)拥有了一个 mutable 的 API ,学习它的成本极其低下,因为这个库只有一个 API。
什么是 immutable ?
在介绍为什么引入 Immer 之前,我们不妨来回想一下最原始的 Reducer。Reducer 是一个极其简单的概念,通过他我们能够轻松的改变一个 Store 的状态:
const Reducer = (state, action) =>{
return {...state, fuck: action.shit}
}
如果是数组,那就搞成:
const Reducer = (state, action) =>{
const newArray = action.shit.map((i,idx)=>{
if(idx === 1) return {...i, fuck:'shit}
return i
})
return {...state, fuck: newArray}
}
这样就能够更新了。这里为什么要返回一个新的数组的究其原因,让我来带大家一起实现一个超级简单的 Redux 你就能够明白:
Redux 使用的是一个 发布/订阅 模型,通过 subscribe 函数,把某个函数注册到 Store 中去,当执行 dispatch 的时候,就会将 Store 的全局状态进行广播,具体怎么广播呢?答案就是遍历所有的 subscribe 进来的函数。
然而,我们总不能没发送一个 dispatch 就要更新一次,那样代价也太大了,尤其是「当状态没有发生改变的时候,我们更新有个屁用?」
这里的没有改变,说的就是 Store 了。
//核心代码
const store = reducer(this.store, action)
if(store !== this.store)
....
在这里我们看到,Store 如果还是原来的那个 Store 那么就不会更新。你在 reducer 中直接修改 store 是不可能对 store 进行更新的,所以我们必须要每次:
{...state,....}
这就是 immutable 的核心了,这种模式的好处,简单来说就是实现起来非常简单。
immutable 的最大毛病
immutable 的最大毛病就是在更新深层次的嵌套 object 以及性能上,深层次怎么说呢,看例子:
const fuckDeep = {
shit:{
canNotMakeUp:[ {
haha:[ {hello:1},{world:2} ]
} ]
}
//我们要更新fuckDeep.shit.canNotMakeup[0].haha[0].hello的值
有时候,我们的数据结构就是这样,我们想通过修改 :
fuckDeep.shit.canNotMakeup[0].haha[0].hello = 'oh shit'
这么做是不可能的,我们的做法就是对其进行 递归深拷贝,制造一个层次一样的新对象,并且带着 ‘oh shit’ 的值。
由此,因为我们可能要对对象进行大量的操作,所以我们必须要每次 递归深拷贝 执行这些操作,这就带来了严重的性能问题。
有没有办法优雅的解决这样的问题?
人类是聪明的,解决的方案其实早就有了,那就是 immutable.js 这玩意,这玩意是 fb 出的,哈哈,不能不说,这玩意又有自己的一套 API ,学起来也是够费力的,他的作用大约就是让你能够使用这样的形式:
fuckDeep.shit.canNotMakeup[0].haha[0].hello = 'oh shit'
但是,因为 API 太多了,所以大家都懒得用(就像原来的 Context API 那样)
那么这时候,immer 出现了,他只有一个 API:
const fuckDeep = {
shit:{
canNotMakeUp:[ {
haha:[ {hello:1},{world:2} ]
} ]
}
const newState = product(fuckDeep, (draft) => {
draft.shit.canNotMakeup[0].haha[0].hello = 'oh shit'
})
newState.shit.canNotMakeup[0].haha[0].hello === 'oh shit' //true
newState === fuckDeep ? // false
没错了,就是这么简单。
集成到 Rectx
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider, Controller, Listen } from 'rectx'
class LikeController extends Controller {
state = {
isLike: false,
isMount: false
}
handleClick = () => {
this.setState((draft)=>{
draft.isLike = true
})
}
}
const Like = () => (
<Listen
to={[LikeController]}
>
{like => (
<div>
<button onClick={() => like.handleClick()}>Click me</button>
<div>{like.state.isLike ? 'I love you' : 'I hate you'}</div>
</div>
)}
</Listen>
)
ReactDOM.render(
<Provider>
<Like />
</Provider>,
document.getElementById('root')
)
值得注意的是:
handleClick = () => {
this.setState((draft)=>{
draft.isLike = true
})
}
我们在 setState 的时候,使用的是函数形式,这个函数你不需要返回任何东西,你只需要修改里面的状态,爱怎么改怎么改。
当 setState 完毕以后,你 Listen 的函数,就会进行更新
总结一下
第二集中我们着重处理了几大痛点:
- 版本问题:现在的 Rectx 可以使用到 React >15 的版本中
- 修改 State 的痛苦:引入 immer ,对其进行封装,轻松愉快的解决了问题
- 单元测试:没有单元测试,连我自己都他妈不敢用!!!
Yeah ,到此为止~谢谢大家观看
仓库地址:仓库 喜欢的给点哦~