引言
本项目在经历了大半年的时间终于迎来了第二篇文章(。・∀・)ノ゙,文章主要内容为封装页面hooks,让函数组件的用法类似于rematch
可以持续关注本项目 github.com/zhixiaoqian…
一、使用方法
1. 无store时的基本用法
import Taro from '@tarojs/taro'
import PropTypes from 'prop-types'
import { usePage, useMount } from '@/hooks'
import { AtTabBar } from 'taro-ui'
const init = {
state: {
url: '/xxx',
tabList: [],
},
reducers: {
handleClick () {
// this中挂载了 state、setState、location、reducers & effects 中的方法
this.setState({ url: '/yyy' })
this.navigateTo(this.state.url)
},
navigateTo (url) {
console.warn(url)
},
},
effects: {
async initData () {
const { url } = this.state
await new Promise((resolve) => setTimeout(() => resolve(url), 500))
},
},
}
export default function Footer (props) {
// state: 最新的state
// events: 包含reducers及effects里所有的方法
// loading: effects中的所有方法都会有loading状态,如:loading = { [fnName]: false }
// error: effects中的所有方法都会有error
const [state, events, loading, error] = usePage(init)
useMount(() => {
events.initData()
})
const { current } = props
const { tabList } = state
return (
<AtTabBar
fixed
tabList={tabList}
onClick={events.handleClick.bind(this, tabList)}
current={current}
/>
)
}
Footer.propTypes = {
current: PropTypes.number,
}
2. 需要用到store时
import { useSelector, useDispatch } from '@tarojs/redux'
···
const [state, events, loading, error] = usePage(init)
const pageStore = useSelector(({ globalData }) => ({
globalData,
}))
const dispatch = useDispatch()
console.warn(pageStore, dispatch.globalData)
useMount(() => {
// 传入dispatch
events.initData(dispatch)
})
···
二、封装hooks
1. common util js:下面将会用到
export const isObject = function (obj) {
return toString.call(obj) === '[object Object]'
}
export const isBoolean = function (bool) {
return toString.call(bool) === '[object Boolean]'
}
export const isFunction = function (arg) {
return toString.call(arg) === '[object Function]'
}
2. useState-setState:与class Component的保持一致, 暂无实现回调函数
export function useState (initState) {
const [state, setState] = Taro.useState(initState)
const setComboState = newState => {
if (isObject(newState)) {
setState(prevState => {
return { ...prevState, ...newState }
})
} else {
setState(newState)
}
}
return [state, setComboState]
}
3. usePage:用法保持与rematch一致
- 将接受的数据处理成 state, pageEvents: { ...reducers, ...effects }
- effects 特殊处理成 调用是setLoading为true, 完成后设置为false, 并处理error状态
- 用一个key去保存某些值 { state, setState, location, loading, ...pageEvents }
- 将方法中的this指向 { state, setState, location, loading, pageEvents }
export function useEventEnhancement (initPageState, state, setState) {
const [loading, setLoading] = useState({})
const [error, setError] = useState(null)
const pageEvents = {}
// 1. 对包含异步的方法做一层封装,获得loading和error状态值
if (initPageState.effects) {
const asyncEvents = initPageState.effects
for (const eventName in asyncEvents) {
const event = asyncEvents[eventName]
if (isFunction(event)) {
pageEvents[eventName] = async function (...args) {
try {
setLoading({
[eventName]: true,
})
if (error) {
console.log('reset error')
setError(null)
}
await event.call(this, ...args)
} catch (err) {
// 请求异常在request那里有打印,这里打印非请求异常,比如代码语法错误等
if (!isBoolean(err.success)) {
console.log('err', err)
}
const title = err.msg ||
err.error ||
err.message ||
'服务器开了小差请稍后重试'
if (err.proxy) {
showToast({
title,
})
} else {
setError(err)
}
}
setLoading({
[eventName]: false,
})
}
}
}
}
if (initPageState.reducers) {
const events = initPageState.reducers
for (const eventName in events) {
const event = events[eventName]
if (isFunction(event)) {
pageEvents[eventName] = event
}
}
}
initPageState._instance = Object.assign(initPageState._instance, {
state,
setState,
loading,
...pageEvents,
})
// 2. 将所有方法加入到this环境中,这样就可以直接调用其他方法
for (const propName in pageEvents) {
const handler = pageEvents[propName]
if (isFunction(handler)) {
pageEvents[propName] = function (...args) {
// 此处可以收集formId,以及点击事件
return handler.call(initPageState._instance, ...args)
}
}
}
return [pageEvents, loading, error]
}
export function usePage (initPageState = {}) {
if (!initPageState._instance) initPageState._instance = {}
const [state, setState] = useState(initPageState.state || {})
// 缓存路由信息
useRouter(params => {
initPageState._instance.location = params
})
const [events, loading, error] = useEventEnhancement(
initPageState,
state,
setState
)
return [state, events, loading, error]
}
更详细的内容可前往 github.com/zhixiaoqian…
总结
至此,一个简单的Taro Hook开发方式封装完成,在开发时可根据实际业务进行修改,在接下来的文章里会写关于小程序、云函数等等一些问题的解决方案,喜欢这篇的话请继续关注后续文章。