【问题标题】:React Hook : Correct way of using custom hook to handle onClick Event?React Hook:使用自定义钩子处理 onClick 事件的正确方法?
【发布时间】:2020-04-23 01:40:14
【问题描述】:

正如标题所说,使用自定义钩子处理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? 谢谢

【问题讨论】:

  • 你的问题有点奇怪。对我来说看起来不错。为什么你认为它是错误的?
  • 因为我必须在自定义 Hook 中创建一个 handleClick()?我觉得有这个功能来触发重新渲染有点奇怪。

标签: javascript reactjs react-hooks


【解决方案1】:

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

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
};

【讨论】:

  • 谢谢,有没有useEffects?我认为 React 说建议将任何副作用(例如 fetch 或 time interval )放在 useEffects 中?
  • 没关系,我想我明白了。您的代码不会触发 componentDidmount,因为它没有 useEffects。谢谢
【解决方案2】:

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

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)

【讨论】:

  • 谢谢,你介意分享一下调用这个useApi()的代码吗?我对“ setState: setData”有点困惑
  • 我已经更新了答案以适应钩子的使用。如果还有什么不能解释的,请告诉我。我只是将 setData 函数暴露为 setState,以便调用组件可以看到比 setData 更明显的 setState。
  • 谢谢,不胜感激。
猜你喜欢
  • 2021-09-12
  • 2019-07-26
  • 2021-06-16
  • 1970-01-01
  • 2020-02-11
  • 1970-01-01
  • 2021-06-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多