【问题标题】:How to make an HTTP hook reusable on the same component如何使 HTTP 挂钩可在同一组件上重用
【发布时间】:2021-03-21 14:35:41
【问题描述】:

我有一个可以像这样使用的 HTTP 钩子:

const { data, error, isLoading, executeFetch } = useHttp<IArticle[]>('news', []);

在同一个组件中,我想触发另一个 API 调用来 POST 数据并更新其中一篇文章:

const handleChange = (article: IArticle, event: React.ChangeEvent<HTMLInputElement>) => {
        executeFetch(`updateNews?id=${article.id}`, { method: 'post', data: { isRead: event.target.checked }});
    };

    return (
        <>
            <div className={classes.articleListHeader}>
                <h1>Article List</h1>
                <small className={classes.headerSubtitle}>{data.length} Articles</small>
            </div>
            <ul>
                {data.map(article => <Article key={article.id} article={article} handleChange={handleChange}/>)}
            </ul>
        </>
    )

我用来获取数据的自定义钩子:

export function useHttp<T>(initUrl: string, initData: T): UseHttp<T> {
    const initOptions: AxiosRequestConfig = { url: initUrl };

    const [options, setOptions] = useState(initOptions);

    const useHttpReducer = createHttpReducer<T>();
    const [state, dispatch] = useReducer(useHttpReducer, {
        isLoading: false,
        error: '',
        data: initData
    });

    useEffect(() => {
        let cancelRequest = false;

        const fetchData = async (cancelRequest: boolean = false) => {
            if (!options.url) return;

            dispatch({ type: API_REQUEST});
            try {
                const responsePromise: AxiosPromise<T> = axios(options);
                const response = await responsePromise;
                if (cancelRequest) return;
                dispatch({ type: API_SUCCESS, payload: response.data });
            } catch (e) {
                console.log("Got error", e);
                dispatch({ type: API_ERROR, payload: e.message });
            }
        };
        fetchData(cancelRequest);

        return () => {
            cancelRequest = true;
        }

    }, [options]);

    const executeFetch = (url: string, options: AxiosRequestConfig = axiosInitialOptions): void => {
        options.url = url;
        setOptions(options);
    };


    return { ...state, executeFetch}

问题是,当我做这样的事情时,data 替换为新的响应(POST 请求),然后我的 UI 崩溃(没有更多的文章列表..)

当我需要在同一个组件中调用另一个 API 并同时保持 HTTP 挂钩的可重用性时,管理此类情况的最佳做法是什么?

我只是想在我的 GET 请求之后在组件中的某处执行 POST 请求 - 如何以可重用的方式执行它并解决我的问题?

【问题讨论】:

  • 不确定这是否会有所帮助,但您是否查看了 Vercel 的 useSWR
  • 谢谢,但我想学习:D
  • executeFetch 是在组件内部定义的函数吗?如果是,你能添加它的实现吗?现在,我不明白为什么data 的值会改变。
  • @Eldraxm executeFetch 函数是从 useHttp 钩子返回的。
  • 可以重命名两次调用中数据的返回值。示例: const { data: firstAPICall, error: firstError } = useHttp('news', []); const { 数据:secondAPICall,错误:secondError} = useHttp('news', []);

标签: javascript reactjs


【解决方案1】:

您可以重构自定义挂钩以接收回调函数。我省略了cancelRequest的部分,如果你使用axios可以通过CancelToken取消请求:

export function useHttp<T>(initUrl: string): UseHttp<T> {
   const initOptions: AxiosRequestConfig = { url: initUrl };

   const [options, setOptions] = useState(initOptions);

   const useHttpReducer = createHttpReducer<T>();
   const [state, dispatch] = useReducer(useHttpReducer, {
      isLoading: false,
      error: '',
   });

   const fetchData = async (options, callback) => {
      if (!options.url) return;
      dispatch({ type: API_REQUEST});
      try {
         const responsePromise: AxiosPromise<T> = axios(options);
         const response = await responsePromise;
         dispatch({ type: API_SUCCESS, payload: response.data });
         callback(response.data);
      } catch (e) {
         console.log("Got error", e);
         dispatch({ type: API_ERROR, payload: e.message });
      }
   };

   const executeFetch = (url: string, requestOptions: AxiosRequestConfig = axiosInitialOptions, callback): void => {
      options.url = url;
      fetchData({...options, ...requestOptions}, callback);
   };

   return { ...state, executeFetch}
};

用法:

const [articles, setArticles]= useState();
const { error, isLoading, executeFetch } = useHttpRequest();


const handleChange = (article: IArticle, event: React.ChangeEvent<HTMLInputElement>) => {
   executeFetch(`updateNews?id=${article.id}`, { method: 'post', data: { isRead: event.target.checked }}, setArticles);
};

【讨论】:

  • 谢谢,这看起来是个好主意!!任何机会分享它如何与我的自定义钩子一起使用 - 我已经有一个我正在使用的,我发布了代码。我尝试添加回调逻辑,但没有成功。
  • 我更新了我的答案,这只是一个想法,我没有测试过,希望对你有帮助
猜你喜欢
  • 1970-01-01
  • 2021-02-13
  • 2021-06-16
  • 2021-12-24
  • 2021-12-04
  • 1970-01-01
  • 2020-12-30
  • 2021-12-17
  • 2014-03-31
相关资源
最近更新 更多