网页前端状态管理库Redux学习笔记(一)

时间:2024-05-03 16:36:43

最近在博客园上看到关于redux的博文,于是去了解了一下。

这个Js库的思路还是很好的,禁止随意修改状态,只能通过触发事件来修改。中文文档在这里

前面都很顺利,但是看到异步章节,感觉关于异步说得很乱,而且必须配合插件才能实现异步。我是不喜欢用插件的人,能不用则不用。因此自己写了一个异步解决方案。大致的思路如下:

  1. 只在一个函数doSometing中处理异步方法和处理返回值,使用action传递执行类型参数
  2. 需要执行异步方法时,使用action传递参数,指示函数执行异步,然后返回特定状态,例如字符串"fetching"
  3. 在异步方法的回调中再次触发此状态修改(执行store.dispatch)
  4. 需要执行处理返回的数据时,使用action传递参数,指示函数执行数据处理,然后返回正确的状态值
  5. 最后,指定在哪里处理正确的状态值,可以在状态对象中订阅事件,也可以在第3步中直接处理

最后,贴上我写的例子,欢迎指教:)

<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<title>Redux Demo</title>
<style>
html,body{
height: 100%;
overflow: hidden;
margin: 0;
}
#box{
max-height: 85%;
overflow-x: hidden;
overflow-y: auto;
padding: 0;
margin: 0;
list-style-type: none;
}
#box>li:first-child{
margin-top: 0;;
}
#box>li:last-child{
margin-bottom: 0;
}
li{
background: #f23;
padding: 5px 10px;
margin: 5px;
}
#btn{
width: 120px;
height: 10%;
margin: 2.5% calc(50% - 60px);
}
dialog::backdrop {
background: rgba(0,0,0,0.9);
}
</style>
</head>
<body>
<button id="btn">click btn</button>
<ul id="box"></ul>
<dialog id="dial"></dialog> <script type="text/javascript" src="http://cdn.bootcss.com/redux/3.5.2/redux.min.js" ></script>
<script type="text/javascript">
var store;
Type1();
btn.addEventListener("click",function() {
store.dispatch({type:"act",doAddItem:true});
}); function renderListItem(index, callback) {
box.innerHTML+="<li><p>user "+index+"</p><p>your words here.</p></li>";
!!callback && callback();
}
function showLoading (msg, isCanClose) {
dial.innerHTML=msg;
!dial.open && dial.showModal();
isCanClose && (dial.onclick=function(){this.close();dial.onclick=null;});
}
function hideLoading () {
dial.close();
}
function Type1(){
var FLAGS={
INCREMENT:"i",
DECREMENT:"d",
SAYHI:"s"
};
function counter(state, action) {
!state && (state=1);
return (action===FLAGS.INCREMENT)?(++state):(--state);
}
function sayHello(state, action) {
state=!!action.name?action.name:"robin";
return (action.word||"hello")+", "+state;
}
function asyncCall (callback) {
window.setTimeout(function () {
callback && callback();
},3000);
}
function addListItem (state, param) {
if(typeof param.asyncCall!=="number"){
showLoading("waiting for fetching......");
(typeof state!=="number") && (state=-1);
asyncCall((function (state) {
return function () {
store.dispatch({type:"act",doAddItem:{asyncCall:state}});
};
})(state));
return "fetching";
}else{
var msg;
state=param.asyncCall;
(~~(10*Math.random()))%2===0 && (msg="internal error");
if(!msg){
renderListItem(++state, hideLoading);
}else{
showLoading("error : "+msg, true);
}
return state;
}
}
function Reducers (state, action) {
!state && (state={});
!!action.doSayHi && (state.sayHello=sayHello(state.sayHello, action.doSayHi));
!!action.doCounter && (state.counter=counter(state.counter, action.doCounter));
!!action.doAddItem && (state.itemCount=addListItem(state.itemCount, action.doAddItem));
return state;
} store=Redux.createStore(Reducers);
store.subscribe(()=>{
var state=store.getState();
console.log("state changed to : ", state);
});
console.log("init state is : ",store.getState()); function action(conf){
var res={type:"action"};
if(!!conf){
!!conf.doSayHi && (res.doSayHi={name:conf.doSayHi.name, word:conf.doSayHi.word});
!!conf.doCounter && (res.doCounter=conf.doCounter);
!!conf.doAddItem && (res.doAddItem=conf.doAddItem);
}
return res;
} var act=action({
doSayHi:{
name:"Gant",
word:"Good noon"
}
});
store.dispatch(act); act=action({
doCounter:FLAGS.INCREMENT
});
store.dispatch(act); act=action({
doSayHi:{
name:"Rassual",
word:"Good morning"
}
});
store.dispatch(act); act=action({
doSayHi:{
name:"dear dd",
word:"Good evening"
},
doCounter:FLAGS.DECREMENT
});
store.dispatch(act);
}
</script>
</body>
</html>