注意:我知道useAbortableFetch钩子。试图重新创建一个简单的版本。
我正在尝试创建一个钩子,它返回一个可以发出可中止的获取请求的函数。
我的想法是,我希望这个钩子能保持状态,并在需要时更新它。更新部分由另一个有能力的输入更改控制
我目前正在做的是
function useApiData(baseUrl){
const [state, setState] = use state({
data: null,
error: null,
loading: false
})
const controller = useRef(new AbortController)
const fetchData = searchTerm => {
if(state.loading){
controller.current.abort()
controller.current = new AbortController;
}
const signal = controller.signal;
setState(state => ({...state, loading: true})
fetch(url + searchTerm, {signal})
.then(res => res.json())
.then(data => {
setState(state => ({...state, data}))
return data
})
.catch(error => {
setState(state => ({...state, error}))
})
.finally(() => setState({...state, loading: false}))
}
const fetchCallback = useCallback(debounce(fetchData, 500), [])
return {...state, search: fetchCallback}
}
使用
function App(){
const dataState = useApiData(url);
return ComponentWithInputElement {...dataState} />
}
function ComponentWithInputElement(props){
const [value, setValue] = useState('')
const onInput = ev => {
setValue(ev.target.value)
props.search(ev.tagert.value)
}
return (
<>
<input value={value} on input={onInput}>
{props.data?.length && <render datacomp>}
</>
)
}
这似乎甚至无法发送第一个请求。有什么办法让这种模式发挥作用吗?
在useEffect中这样做会非常简单,但我无法访问输入值以将其作为dep
useEffect(()=>{
const controller = new AbortController();
const signal = controller.signal
fetch(url + value, {signal})
return () => controller.abort()
},[value])
你试图做的部分事情感觉不到"对";。您尝试做的一件事是将输入值的状态(如表单状态(存储在同一个钩子中。但这些状态并不相同,因为当用户键入时,它(暂时保存回服务器之前(与从服务器获取的状态不同。如果对两者重复使用相同的状态项,则在字段中键入的过程中,将丢失从服务器获取的状态。
你可能会想;但我不再需要它了"——但当新的需求出现时(比如你需要显示一些静态信息和一个可编辑的表单(,这往往是一个错误的抽象。从这个意义上说,从长远来看,它可能会减少的可重用性。
这是一个围绕单个用例建模抽象的经典案例,这是一种常见的陷阱。
您可以在核心挂钩中添加一个新的状态项来管理这个表单状态,但您已经做到了,这样您就只能将表单状态与提取的数据保持在同一级别——在某些情况下,这可能会起作用,但它是";过度覆盖";在其他方面。
这基本上就是所有状态提取库(如react query(的工作方式——提取的数据与表单数据是分开的。表单数据只是从前者初始化为其初始值。但是输入被绑定到";复制";。
如果您刚刚从核心挂钩中的一个附加状态项返回setState
,然后将该setState
传递给子项以用作更改处理程序,那么您想要的是可能的。然后将这个新状态的实际表单字符串值从父级传递给子级,并将其绑定到输入的value
属性。
然而,我鼓励反对它,因为它是一个架构缺陷。您希望保留您的本地状态,只需从提取的状态对其进行初始化即可。如果你只打算在这种情况下使用它,我的建议可能是可以的,但你的答案意味着重用。我想我需要更多关于这种模式在你的应用程序中有多常见的信息。
至于中止——你只需要从钩子返回控制器,这样消费者就可以访问它(假设你想在消费者中中止?(