React:实现一个钩子以获取数据并返回加载状态

时间:2019-09-12 14:48:46

标签: javascript reactjs react-hooks

我正在尝试实现一个钩子(useFetch(route, dependencies=[])),该钩子将用于处理fetch数据并返回标量值loading,该值用于确定用户是否当前正在获取新数据。

这在初始呈现中按预期起作用(因为loading的默认状态为true)。

此问题在以后的每个渲染中都会发生。由于loading状态当前设置为false(在初始渲染之后),因此loading状态将在额外的渲染周期内保持为false,然后再更新为true。 useEffect()

如何防止额外的loading=false渲染周期,所以我在其中使用此useFetch()的组件会立即意识到当前正在提取数据,并且可以显示'...loading'文本在render()中(无需渲染两次即可达到这一点)。

useFetch

function useFetch(route, successHandler, dependencies = []) {
    const [loading, setLoading] = useState(true);
    useEffect(() => {
        setLoading(true);
        fetch(route,
            {
                method: 'POST',
                body: JSON.stringify({}),
                headers: {"Content-Type": "application/json"}
            })
            .then(res => res.json())
            .then(
                () => {
                  // Set state data from component here 
                  successHandler();
                }, 
                () => {/*...*/})
            .finally(() => {
                setLoading(false);
            });
    }, dependencies);
    return loading;
}

组件

function Component(){
  const [toggle, setToggle] = useState(false);
  const [options, setOptions] = useState({});
  const loading = useFetch('endpoint', 
    () => setOptions({foo: 'bar'}), 
    [toggle]);

  return <div>
    <button onClick={()=>{setToggle(!toggle)}}>Trigger useFetch()</button>
    {loading ? '...loading': options.foo}
  </div>;
}

任何帮助或设计建议将不胜感激

2 个答案:

答案 0 :(得分:2)

您的代码有两个错误:

const loading = useFetch('endpoint', 
    () => setOptions({foo: 'bar'}), 
    [toggle]);
  1. 依赖项数组应该是useEffect中使用的变量,在您的情况下可以是[route]
  2. [toggle]作为依赖项参数将发送[false][true],具体取决于您的状态变量。它将不受您创建的钩子的useEffect的控制。

查看此示例,它可以帮助您更好地理解:

Edit friendly-ives-xt5k0

答案 1 :(得分:1)

通过此操作,您将更少触发一个渲染,因为切换状态触发了加载状态的状态渲染。除此之外,当状态改变时,它自然会对投降的东西做出反应。希望这会有所帮助。

您也可以将这种效果放在useFetch中。喜欢:

 function useFetch(route, successHandler) {
  const [loading, setLoading] = useState(false)

   const fetchData = useCallback(() => {
     function fetchData () {
       setLoading(true)

       fetch(...)
        .then(successHandler)
        .finally(() => {
            setLoading(false)
        })
     }
   }, [route])

   // include route if you want to update the effects on route change too
   useEffect(fetchData, [route])

   return [fetchData, loading]
}