@tiki.vn/redux-miniprogram-bindings
v1.4.4
Published
Redux binding utils for miniprogram
Downloads
153
Readme
redux-miniprogram-bindings
Redux cho các mini-programs (Tiki, Alipay và Wechat)
Đặc điểm
- API đơn giản và linh hoạt. Nó có thể được sử dụng dễ dàng chỉ với một connect (cung cấp phương thức tắt - shortcut method - tương tác
$page
alias và$component
alias). - Đầy đủ các hàm, phương thức đa dạng đáp ứng các nhu cầu và ngữ cảnh sử dụng khác nhau.
- Hỗ trợ sử dụng trong TXML.
- Sau khi
dispatch
được thực thi, trạng thái nội tại (internal state) của các component tự động được cập nhật; và view được đưa vào hàng đợi để kết xuất cập nhật. - Hỗ trợ mini-program của Tiki, WeChat và Alipay.
Cài đặt
Cài đặt thông qua
npm
hayyarn
.# npm $ npm install --save redux-miniprogram-bindings redux # yarn $ yarn add redux-miniprogram-bindings redux
Ngoài ra bạn có thể tìm thấy các tập tin phân phối bên trong của thư mục dist của redux-miniprogram-bindings.
Tập tin redux cần được import riêng biệt (nó là thư viện binding và không chứa redux code).
Sử dụng
Tạo ra thực thể Redux Store
// store.js import { createStore, combineReducers } from 'redux' function counter(state = 0, action) { switch (action.type) { case 'INCREMENT': return state + (action.step || 1) case 'DECREMENT': return state - (action.step || 1) default: return state } } const initUserInfo = { name: 'userName', age: 25 } function userInfo(state = initUserInfo, action) { switch (action.type) { case 'SET_USER_INFO': return { ...state, ...action.userInfo } default: return state } } const rootReducer = combineReducers({ counter, userInfo }) const store = createStore(rootReducer) export default store
Thiết lập provider trong tập tin
app.js
// setupStore.js import store from 'your/store/path' // micro channel applet import { setProvider } from 'redux-miniprogram-bindings' // Alipay applet // import { setProvider } from 'redux-miniprogram-bindings/dist/redux-miniprogram-bindings.alipay.min.js' setProvider({ store })
// app.js // Make sure to call import './setupStore.js' App({ /** ... */ })
Sử dụng trong page
import { $page } from 'redux-miniprogram-bindings' import { actionCreator1, actionCreator2 } from 'your/store/action-creators/path' $page({ mapState: ['dependent', 'state'], mapDispatch: { methodsName1: actionCreator1, methodsName2: actionCreator2, }, })({ onLoad() { // Read the value in state const { dependent } = this.data // dispatch actionCreator1 this.methodsName1() // dispatch actionCreator2 this.methodsName2(/** ...args */) }, })
Sử dụng trong component
import { $component } from 'redux-miniprogram-bindings' import { actionCreator1, actionCreator2 } from 'your/store/action-creators/path' $component({ mapState: [ (state) => ({ data1: state.dependent, data2: state.state, }), ], mapDispatch: (dispatch) => ({ methodsName1: () => dispatch(actionCreator1()), methodsName2: (...args) => dispatch(actionCreator2(...args)), }), })({ attached() { // Read the value in state const { data1: dependent } = this.data // dispatch actionCreator1 this.methodsName1() // dispatch actionCreator2 this.methodsName2(/** ...args */) }, })
Sử dụng trong XML
<view tiki:if="{{ data1 }}">{{ data2 }}</view>
API
setProvider:(config) => void
Thiết lập Provider
Các thuộc tính:
store:
Store
Redux Store instance object, phải được truyền.
namespace:
string
Mặc định có giá trị rỗng.
Sau khi namespace được thiết lập, tất cả dependent state data sẽ được lưu trữ trong đối tượng với trường namespace là key value. Trong trường hợp này, giá trị state cần được đọc cùng với giá trị của trường namespace.
Giả sử
namespace: '$store'
, bạn cần sử dụngthis.data.$store.xxx
để nhận giá trị dependent state trong Page (hoặc Component) và bạn cần thêm tiền tố trong XML<view>{{ $store.xxx }}</view>
Ý nghĩa của namespace:
- Xác định dữ liệu nào đến từ store.
- Dữ liệu trong store phải được dispatch để kích hoạt việc cập nhật. Namespace có thể giúp tránh dùng nhầm
this.setData
khiến dữ liệu trong store bị thay đổi không mong muốn nhờ có thêm tiền tố namespace:this.setData({ '$store.xxx': xxx })
component2:
boolean
Có mở component thứ 2 hay không,giá trị mặc định
false
. Chỉ có Alipay applet hỗ trợ.
Hàm này phải được gọi trước tất cả các code liên quan đến
store
Best Practices:
Làm cách nào đảm bảo rằng
setProvider
được gọi trước tất cả mã sử dụngstore
?Bạn có thể tạo một tập tin .js riêng biệt (ví dụ
setupStore.js
) để gọisetProvider
,Import tập tin này ở đầu tập tinapp.js
.// setupStore.js import store from 'your/store/path' // WeChat Mini Program import { setProvider } from 'redux-miniprogram-bindings' setProvider({ store })
// app.js import './setupStore.js' App({})
connect:(config) => (options) => void | options
Kết nối store
Thuộc tính:
type:
"page" | "component"
Đối tượng kết nối. Nhận giá trị:
page
、component
; mặc định làpage
.mapState:
(string | ((state: Object) => Object))[]
State mà instance phụ thuộc vào,tuỳ chọn
Dependent state data sẽ được inject vào dữ liệu của store, và sẽ được tự động cập nhật khi state biến đổi sau đó. Nó có kiểu dữ liệu là mảng; mảng này có thể chứa string hoặc hàm.
Mảng string:Các string là các key value tương ứng của dependent state. Page hay component sẽ tự động cập nhật trạng thái và đi vào hàng đợi theo lô chờ kích hoạt kết xuất view khi dependent state thay đổi.
{ // state1, state2 are the key values in the state of the store // store.getState().state1 // store.getState().state2 mapState: ['state1', 'state2'] }
Mảng hàm:Hàm nhận state làm tham số. Dữ liệu mới nhất của state có thể truy xuất thông qua đối tượng state. Hàm sẽ trả về một object; mỗi mục trong object đó có thể thuộc bất kỳ kiểu dữ liệu nào, thường dựa trên dữ liệu kết hợp state.
Phương thức này thực thi hàm khi dữ liệu ở store thay đổi (không nhất thiết phải là state mà thực thể hiện tại phụ thuộc vào) và sau đó đối chiếu kết quả trả về của hàm với dữ liệu hiện có, và tiến hành kết xuất các thay đổi.
{ mapState: [ (state) => ({ region: state.province + state.city + state.area, name: state.userInfo.name, }), ] }
Mảng vừa string vừa hàm: hàm sẽ được gọi mỗi khi dữ liệu trong store thay đổi. Để tối ưu hoá hiệu suất hoạt động, tốt nhất bạn sử dụng càng nhiều string cũng như càng ít hàm càng tốt đối với mảng loại này. Ngoài ra, hàm nên nhỏ gọn và thực thi nhanh chóng.
{ mapState: [ 'name', (state) => ({ region: state.province + state.city + state.area, }), ] }
mapDispatch:
Object | dispatch => Object
Inject một hàm xử lý dispatch, tuỳ chọn.
Ở dạng object:
key
là tên của hàm tuỳ biến, nó có thể được truy cập thông qua tên bên trong object, vàvalue
là giá trị của hàmactionCreator
.actionCreator
nằm trong hàm tự động gọi dispatch và được inject vào phương thức thực thể (instance method).// Configuration mapDispatch: { methodsName1: actionCreator1, methodsName2: actionCreator2, } // Call this.methodsName1() // equivalent to dispatch(actionCreator1()) // call this.methodsName2(a, b, c) // equivalent to dispatch(methodsName2(a, b, c))
Ở dạng hàm: hàm này sẽ nhận dispatch làm tham số và trả ra một object, bao gồm hàm xử lý sau khi kết thúc tùy chỉnh.
// Configuration mapDispatch: (dispatch) => ({ methodsName1: () => dispatch(actionCreator1()), methodsName2: (...args) => dispatch(actionCreator2(...args)), }) // Transfer this.methodsName1() this.methodsName2(a, b, c)
Lưu ý: Những hàm bị inject thông qua thông qua
mapDispatch
cũng có thể được sử dụng như các hàm xử lý sự kiện trong XML. Nếu bạn cần truyền các tham số hàm, theo mặc định, phương thức xử lý sự kiện (event handler) sẽ truyền đối tượng sự kiện (event object) dưới dạng tham số đầu tiên của hàm.<view onTap="handleAdd">Add</view>
manual:
boolean
Cho phép gọi
Page()
hayComponent()
,giá trị mặc địnhfalse
Nếu mang giá trị
true
,connect
trả về đối tượng tùy chọn đã được xử lý, bạn gọi thủ công Page() hay Component() để khai báo thực thể. Nó hữu ích cho người dùng trong việc tùy biến các mở rộng (extension).const options = connect({ manual: true, })({ // ... }) options.xxx = 'xxx' Page(options)
Gói mở rộng:nếu bạn không thích dùng
connect
hay cách truyền tham số của nó, bạn có thể tự tạo ra gói cho riêng mình.Ví dụ về Page:
// bootstrap.js import { connect } from 'redux-miniprogram-bindings' const oldPage = Page Page = function (options) { const { mapState, mapDispatch, ...restOptions } = options const realOptions = connect({ mapState, mapDispatch, // This must be set to manual, because the Page function has been rewritten. manual: true, })(restOptions) oldPage(realOptions) }
// app.js // Introduce extension import './bootstrap.js' App({})
// use Page({ mapState: [ // ... ], mapDispatch: { // ... }, })
$page
Alias used to connect Page
$page()({})
// equivalent to
connect({ type: 'page' })({})
$component
Alias used to connect Component
$component()({})
// equivalent to
connect({ type: 'component' })({})
Utils
useStore: () => Store
Lấy thực thể store
import { useStore } from 'redux-miniprogram-bindings'
const store = useStore()
// It is equivalent to the following method, but it is not recommended to use the following method when Alipay applet subcontracting, there will be multiple store instances
import store from 'your/store/path'
useState: () => Object
Lấy đối tượng state hiện tại.
import { useState } from 'redux-miniprogram-bindings'
const state = useState()
// It is equivalent to the following method, but it is not recommended to use the following method when Alipay applet subcontracting, there will be multiple store instances
import store from 'your/store/path'
const state = store.getState()
useDispatch: () => Dispatch
Lấy hàm dispatch
import { useDispatch } from 'redux-miniprogram-bindings'
const dispatch = useDispatch()
// It is equivalent to the following method, but it is not recommended to use the following method when Alipay applet subcontracting, there will be multiple store instances
import store from 'your/store/path'
const dispatch = store.dispatch
useSubscribe
Đăng ký (subscription) với store.
Nhận hàm callback khi dữ liệu trong store thay đổi. Hàm này nhận 2 tham số: currState
- trạng hiện thời - và prevState
- trạng thái trước đó. Nó sẽ so sánh 2 trạng thái này.
Trả về một hàm có thể được gọi để huỷ đăng ký.
import { useSubscribe } from 'redux-miniprogram-bindings'
$page()({
onLoad() {
// Enable subscription
this.unsubscribe = useSubscribe((currState, prevState) => {
if (currState.userInfo.name !== prevState.userInfo.name) {
console.log('userName change')
}
})
},
onUnload() {
// Huỷ đăng ký
this.unsubscribe()
},
})
useRef
Lấy tham chiếu đến dữ liệu trong đối tượng state.
Nhận hàm selector. Hàm selector này có tham số là state và có thể trả ra bất kỳ loại giá trị nào (khuyến nghị trả về assembled data sử dụng state).
Trả về đối tượng tham chiếu, có thuộc tính value
chỉ đọc, thông qua đó giá trị mới nhất được hàm selector trả ra có thể được truy xuất.
import { useRef } from 'redux-miniprogram-bindings'
const selector = (state) => state.userInfo.name
const userNameRef = useRef(selector)
setInterval(() => {
// Regardless of whether the state data is changed, the latest value of state.userInfo.name is always obtained
console.log(userNameRef.value)
}, 1000)
Một hàm khác có chức năng tương tự
import { useState } from 'redux-miniprogram-bindings'
const selector = (state) => state.userInfo.name
const getUserName = () => selector(useState())
setInterval(() => {
// Regardless of whether the state data is changed, the latest value of state.userInfo.name is always obtained
console.log(getUserName())
}, 1000)
Việc sử dụng phương thức nào hoàn toàn phụ thuộc vào sở thích cá nhân.
useSelector
Lưu tạm (cache) kết quả của hàm selector.
Nhận state làm tham số và có thể trả ra bất cứ loại giá trị nào (khuyến nghị trả về assembled data sử dụng state).
Đồng thời, nó nhận mảng deps chứa key value của state mà sự thay đổi kết quả của hàm selector phụ thuộc vào.
Trả về một hàm có cùng signature với hàm selector. Mỗi khi hàm được thực thi, giá trị của mỗi phần trong mảng deps của dependency item sẽ được so sánh (shallow comparison) và hàm sẽ tái thực thi chỉ khi nào giá trị của dependency item thay đổi. Từ đó giảm tần suất thực thi các hàm luận lý phức tạp.
// Use with useRef
import { useRef, useSelector } from 'redux-miniprogram-bindings'
// Only re-execute when state.userInfo changes
const userNameselector = useSelector((state) => state.userInfo.name, ['userInfo'])
const userNameRef = useRef(userNameselector)
setInterval(() => {
console.log(userNameRef.value)
}, 1000)
// Use with mapState
import { $page, useSelector } from 'redux-miniprogram-bindings'
// Only re-execute when state.userInfo changes
const userNameSelector = useSelector((state) => ({ userName: state.userInfo.name }), ['userInfo'])
$page({
mapState: [userNameSelector],
})({})