不用redux-thunk之前
// store.js
import { createStore } from 'redux';
export const reducer = (state = {
count: 0
}, action) => {
switch(action.type) {
case 'CHANGE_DATA': {
return {
...state,
count: action.data
}
}
default:
return state;
}
}
export const store = createStore(reducer);
附:手写发布订阅
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
// const saleCenter = {};
// saleCenter.clientList = [];
// saleCenter.listen = function (fn) {
// this.clientList.push(fn);
// };
// saleCenter.attach = function () {
// for (let i = 0, fn; (fn = this.clientList[i++]); ) {
// fn.apply(this, arguments);
// }
// };
// // 测试
// saleCenter.listen((info) => {
// console.log(info)
// })
// saleCenter.listen((info) => {
// console.log(info);
// })
// saleCenter.attach('房子降价了')
const saleCenter = {};
saleCenter.clientList = {};
saleCenter.listen = function (type, fn) {
if (!this.clientList[type]) { // 如果还没有订阅过此类型的消息,则给该类型创建一个缓存列表
this.clientList[type] = [];
}
this.clientList[type].push(fn);
};
saleCenter.attach = function () {
const type = Array.prototype.shift.apply(arguments),
fns = this.clientList[type]; // 取出该消息类型对应的回调函数集合
if (!fns || fns.length === 0) {
return false;
}
for (let i = 0, fn; (fn = fns[i++]); ) {
fn.apply(this, arguments);
}
};
// 测试
saleCenter.listen('土豪', (info) => {
console.log(info);
});
saleCenter.listen('土鳖', (info) => {
console.log(info);
});
saleCenter.attach('土豪', '房子降价了');
</script>
</body>
</html>
// App.jsx
class ReduxTest extends React.Component {
constructor(props) {
super(props);
store.subscribe(() => {
console.log('subscribe')
this.setState({
count: store.getState().count
})
})
}
changeData = () => {
const { count } = store.getState();
const action = {
type: 'CHANGE_DATA',
data: count + 1
}
store.dispatch(action);
}
render() {
return (
<div>
<span>{this.state?.count}</span>
<button onClick={this.changeData}>按钮+1</button>
</div>
)
}
}
export default ReduxTest;
对于上述代码,我们dispatch一个action,其中action必须为一个对象。
但是实际开发中,action里的数据往往是一个异步接口获取的数据,这个时候,我们可以
class ReduxTest extends React.Component {
constructor(props) {
super(props);
store.subscribe(() => {
console.log('subscribe', store.getState());
this.setState({
count: store.getState().count
});
});
}
changeData = () => {
const { count } = store.getState();
let res;
const p = new Promise(resolve => {
setTimeout(() => {
res = 111;
resolve(res);
}, 1000);
});
p.then(r => {
const action = {
type: 'CHANGE_DATA',
data: r
};
store.dispatch(action);
});
};
render() {
return (
<div>
<span>{this.state?.count}</span>
<button onClick={this.changeData}>按钮+1</button>
</div>
);
}
}
export default ReduxTest;
但是,上述会把处理的异步的逻辑写在组件里,使代码变得混乱,
因此,假如我dispatch一个函数,在这个函数里去处理异步的逻辑,岂不是使代码变得更简洁?!
但是根据Redux的三大原则之reducer必须为一个纯函数,所以不能直接dispatch一个函数,所以需要借用中间件redux-thunk。
const getData = () => {
let res;
const p = new Promise(resolve => {
setTimeout(() => {
res = 111;
resolve(res);
}, 1000);
});
p.then(r => {
const action = {
type: 'CHANGE_DATA',
data: r
};
store.dispatch(action);
});
};
class ReduxTest extends React.Component {
constructor(props) {
super(props);
store.subscribe(() => {
console.log('subscribe', store.getState());
this.setState({
count: store.getState().count
});
});
}
changeData = () => {
const { count } = store.getState();
store.dispatch(getData);
};
render() {
return (
<div>
<span>{this.state?.count}</span>
<button onClick={this.changeData}>按钮+1</button>
</div>
);
}
}
export default ReduxTest;
这样,就将处理数据的逻辑放在了action层,而不是视图层,视图层只负责渲染就好。
总结:
其实没有redux-thunk,也可以完成同样的功能,只是将处理异步逻辑的代码写在组件里,为了让代码更简洁、解耦,所以通过redux-thunk可以dispatch一个函数,然后在这个函数里处理异步操作。
redux-thunk 源码参考:https://segmentfault.com/a/1190000022792225
2024-6-10日记
视频讲解:手写redux-thunk