React Hook:使用自定义钩子处理onClick事件的正确方法?

时间:2020-04-23 01:40:14

标签: javascript reactjs react-hooks

正如标题所述,使用自定义钩子处理onClick事件的正确方法是什么?

当用户点击搜索按钮时,此codesandbox application将在屏幕上显示新的报价。

function App() {
  const [{ data, isLoading, isError }, doFetch] = useDataApi(
    "https://api.quotable.io/random"
  );

  return (
    <Fragment>
      <button disabled={isLoading} onClick={doFetch}>
        Search
      </button>
      {isError && <div>Something went wrong ...</div>}
      {isLoading ? <div>Loading ...</div> : <div>{data.content}</div>}
    </Fragment>
  );
}

我创建了一个名为useDataApi()的自定义钩子,它将从API中获取新的报价。为了在用户单击按钮时更新报价,在useDataApi()内,我创建了一个handleClick(),它将更改点击值的值以触发重新渲染。并且此handleClick()函数将返回到App()

const useDataApi = initialUrl => {
  const [data, setData] = useState("");
  const [click, setClick] = useState(true);
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);

  const handleClick = () => {
    setClick(!click);
  };

  useEffect(() => {
    const fetchData = async () => {
      setIsError(false);
      setIsLoading(true);
      try {
        const result = await axios(initialUrl);

        setData(result.data);
      } catch (error) {
        setIsError(true);
      }
      setIsLoading(false);
    };
    fetchData();
  }, [initialUrl, click]);

  return [{ data, isLoading, isError }, handleClick];
};

这是可行的,但是,我认为这不是正确的解决方案。

我还尝试将fetchData()useEffect中移出并返回fetchData(),它也可以工作。但是根据React Doc的说法,建议在useEffect内移动函数。

const useDataApi = initialUrl => {
  const [data, setData] = useState("");
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);

  const fetchData = async () => {
    setIsError(false);
    setIsLoading(true);
    try {
      const result = await axios(initialUrl);
      setData(result.data);
    } catch (error) {
      setIsError(true);
    }
    setIsLoading(false);
  };

  useEffect(() => {
    fetchData();
  }, []);

  return [{ data, isLoading, isError }, fetchData];
};

此外,对于创建此类应用程序,我使用的方式是否还可以,或者还有另一种正确的解决方案,例如不使用任何useEffects或不创建任何自定义的Hook? 谢谢

2 个答案:

答案 0 :(得分:2)

不确定这是否正确,但这是我的解决方法。

const useDataApi = initialUrl => {
  const [data, setData] = useState("");
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);

  const doFetch = async () => {
    setIsError(false);
    setIsLoading(true);
    try {
      const result = await axios(initialUrl);

      setData(result.data);
    } catch (error) {
      setIsError(true);
    }
    setIsLoading(false);
  };

  return [{ data, isLoading, isError }, doFetch];
};

顺便说一句,不要直接改变状态。

const handleClick = () => {
    setClick(!click);         // don't do this
    setClick(prev => !prev);    // use this
};

答案 1 :(得分:1)

您的实现很好。我们也在使用类似的东西。希望你觉得它有用。

function useApi(promiseFunction, deps, shouldRun=true){
  // promisFunction returns promise
  const [loading, setLoading] = useState(false)
  const [data, setData] = useState(false)
  const [error, setError] = useState(false)
  
  const dependencies: any[] = useMemo(()=>{
    return [...dependencyArray, shouldRun]
  },[...dependencyArray, shouldRun])
  
  const reload = () => {
    async function call() {
      try {
        setError(null)
        setLoading(true)
        const res = await promiseFunction();
      }
      catch (error) {
        setError(error)
      }
      finally {
        setLoading(false)
      }
    }
    call();
  }

  useEffect(() => {
    if(!shouldRun) return
    setResult(null) //no stale data
    reload()
  }, dependencies)
  
  return {loading, error, data, reload, setState: setData}
}

下面的代码将提供一些有关如何使用它的想法。

function getUsersList(){
  return fetch('/users')
}

function getUserDetail(id){
  return fetch(`/user/${id}`)
}
const {loading, error, data }  = useApi(getUsersList, [], true)

const {loading: userLoading, error: userError, data: userData}
 = useApi(()=>getUserDetail(id), [id], true)

相关问题