redux-miniprogram v1.5.0
redux-miniprogram
安装
$ yarn add redux-miniprogram
或者
$ npm i redux-miniprogram
简介
微信小程序 redux 绑定工具。
它使您的 Page
和 Component
可以从 Redux Store
中读取数据,并向 Redux Store
派发 action
以更新数据,使用体验类似 react-redux。
- 支持 typescript
- 支持 批量更新
使用
- 首先创建一个
Redux Store
。
// store/index.js
import { createStore } from 'redux';
const SAVE_COUNT = 'SAVE_COUNT';
export function saveCount(count) {
return {
type: SAVE_COUNT,
payload: count
}
}
const initState = { count: 0 };
function reducer(state = initState, action) {
switch (action.type) {
case SAVE_COUNT:
return {
...state,
count: action.payload,
};
default:
return state;
}
}
const store = createStore(reducer);
export default store;
- 使用
Provider
把Redux Store
添加到App Options
中。
// app.js
import { Provider } from "redux-miniprogram";
import store from "./store";
App(
Provider(store)({
onLaunch: function() {
console.log(this.store); // 可以获取到 Redux Store
}
})
);
Page
中使用ConnectPage
连接Redux Store
。
// pages/index/index.js
import { ConnectPage } from "redux-miniprogram";
import { saveCount } from '../../store';
const mapStateToProps = (state) => ({
state: {
count: state.count
}
});
const mapDispatchToProps = (dispatch) =>({
saveCount(count) {
dispatch(saveCount(count));
},
dispatch
})
Page(
ConnectPage(mapStateToProps, mapDispatchToProps)({
onLoad: function() {
// 使用了和 vue 一样的批量更新 多次修改会合并成一次
this.store.dispatch(saveCount(10));
this.store.saveCount(100);
setTimeout(() => {
console.log(this.store.count); // 100
}, 0)
},
})
);
Component
中使用ConnectComponent
连接Redux Store
。
// components/index/index.js
import { ConnectComponent } from "redux-miniprogram";
import { saveCount } from '../../store';
const mapStateToProps = (state) => ({
state: {
count: state.count
}
});
Component(
ConnectComponent(mapStateToProps)({
attached: function() {
...
},
})
);
wxml
中使用store.xxx
获取Connect Store
中的状态。
<view>{{store.count}}</view>
注意
因为 diff 算法中判断相等的方式和 Object.is() 的表现一致, 所以修改状态必须返回不可变数据, 否则不会触发更新
不使用不可变数据会发生什么?
const a = { v: 1 };
const b = a;
a.v = 100;
a === b // true
const c = [];
const d = c;
c.push(100);
c === d // true
使用不可变数据后
const a = { v: 1 };
const b = {...a};
a.v = 100;
a === b // false
const c = [];
const d = [...c];
c.push(100);
c === d // false
如何操作不可变数据?
Object
操作不可变数据举例
{...}
Object.assign()
Array
操作不可变数据举例
[...]
[].concat()
[].map()
[].filter()
下面列举了部分利用不可变数据修改状态的方法
const initState = {
a: {
v:1
},
b: [1]
}
function reducer(state = initState, action) {
switch(action.type){
case '对象 a 修改属性 v 的值': {
const newA = {
...state.a
v: action.payload
};
return {
...state,
a: newA
}
}
case '对象 a 新增属性 v2': {
const newA = {
...state.a
v2: action.payload
};
return {
...state,
a: newA
}
}
case '对象 a 删除属性 v2': {
const newA = {...state.a};
delete newA.v2;
return {
...state,
a: newA
}
}
case '数组 b 修改指定下标的值': {
return {
...state,
b: state.b.map((item, index) => {
return index === action.index ? action.payload : item;
})
}
}
case '数组 b 添加新值': {
return {
...state,
b: [...state.b, action.payload]
}
}
case '数组 b 删除指定下标的值': {
return {
...state,
b: state.b.filter((item, index) => index !== action.index)
}
}
default:
return {...state}
}
}
API
Provider(store)(options)
Provider
用来把 Redux Store
添加到 App Options
中,使 ConnectPage()
和 ConnectComponent()
能访问到 Redux Store
。
参数
store
(Redux Store) 小程序中唯一的 Redux Store
返回值
Provider()
返回一个 Provided
函数
Provided: (options) => Object
Provided: (options) => Object
参数
options
(App Options) 注册小程序所需的参数
返回值
Object
(App Options) 一个新的注册小程序所需的参数
// App Options
const options = {
onLaunch: function() {
...
}
}
// 把 Redux Store 传给 Provider
// 返回 Provided 函数
const Provided = Provider(store);
// 把 App Options 传给 Provided
// 返回一个新的 App Options
const appOptions = Provided(options);
// 然后把新的 App Options 传入 App
App(appOptions);
// 以上可以直接简写成以下形式
App(
Provider(store)({
onLaunch: function() {
...
}
})
);
ConnectPage(mapStateToProps?, mapDispatchToProps?)(options)
ConnectPage
用来连接 Page
和 Redux Store
,使用 ConnectPage
可以创建一个 Connect Store
,然后把 Connect Store
添加到 Page Options
中。
参数
ConnectPage()
接收两个参数,均为可选。
mapStateToProps?: (state) => ({ state?: Object, pureState?: Object })
mapDispatchToProps?: (dispatch) => Object
最终 mapStateToProps() 返回的 state,pureState 和 mapDispatchToProps() 返回的 Object 会合并成一个 Connect Store
返回值
ConnectComponent()
返回一个 Connected
函数
Connected: (options) => Object
mapStateToProps?: (state) => ({ state?: Object, pureState?: Object })
mapStateToProps
用来订阅 Redux Store
中的 state
mapStateToProps
调用时以 state
作为参数,返回 { state?: Object, pureState?: Object }
, 返回的 state
会参与渲染, 返回的 pureState
不会参与渲染
如果 ConnectPage() 没有传入 mapStateToProps,则不会订阅任何状态。
// 如果这是 Redux Store 中的状态
const state = {
count: 0,
name: 'test'
}
// 订阅的 state 分两种类型
const mapStateToProps = (state) => ({
// 普通 state, 会参与渲染
state: {
count: state.count
},
// 纯 state, 不会参会与渲染
pureState:{
name: state.name
}
})
mapDispatchToProps?: (dispatch) => Object
mapDispatchToProps
用来包装 Redux Store
中的 dispatch
mapDispatchToProps
调用时以 dispatch
作为参数
通常情况下,你会利用这个 dispatch
来创建内部调用了 dispatch
的新函数
const mapDispatchToProps = (dispatch) => {
return {
increment: () => dispatch({ type: 'INCREMENT' }),
decrement: () => dispatch({ type: 'DECREMENT' }),
// 建议在这里手动添加上 dispatch
dispatch,
}
}
mapDispatchToProps 的默认值为 (dispatch) => ({ dispatch }),所以 ConnectPage() 没有传入 mapDispatchToProps 的情况下 Connect Store 中会默认存在一个 dispatch 函数。
Connected: (options) => Object
参数
options
(Page Options) 注册小程序中的一个页面所需的参数
返回值
Object
(Page Options) 一个新的注册小程序中的一个页面所需的参数
Connected
用来把 Connect Store
添加到 Page Options
中,使 wxml
中 store.xxx
和 javascript
中 this.store.xxx
能读取到 Connect Store
中的状态和 dispatch
函数。
// Page Options
const options = {
onLoad: function() {
...
}
}
const mapStateToProps = state => ({
state: {
count: state.count
}
});
const mapDispatchToProps = dispatch => ({
increment: () => dispatch({ type: 'INCREMENT' }),
dispatch,
});
// 把 mapStateToProps 和 mapDispatchToProps 传给 ConnectPage
// 返回 Connected 函数
const Connected = ConnectPage(mapStateToProps, mapDispatchToProps);
// 把 Page Options 传给 Connected
// 返回一个新的 Page Options
const pageOptions = Connected(options);
// 然后把新的 Page Options 传入 Page
Page(pageOptions);
// 以上可以直接简写成以下形式
Page(
ConnectPage(mapStateToProps, mapDispatchToProps)({
onLoad: function() {
// this.store 获取到的就是 Connect Store
console.log(this.store.count);
this.store.increment();
this.store.dispatch({ type: 'INCREMENT' });
}
})
);
// wxml 中 store.count 获取的就是 Connect Store 中的状态
<view>{{store.count}}</view>
ConnectComponent(mapStateToProps?, mapDispatchToProps?)(options)
ConnectComponent
用来连接 Component
和 Redux Store
,使用 ConnectComponent
可以创建一个 Connect Store
,然后把 Connect Store
添加到 Component Options
中。
参数
ConnectComponent()
接收两个参数,均为可选。
mapStateToProps?: (state) => ({ state?: Object, pureState?: Object })
mapDispatchToProps?: (dispatch) => Object
最终 mapStateToProps() 返回的 state,pureState 和 mapDispatchToProps() 返回的 Object 会合并成一个 Connect Store
返回值
ConnectComponent()
返回一个 Connected
函数
Connected: (options) => Object
mapStateToProps?: (state) => ({ state?: Object, pureState?: Object })
mapStateToProps
用来订阅 Redux Store
中的 state
mapStateToProps
调用时以 state
作为参数,返回 { state?: Object, pureState?: Object }
, 返回的 state
会参与渲染, 返回的 pureState
不会参与渲染
如果 ConnectComponent() 没有传入 mapStateToProps,则不会订阅任何状态。
// 如果这是 Redux Store 中的状态
const state = {
count: 0,
name: 'test'
}
// 订阅的 state 分两种类型
const mapStateToProps = (state) => ({
// 普通 state, 会参与渲染
state: {
count: state.count
},
// 纯 state, 不会参会与渲染
pureState:{
name: state.name
}
})
mapDispatchToProps?: (dispatch) => Object
mapDispatchToProps
用来包装 Redux Store
中的 dispatch
mapDispatchToProps
调用时以 dispatch
作为参数
通常情况下,你会利用这个 dispatch
来创建内部调用了 dispatch
的新函数
const mapDispatchToProps = (dispatch) => {
return {
increment: () => dispatch({ type: 'INCREMENT' }),
decrement: () => dispatch({ type: 'DECREMENT' }),
// 建议在这里手动添加上 dispatch
dispatch,
}
}
mapDispatchToProps 的默认值为 (dispatch) => ({ dispatch }),所以 ConnectComponent() 没有传入 mapDispatchToProps 的情况下 Connect Store 中会默认存在一个 dispatch 函数。
Connected: (options) => Object
参数
options
(Component Options) 注册小程序中的一个页面所需的参数
返回值
Object
(Component Options) 一个新的注册小程序中的一个页面所需的参数
Connected
用来把 Connect Store
添加到 Component Options
中,使 wxml
中 store.xxx
和 javascript
中 this.store.xxx
能读取到 Connect Store
中的状态和 dispatch
函数。
// Component Options
const options = {
onLoad: function() {
...
}
}
const mapStateToProps = state => ({
state: {
count: state.count
}
});
const mapDispatchToProps = dispatch => ({
increment: () => dispatch({ type: 'INCREMENT' }),
dispatch,
});
// 把 mapStateToProps 和 mapDispatchToProps 传给 ConnectComponent
// 返回 Connected 函数
const Connected = ConnectComponent(mapStateToProps, mapDispatchToProps);
// 把 Component Options 传给 Connected
// 返回一个新的 Component Options
const componentOptions = Connected(options);
// 然后把新的 Component Options 传入 Component
Component(componentOptions);
// 以上可以直接简写成以下形式
Component(
ConnectComponent(mapStateToProps, mapDispatchToProps)({
attached: function() {
// this.store 获取到的就是 Connect Store
console.log(this.store.count);
this.store.increment();
this.store.dispatch({ type: 'INCREMENT' });
}
})
);
// wxml 中 store.count 获取的就是 Connect Store 中的状态
<view>{{store.count}}</view>
useStore()
获取小程序中唯一的 Redux Store
import { useStore } from "redux-miniprogram";
const store = useStore();
useState()
获取 Redux Store
中的状态
import { useState } from "redux-miniprogram";
const state = useState();
useDispatch()
获取 Redux Store
中的 dispatch 函数
import { useDispatch } from "redux-miniprogram";
const dispatch = useDispatch();
优化
mapStateToProps
订阅的 state
会参与渲染, 所以当 mapStateToProps
订阅的 state
发生改变时, 会触发小程序的 setData 接口更新视图, 而 setData 的性能开销是十分昂贵的
可能有时候我们需要订阅某些 state
, 但是这些 state
并不会参与渲染, 这种情况下 state
发生改变导致的 setData
很明显是多余的, 带来的是完全不必要的性能开销
所以不参与渲染的 state
尽量使用 pureState
订阅, 不然容易出现性能瓶颈
const state = {
name: '张三',
age: 20
}
const mapStateToProps = (state) => ({
// 状态发生改变 先 diff 再触发 setData 更新视图
state: {
name: state.name
},
// 状态发生改变, 只简单的同步当前状态, 不会 diff, 也不会触发 setData
pureState: {
age: state.age
}
})
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago