目录
1.Redux
本文引用了,这篇掘金文章,这篇文章写的挺好的,b站也有她的视频!
1)redux的官方介绍
redux的官方描述:项目中所有的state
都以一个对象树
的形式存储在一个单一的store
中。唯一改变state
的办法是触发action
,一个描述发生什么的对象。为了描述action
如何改变state
树,则需要编写reducers
。
redux的官方例子:
import { createStore } from "redux";
//这是一个reducer,形式(state,action) => state 的纯函数
//描述了action如何把state转变为下一个state
//state的形式取决于你,可以是基本类型,数组或者对象
//甚至是Immutable.js生成的数据结构。唯一的要点是
//当state变化时需要返回全新的对象,而不是修改传入的参数
//下面例子使用'switch'语句和字符串来做判断,但是你可以写帮助类(helper)
//根据不同的约定来判断,只要适合你的项目即可
function counter(state = 0, action){
switch(action.type){
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state -1;
default:
return state;
}
}
//创建Redux Store来存放项目状态
//API 是{ subscrible, dispatch, getState }
let store = createStore(counter);
//可以手动订阅更新,也可以事件绑定到视图层
store.subscrible(()=>
console.log(store.getState())
)
//改变内部state唯一方法是dispatch一个action
//action可以被序列化,用日记记录和存储下来,后期还可以以回放的方式执行
store.dispatch({type: 'INCREMENT'});
store.dispatch({type: 'INCREMENT'});
store.dispathc({type: 'DECREMENT'}):
把要做的修改
变成一个普通对象
,这个对象被叫做action
,而不是直接修改state
。然后编写专门的函数来决定每个action
如何改变项目中的state
,这个函数被叫做reducer
。
2)副作用
副作用
让函数变得不纯。如果依赖于外部状态就无法保证输出相同,就会带来副作用。
所有的外部交互都有可能产生副作用,副作用会给程序带来安全隐患和不确定性,要尽可能的控制副作用在可控制的范围内发生。
3)纯函数
纯函数要满足三个特点:(1)相同的输入总是会返回相同的输出
(2)不产生副作用
(3)不依赖于外部状态
var a = 10
function f(b){
return a+b
}
console.log(f(5))
//因为全局a=10时,输出是15,当全局的a=0时,输出是5。
//所以上面的函数不满足上面的第一条和第三条规定,不是纯函数。
obj = {}
function f(b){
obj.a = 10
return b
}
console.log(f(5))
//因为函数改变了全局作用域里面的obj。
//所以不满足第二条规定,该函数不是纯函数。
4)redux的三大核心
单一数据源
整个项目的state
被存储在一棵object tree
中,并且这个object tree
只存在于唯一一个store
中。State是只读的
唯一改变state
的方法就是触发action
,action
是一个用于描述已发生事件的普通对象。
store.dispatch({type:'COMPLETE_TOO', index: 1})
- 使用纯函数来执行修改
为了描述action
如何改变state tree
,你需要去编写reducers
reducers
只是一些纯函数,它接受先前的state
和action
,并且返回新的state
。
纯函数的好处
:可以复用,可以控制顺序,传入附加参数。
5)redux的状态
(1) state-状态
就是我们传递的数据
,那么我们在用Rect
开发项目的时候,大致可以把State
分为三类
- DomainDate:可以理解成服务器端端数据,比如:获取用户的信息,商品的列表等等
- UI State: 决定当前UI决定展示的状态,比如:弹框的显示隐藏,受控组件等等
- App State:App级别的状态,比如:当前是否请求loading,当前路由信息等可能被多个和组件去使用的到的状态
(2)action-事件
action
是把数据从应用(组件)传到store
的载体,它是store
数据的唯一来源,一般来说,我们可以通过store.dispatch()
将action
传送给store
。
action的特点:
action
的本质就是一个javascript的普通对象
action
对象内部必须要有
一个type属性
来表示要执行的动作- 多数情况下,这个
type
会被定义成字符串常量
- 除了
type字段
之外,action
的结构随意进行定义 - 而我们项目中,更多的喜欢用
action创建函数
(就是创建action的地方) 只是描述了有事情要发生,并没有描述去如何去更新state
//action
{
type: '字符串常量',
info: {...},
isLoading: true
}
//action创建函数
function addAction(params){
//返回一个action对象
return {
type: 'add',
...params
}
}
(3)reducer-纯函数
reducer
本质就是一个函数
,它用来响应
发过来的action
,然后经过处理,把state
发送给store
。
注意:在reducer函数中,需要return返回值,这样store才能接受到数据,函数会接受两个参数,第一个参数是初始化state
,第二个参数action。
const initState = {...}
const rootReducer = (state=initState,action) => {
...
return {...}
}
(4)store-联系action和reducer
store 就是把action与reducer联系到一起的对象
主要的职责:
- 维持应用的state
- 提供
getState()
方法获取state
- 提供
dispatch()
方法发送action
- 通过
subscribe()
来注册监听 - 通过
subscribe()返回值
来注销监听
import {creteStore} from "redux"
const store = createStore(传入reducer)
2.Mobx
3.Recoil
1)RecoilRoot
如果组件中使用Recoil
,则可以将RecoilRoot
放置在父组件的某个位置。最好将其放在根组件中
。
2)Atom
一个atom
代表一个状态
。atom可在任意组件中进行读写。读取atom值的组件隐式订阅了该atom,因此任何atom的更新都将导致该atom的组件重新渲染。
3)Selector
selector
代表一个派生状态
,派生状态时状态的转换
。你可以将派生状态视为将状态传递给以某种方式修改给定状态的纯函数的输出。
4)常用的recoil的hook函数
(1)useRecoilState
当组件同时需要读写状态时,推荐使用该hook。
(2)useRecoilValue
当一个组件需要在不写入state的情况下读取state时,推荐使用该hook。
(3)useSetRecoilState
返回一个setter函数,用来更新可写Recoil state的值,状态变化不会导致组件重新渲染。
当一个组件需要写入而不需要读取state时,推荐使用此hook。
如果组件使用了useRecoilState()
来获取setter函数
,那么同时它也会订阅更新,并在atom
或selector
更新时重新渲染。使用useSetRecoilState()
允许组件在值发生改变时而不用给组件订阅重新渲染的情况下设置值。
5)关于recoil的例子
(1)输入框
src文件夹
下的app.js
import {RecoilRoot} from "recoil";
import HomePage from "./pages/HomePage";
export default function App(props) {
return (
<div className="App">
<RecoilRoot>
<HomePage />
</RecoilRoot>
</div>
);
}
其中store文件夹
和pages文件夹
在src文件夹
下
store文件夹
下的index.js
//状态atom,在需要的组件中,直接引入并使用useRecoilState()即可。
import {atom,selector} from "recoil";
export const textState = atom({
key: 'textState', //全局下保持唯一性
default: '', //初始值
});
//派生状态,在需要的组件中,可以使用useRecoilValue()的hook,
//来读取派生状态即可。
export const charCountState = selector({
key: "charCountState", //全局下唯一性
get: ({get}) => {
const text = get(textState);
return text.length;
}
});
pages文件夹
下的HomePage
组件
import {useRecoilState, useRecoilValue, useSetRecoilState} from "recoil";
import {textState, charCountState} from "../store/";
import TextInput from "./TextInput.js";
import CharacterCount from "./CharacterCount.js";
export default function HomePage(props) {
return (
<div>
<h3>HomePage</h3>
<TextInput/>
<CharacterCount/>
</div>
);
}
pages文件夹
下的TextInput
组件
import {useRecoilState} from "recoil";
import {textState, charCountState} from "../store/";
export function TextInput(props) {
const [text, setText] = useRecoilState(textState);
const onChange = e => {
setText(e.target.value);
};
return (
<div>
<input type="text" value={text} onChange={onChange}/>
<br/>
Echo:{text}
<div>
);
}
pages文件夹
下的CharacterCount
组件
import {useRecoilValue} from "recoil";
import {textState, charCountState} from "../store/";
export function CharacterCount(props){
const count = useRecoilValue(charCountState);
return (
<div>
<p>{count}</p>
<div>
);
}
(2)todolist清单
4.对比redux,mobx,recoil
redux
是集中式管理state
,而recoil
和mobx
是分散式的recoil
目前只适用于hooks函数recoil
时facebook开发的,可以使用react内部的调度机制,这是redux
和mobx
不支持的