-
Notifications
You must be signed in to change notification settings - Fork 8
useRequest
useRequest 是一个功能丰富的异步数据管理的 Hooks,Compose 项目中的网络请求场景使用 useRequest 就够了。
useRequest 通过插件式组织代码,核心代码极其简单,并且可以很方便的扩展出更高级的功能。目前已有能力包括:
- 自动请求/手动请求
- 轮询
- 防抖
- 节流
- 错误重试
- loading delay
- SWR(stale-while-revalidate)
- 缓存
suspend fun mockRequestArticle(): MockArticle {
delay(2000L)
return MockArticle(Clock.System.now().toEpochMilliseconds(), NanoId.generate(200))
}
val (data, loading, error) = useRequest(
requestFn = {
mockRequestArticle()
}
)你只需要将在 requestFn 参数赋值一个lambda闭包,在这个闭包中调用一个suspend修饰的异步函数即可
在组件初次加载时,会自动触发该函数执行。同时自动管理该异步函数的 loading , data , error 等状态。
useRequest 的返回值是一个 七元组,它的类型如下:
Tuple7<TData?, Boolean, Throwable?, ReqFn, MutateFn<TData>, RefreshFn, CancelFn>-
data异步数据 -
loading状态 -
error错误 -
req请求函数 -
mutate修改函数 -
refresh刷新函数 -
cancel取消请求函数
我们使用 kotlin 的解构语法可以轻松的获取他们。
req 请求函数是一个同步函数,它会使用当前组件的协程作用域,你只需要把他当作同步函数在组件中调用即可。
useRequest 的第二个参数是 options,你可以通过这个参数完成各种请求状态管理的配置。
如果设置了 options.manual = true,则 useRequest 不会默认执行,需要通过 req 来触发执行。
import xyz.junerver.compose.hooks.invoke
val (data, loading, error, req) = useRequest(
requestFn = {
mockRequestArticle()
},
optionsOf {
manual = true
}
)
TButton(text = "manual request") { req() }你可以使用顶层函数
optionsOf来方便的创建选项,这个函数支持多个hooks,在本项目中所有需要设置配置的场合都可以是用这个函数
req函数的签名为:(Array<Any?>) -> Unit,你需要手动导入import xyz.junerver.compose.hooks.invoke,这样你就可以将它如同普通函数一样使用
上面我们局的例子的异步请求是没有参数的,但实际场景大部分请求都需要传递参数,我们可以通过 options.defaultParams 来为异步请求设置默认参数:
useRequest(
requestFn = { NetApi.userInfo(it[0] as String) },
optionsOf {
defaultParams = arrayOf("junerver")
}
)在闭包中我们可以通过 it[index] 对参数进行类型转换,如果你在 Jvm/Android 平台使用,我提供了一个方便的转换函数 asSuspendNoopFn
例如上面的例子,如果你可以写成这样:
useRequest(
requestFn = NetApi::userInfo.asSuspendNoopFn(),
optionsOf {
defaultParams = arrayOf("junerver")
}
)useRequest 提供了以下几个生命周期配置项,供你在异步函数的不同阶段做一些处理。
-
onBefore:请求之前触发 -
onSuccess:请求成功触发 -
onError:请求失败触发 -
onFinally:请求完成触发
useRequest(
requestFn = { NetApi.userInfo(it[0] as String) },
optionsOf {
defaultParams = arrayOf("junerver")
onBefore = {
state += "onBefore: ${it.joinToString("、")}"
}
onSuccess = { data, _ ->
println("Lifecycle Lifecycle: onSuccess")
state += "\n\nonSuccess:\nData:$data"
}
onError = { err, pa ->
state += "\n\nonError: ${pa.joinToString("、")}\nError: ${err.message}"
}
onFinally = { _, _, _ ->
state += "\n\nonFinally!"
}
}
)useRequest 提供了 refresh 方法,使我们可以使用上一次的参数,重新发起请求。
假如在读取用户信息的场景中
- 我们读取了 ID 为
junerver的用户信息req("junerver") - 我们通过某种手段更新了用户信息
- 我们想重新发起上一次的请求,那我们就可以使用
refresh来代替req("junerver"),这在复杂参数的场景中是非常有用的
useRequest 提供了 mutate, 支持立即修改 useRequest 返回的 data 参数。
val (userInfo, loading, _, _, mutate) = useRequest(
requestFn = { NetApi.userInfo(it[0] as String) },
optionsOf {
defaultParams = arrayOf("junerver")
}
)
TButton(text = "changeName") {
// 你可以在发起修改的同时,使用 mutate 进行乐观更新
mockFnChangeName(newName)
if (userInfo.asBoolean()) {
mutate {
it!!.copy(name = input)
}
}
}mutate 函数的签名是:fun mutate(mutateFn: (TData?) -> TData)
useRequest 提供了 cancel 函数,用于忽略当前异步请求返回的数据和错误
注意:调用 cancel 函数并不会立即停止 协程job 的执行(这涉及协程的取消机制)
同时 useRequest 会在以下时机自动忽略响应:
- 组件卸载时,正在进行的 promise
- 竞态取消,当上一次 promise 还没返回时,又发起了下一次 promise,则会忽略上一次 promise 的响应
通过设置 options.loadingDelay ,可以延迟 loading 变成 true 的时间,有效防止闪烁。
这在一些快速反回结果的场景将会很有用,简单来说,只要接口响应时间小于你设置的 loadingDelay ,loading 状态将会保持 fasle。
val (userInfo, loading, _, request) = useRequest(
requestFn = { NetApi.userInfo(it[0] as String) },
optionsOf {
defaultParams = arrayOf("junerver")
loadingDelay = 1.seconds
}
)通过设置 options.pollingInterval,进入轮询模式,useRequest 会定时触发 service 执行。
val (userInfo, loading) = useRequest(
requestFn = { NetApi.userInfo(it[0] as String) },
optionsOf {
defaultParams = arrayOf("junerver")
pollingInterval = 3.seconds
pollingWhenHidden = true
pollingErrorRetryCount = 5
}
)-
pollingInterval轮询间隔时间 -
pollingWhenHidden后台仍然发起轮询 -
pollingErrorRetryCount轮询最大错误重试次数