React中使用SWR时点击按钮触发新fetch请求的问题
实现方案
两种主流实现方式,都符合React Hooks调用规则和SWR设计逻辑:
方案1:变更SWR缓存key触发自动重请求
SWR默认以传入的key(这里是你拼接的url字符串)作为缓存唯一标识,只要key发生变化,就会自动发起全新请求。
你可以在请求参数后追加一个可手动更新的标识位,接口会自动忽略多余参数不影响返回结果,实现步骤:
- 修改自定义
useFetchHook,支持接收刷新标识参数,拼入请求url:
import useSWR from "swr"; export function useFetch(category, amount, difficulty, refreshFlag = 0) { // 追加refresh参数,值变化时url作为SWR key同步变化 const url = `https://opentdb.com/api.php?amount=${amount}&category=${category}&difficulty=${difficulty}&type=multiple&refresh=${refreshFlag}`; const fetcher = (...args) => fetch(...args).then((res) => res.json()); const { data, error } = useSWR(url, fetcher, { revalidateOnFocus: false, }); return { data, isLoading: !error && !data, isError: error, }; }
- 在Quiz组件内维护刷新标识state,点击「再玩一次」时更新标识值,即可触发自动重请求:
import { useState, useEffect } from "react"; // 其余导入保持原有逻辑 function Quiz() { const { category, color } = useSelector((state) => state.start); const { amount, difficulty } = useSelector((state) => state.settings); const gameEnded = useSelector((state) => state.end.isEnded); const dispatch = useDispatch(); // 新增刷新标识state const [refreshFlag, setRefreshFlag] = useState(0); const { data, isLoading, isError } = useFetch(category, amount, difficulty, refreshFlag); if (isLoading) return <FlowerSpinner color={color} />; if (isError) return <div>An error has occured.</div>; // 原渲染阶段直接dispatch会触发重渲染警告,移到useEffect中执行 useEffect(() => { if (data?.results) dispatch(updateQuestions(createQuestions(data.results))); }, [data, dispatch]); const handlePlayAgain = () => { // 更新标识值,SWR检测到key变化自动发起新请求 setRefreshFlag(prev => prev + 1); // 此处可添加重置答题进度、分数的业务逻辑 }; return ( <> <Questions /> {gameEnded ? <PlayAgainBtn onClick={handlePlayAgain} /> : <VerifyButton />} </> ); } export default Quiz;
方案2:调用mutate方法强制刷新
之前调用mutate未触发新请求,是因为SWR默认对未过期的缓存不会重新拉取,调用时传入强制重验证配置即可生效,实现步骤:
- 修改
useFetchHook,对外暴露SWR返回的mutate方法:
import useSWR from "swr"; export function useFetch(category, amount, difficulty) { const url = `https://opentdb.com/api.php?amount=${amount}&category=${category}&difficulty=${difficulty}&type=multiple`; const fetcher = (...args) => fetch(...args).then((res) => res.json()); // 解构取出mutate方法 const { data, error, mutate } = useSWR(url, fetcher, { revalidateOnFocus: false, }); return { data, isLoading: !error && !data, isError: error, mutate // 对外暴露供外部调用 }; }
- 在Quiz组件中获取mutate方法,点击按钮时调用强制刷新:
import { useEffect } from "react"; // 其余导入保持原有逻辑 function Quiz() { const { category, color } = useSelector((state) => state.start); const { amount, difficulty } = useSelector((state) => state.settings); const gameEnded = useSelector((state) => state.end.isEnded); const dispatch = useDispatch(); // 取出暴露的mutate方法 const { data, isLoading, isError, mutate } = useFetch(category, amount, difficulty); if (isLoading) return <FlowerSpinner color={color} />; if (isError) return <div>An error has occured.</div>; useEffect(() => { if (data?.results) dispatch(updateQuestions(createQuestions(data.results))); }, [data, dispatch]); const handlePlayAgain = async () => { // 传入revalidate: true配置强制发起新请求 await mutate(undefined, { revalidate: true }); // 此处可添加重置答题进度、分数的业务逻辑 }; return ( <> <Questions /> {gameEnded ? <PlayAgainBtn onClick={handlePlayAgain} /> : <VerifyButton />} </> ); } export default Quiz;
两种方案都可以实现需求,方案1更符合SWR的响应式设计逻辑,不需要手动调用刷新方法,推荐优先使用。
内容的提问来源于stack exchange,提问作者Enzo Filippo
相关产品推荐
相关产品推荐

