繁体   English   中英

用于数据获取的自定义钩子

[英]custom hooks for data fetch

我已经制作了这个自定义挂钩来使用fetcher获取数据并返回 state,其中包含要显示在 antd 表中的数据(喜欢它们)。 我不确定这是使用钩子的正确方法,我已经在我有疑问的代码中添加了注释。

import { useEffect, useReducer } from 'react';
import { useLocation, useHistory } from 'react-router-dom';
import queryString from 'query-string'

const initialState = { data: [], loading: true };

const fetchData = (fetcher, params, dispatch) => {

    //in this case i need to change my state to loading, the reducer will do the trick, but i fill this kinda triky, should i call the fatcher after the dispatch end using an effect?

    dispatch({ type: 'FETCHING' })
    fetcher({ ...params, page: params.page > 0 ? params.page - 1 : params.page }, response => dispatch({ type: 'DATA_RECEIVED', payload: response.data }));
    return () => {
        console.log('unmounting useGridDataFetch')
    };
}

const handlePageChange = (page, history, location) => {
    const qparams = queryString.parse(location.search)
    const qs = Object.keys(qparams).map(key => key != 'page' ? `&${key}=${qparams[key]}` : '')
    history.push(`${history.location.pathname}?page=${page}${qs.reduce((acc,val)=> acc + val , '')}`)
}


const useGridDataFetch = (fetcher, initialParams) => {
    const location = useLocation()
    const history = useHistory()
    const params = { ...initialParams, ...queryString.parse(location.search) }

    // to use history and location inside my reducer to change the querystring on page change, i had to put the reducer definition inside the body of the hook, is there a better way?

    const reducer = (state, action) => {
        switch (action.type) {
            case 'FETCHING':
                return { loading: true };
            case 'DATA_RECEIVED':
                const pagination = {
                    pageSize: action.payload.pageable.pageSize,
                    current: action.payload.pageable.pageNumber + 1,
                    total: action.payload.totalElements,
                    onChange: page => handlePageChange(page, history, location)
                }
                return { dataSource: action.payload.content, loading: false, pagination };
            default:
                throw new Error();
        }
    }

    const [state, dispatch] = useReducer(reducer, initialState);
    useEffect(() => fetchData(fetcher, params, dispatch), [location.search])

    return [state, (_params) => fetchData(fetcher, { ...params, ..._params }, dispatch)]
}


export default useGridDataFetch

一般来说,你可以给我的每一个改进我的代码的提示都会很感激。

谢谢。

我会尽力帮助你

首先,您必须从钩子中提取减速器。 reducer function 应该独立于钩子,因为useReducer(reducer, initialState); 仅使用这 2 个参数reducerinitialState一次,并且不会在每次下一次调用时更新它。 因此,您不能在 reducer 中使用handlePageChange方法。

第二点是您的减速器必须返回具有相同接口的数据,例如{ data: Array, isLoading: Boolean } In your case, you have 3 different response from reducer: after init - { data: Array, loading: Boolean } , after FETCHING - { loading: Boolean } , and after DATA_RECEIVED - { dataSource: Object, loading: Boolean, pagination: Object }

我建议你不要使用 useReducer。 我将建议我实现您的代码

const useGridDataFetch = (fetcher, initialParams) => {
  const { data, isLoading } = useGetData(fetcher, initialParams);
  const { dataSource, pagination } = useGetPreparedData(data);
  
  return {
    isLoading,
    dataSource,
    pagination,
  }
}

const useGetData = (fetcher, initialParams) => {
  const [isLoading, setIsLoading] = useState(false);
  const [data, setData] = useState({});

  const location = useLocation();

  useEffect(() => {
    const params = {
      ...initialParams,
      ...queryString.parse(location.search)
    }
    setIsLoading(true);
    fetcher({
      ...params,
      page: params.page > 0 ? params.page - 1 : params.page
    }, response => {
      setData(response.data);
      setIsLoading(false);
    });
  }, [location.search]);

  return {
    isLoading,
    data
  }
}

const useGetPreparedData = (data) => {
  const location = useLocation();
  const history = useHistory();

  const handlePageChange = useCallback((page) => {
    const qparams = queryString.parse(location.search);
    const qs = Object.keys(qparams).map(key => key != 'page' ? `&${key}=${qparams[key]}` : '')
    history.push(`${location.pathname}?page=${page}${qs.reduce((acc,val)=> acc + val , '')}`)
  }, [location.pathname, location.search, history]);

  return useMemo(() => {
    if (!data.content) {
      return {
        dataSource: [],
        pagination: {}
      }
    }

    const pagination = {
      pageSize: data.pageable.pageSize,
      current: data.pageable.pageNumber + 1,
      total: data.totalElements,
      onChange: page => handlePageChange(page)
    }

    return {
      dataSource: data.content,
      pagination
    };
  }, [data, handlePageChange])
}

我可能对这段代码有一些错误,但我认为主要思想很简单

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM