如何实现Recoil selector中的fetch请求在点击事件触发时执行
Recoil selector默认是依赖驱动自动执行的:只要组件中调用useRecoilValueLoadable/useRecoilValue读取了selector值,且selector依赖的状态已初始化,get方法就会在组件挂载时立即执行,这也是当前代码一进页面就发请求、没法等点击事件触发再执行的根本原因。
以下是两种可落地的实现方案,按需选择即可:
方案1:触发开关控制selector执行(推荐,贴合Recoil数据流设计)
核心逻辑是新增一个专门控制请求触发时机的atom作为selector的依赖,只有这个atom被主动更新时,selector才会执行请求逻辑,组件挂载阶段不会触发请求。
步骤1:新增请求触发控制atom
这个atom只负责存储触发标识,不需要复杂结构,默认值设为null代表从未触发过请求:
import { atom, selector, useRecoilValueLoadable, useSetRecoilState } from 'recoil'; // 保留你原有定义的nameState、mobNoState // const nameState = atom({...}) // const mobNoState = atom({...}) // 新增触发开关atom const triggerPhoneRequestAtom = atom<number | null>({ key: 'triggerPhoneRequest', default: null, })
步骤2:改造原有请求selector
把触发开关加入selector依赖,检测到开关未触发时直接返回空闲状态,只有开关被主动更新时才执行fetch逻辑:
export const phoneRequestSelector = selector({ key: "phoneRequestSelector", get: async ({get}) => { const triggerFlag = get(triggerPhoneRequestAtom); // 未触发请求时直接返回空闲状态,不执行fetch if (triggerFlag === null) { return { status: 'idle', data: null }; } const url = `${API_PATH}/goods`; const phoneRequestInfo: PhoneRequestInfo = { userName: get(nameState), phoneNo: get(mobNoState), } const response = await fetch(url, { method: 'POST', body: JSON.stringify(phoneRequestInfo), headers: { 'Content-Type': 'application/json', }, }) const data = await response.json(); return { status: 'success', data }; } })
注意:原代码中fetch的then回调只返回了response流对象,没有解析响应体,实际使用时记得调用
response.json()/response.text()拿到实际返回数据。
步骤3:组件内绑定点击触发逻辑
在点击事件中更新触发开关的值,即可触发selector重新执行发请求:
// 组件内 const requestData = useRecoilValueLoadable(phoneRequestSelector); const setTrigger = useSetRecoilState(triggerPhoneRequestAtom); const onClickHandler = () => { // 每次点击传入全新的唯一值(这里用时间戳),强制Recoil识别到依赖变化,重新执行请求 setTrigger(Date.now()); } // 渲染时根据requestData的状态处理即可: // - requestData.state === 'loading':展示加载态 // - requestData.state === 'hasValue':取requestData.contents里的返回值渲染 // - requestData.state === 'hasError':展示错误提示
如果需要重复点击触发请求,必须保证每次传入setTrigger的值和上一次不同,否则Recoil会判定依赖无变化,不会重新执行selector的get方法。
方案2:useRecoilCallback直接在事件中处理请求(理解成本更低)
如果不想额外加控制atom,可以用useRecoilCallback在点击事件触发时再读取依赖状态、发请求,把结果写入专门存储响应的atom即可,逻辑和普通的事件请求写法一致:
// 新增专门存储请求结果的atom const phoneRequestResultAtom = atom<{ status: 'idle' | 'loading' | 'success' | 'error', data: unknown, error: unknown }>({ key: 'phoneRequestResult', default: { status: 'idle', data: null, error: null } }) // 组件内 const setRequestResult = useSetRecoilState(phoneRequestResultAtom); const requestData = useRecoilValueLoadable(phoneRequestResultAtom); const onClickHandler = useRecoilCallback(async ({snapshot}) => { // 点击触发时才读取最新的依赖值 const userName = await snapshot.getPromise(nameState); const phoneNo = await snapshot.getPromise(mobNoState); // 先更新为加载态 setRequestResult({ status: 'loading', data: null, error: null }); try { const response = await fetch(`${API_PATH}/goods`, { method: 'POST', body: JSON.stringify({userName, phoneNo}), headers: { 'Content-Type': 'application/json' } }) const data = await response.json(); setRequestResult({ status: 'success', data, error: null }); } catch(err) { setRequestResult({ status: 'error', data: null, error: err }); } }, [])
内容的提问来源于stack exchange,提问作者Lokoko
相关产品推荐
相关产品推荐

