2-2-9 & 10 & 11 React Hooks 封装公共行为
Posted 沿着路走到底
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2-2-9 & 10 & 11 React Hooks 封装公共行为相关的知识,希望对你有一定的参考价值。
- 渲染函数的输入是属性
- 输出是JSX
- 行为是Hooks
那么对于多个组件公共行为(副作用)应该如何封装呢?
公共Scroll事件的封装
可以通过Hooks封装公共的行为,例如滑动、触摸等复杂的事件处理,可以用hooks封装,从而简化使用。
import UIEventHandler, useEffect, useRef from "react"
class ScrollDescriptor
private left: number = 0
private top: number = 0
private scrollHeight: number = 0
private offsetHeight: number = 0
private scrollToBottomHandlers : Function[] = []
public onScrollToBottom(handler : Function)
this.scrollToBottomHandlers.push(handler)
return () =>
this.scrollToBottomHandlers =
this.scrollToBottomHandlers.filter(x => x !== handler)
private triggerScrollToBottom()
this.scrollToBottomHandlers.forEach(h => h())
public update(
left: number,
top : number,
offsetHeight : number,
scrollHeight : number
)
this.left = left
this.top = top
this.scrollHeight = scrollHeight
this.offsetHeight = offsetHeight
if(this.bottomReached())
this.triggerScrollToBottom()
public bottomReached()
return this.top + this.offsetHeight >= this.scrollHeight
const useScroll = () =>
const scrollInfo = useRef(new ScrollDescriptor())
const scrollHandler : UIEventHandler<htmlDivElement> = (e) =>
const scroller = e.currentTarget
const left = e.currentTarget.scrollLeft
const top = e.currentTarget.scrollTop
scrollInfo.current.update(left, top, scroller.offsetHeight, scroller.scrollHeight)
return
onScroll : scrollHandler,
info : scrollInfo.current
export const ScrollerExample = () =>
const onScroll, info = useScroll()
useEffect(() =>
const unsub = info.onScrollToBottom(() =>
console.log("bottom reached")
)
return () =>
unsub()
, [])
return (
<div
onScroll=onScroll
style=
height: 600,
width: 400,
overflow: "scroll",
>
<div
style=
height: 800,
width: "100%",
background: "red",
></div>
<div
style=
height: 800,
width: "100%",
background: "blue",
></div>
<div
style=
height: 800,
width: "100%",
background: "yellow",
></div>
</div>
)
状态封装
可以使用hooks进行状态的封装,例如之前我们实现的`受控` 组件和`非受控`组件的公共逻辑。
import ChangeEventHandler, useEffect, useState from "react"
export function useValue<T>(
value,
defaultValue,
onChange,
:
value?: T
defaultValue?: T
onChange?: (val : T) => void
): [T, (val: T) => void]
const controlled = typeof value !== "undefined"
const [_value, setValue] = useState<T>(
controlled ? value : defaultValue
)
useEffect(() =>
if (controlled && value !== _value)
setValue(value)
, [value])
useEffect(() =>
if (!controlled && value !== defaultValue)
onChange && onChange(value)
, [_value])
const setHandler = (val: T) =>
if (!controlled)
setValue(val)
else
onChange && onChange(val)
return [_value, setHandler]
链接外部能力
有时候我们需要链接外部的能力,比如说:使用一个外部的对象。
class SomeBuzObject
public getList()
return ([
..., ..., ...
])
public onListChanged(handler :Function)
// ...
这样的情况可以将对象放到`memo` 或者`ref` 中。然后再用`useEffect` 监听外部对象的变化,最后设置一个版本变量,用于更新组件。
const useBuz = () =>
const obj = useMemo(() => new SomeBuzObject(), [])
// or
//const obj = useRef(new SomeBuzObject())
const [, setV] = useState(0)
useEffect(() =>
obj.onListChanged(() =>
setV(x => x + 1)
)
, [])
return obj
const Component = () =>
const obj = useBuz()
// ...
return <div>
obj.getList().map((item) =>
return <... />
)
</div>
封装业务逻辑
Hooks也可以对于业务逻辑进行封装。
例子:封装分页逻辑
注意用`useEffect` 让page变化成为数据变化的因子,而不是将page看做单纯的一个请求数据。
async function request(path, page)
const resp = await fetch(path + "?" + qs.stringify(page))
const data = await resp.json()
return data
function usePaging(path)
const [page, setPage] = useState(0)
const [list, setList] = useState([])
useEffect(() =>
request(path, page)
.then(json =>
setList(json.data.list)
)
, [page])
return
list,
next : () => setPage(x => x + 1),
prev : () => setPage(x => Math.max(0, x - 1)),
const SomeComponent = () =>
const list, next, prev = usePaging("/products")
// 绘制逻辑
1
以上是关于2-2-9 & 10 & 11 React Hooks 封装公共行为的主要内容,如果未能解决你的问题,请参考以下文章