如何在包含 useEffect 和 useReducer 的点击事件上使用自定义钩子?

发布于 2025-01-17 11:43:09 字数 1647 浏览 0 评论 0 原文

我正在使用包含 useEffect 和 useReducer 的自定义钩子进行 API 调用,并且我在单击按钮时调用此自定义钩子,并且收到此错误(React Hook“useAuth”在函数“handleClick”中调用,该函数既不是 React 函数组件,也不是自定义函数)反应钩子函数)。 代码如下

useAuth.js

import axios from 'axios'
import {useEffect,useReducer} from 'react'

const ACTION = {
 LOADING:'loading',
 SUCCESS:'success',
 ERROR:'error'
}

function reducer(state,action) {
 switch (action) {
    case ACTION.LOADING:
        return {loading:true,data:[]}
    case ACTION.SUCCESS:
        return {...state,loading:false,data:action.payload.data}
    case ACTION.ERROR:
        return {...state,loading:false,data:[],error:action.payload.error}
    default:
        break;
  }
}   
function useAuth (data) {
 const [state, dispatch] = useReducer(reducer, {data:[],loading:true})

 useEffect(() => {
    dispatch({type:ACTION.LOADING})
    const getData = async ()=>{
      try {
          const response = await axios.post('https://expamle.com',data)
          dispatch({type:ACTION.SUCCESS,payload:{data:response.data.data}})
      } catch (error) {
          dispatch({type:ACTION.ERROR,payload:{data:error.response}})
      }
    }
  getData()
 }, [])
 return state
}
export default useAuth

app.js

import logo from './logo.svg';
import './App.css';
import useAuth from './useAuth'

function App() {
 // const {loading,data,error} = useAuth()

 const handleClick = () => {
   const {loading,data,error} = useAuth() // how to use custom hook on click event
 }

 return (
  <div className="App">
    <button onClick={handleClick}></button>
  </div>
 );
}

export default App;

I am using custom hook which contains useEffect and useReducer for API calls and I call this custom hook on button click and I got this error (React Hook "useAuth" is called in function "handleClick" that is neither a React function component nor a custom React Hook function).
Code is below

useAuth.js

import axios from 'axios'
import {useEffect,useReducer} from 'react'

const ACTION = {
 LOADING:'loading',
 SUCCESS:'success',
 ERROR:'error'
}

function reducer(state,action) {
 switch (action) {
    case ACTION.LOADING:
        return {loading:true,data:[]}
    case ACTION.SUCCESS:
        return {...state,loading:false,data:action.payload.data}
    case ACTION.ERROR:
        return {...state,loading:false,data:[],error:action.payload.error}
    default:
        break;
  }
}   
function useAuth (data) {
 const [state, dispatch] = useReducer(reducer, {data:[],loading:true})

 useEffect(() => {
    dispatch({type:ACTION.LOADING})
    const getData = async ()=>{
      try {
          const response = await axios.post('https://expamle.com',data)
          dispatch({type:ACTION.SUCCESS,payload:{data:response.data.data}})
      } catch (error) {
          dispatch({type:ACTION.ERROR,payload:{data:error.response}})
      }
    }
  getData()
 }, [])
 return state
}
export default useAuth

app.js

import logo from './logo.svg';
import './App.css';
import useAuth from './useAuth'

function App() {
 // const {loading,data,error} = useAuth()

 const handleClick = () => {
   const {loading,data,error} = useAuth() // how to use custom hook on click event
 }

 return (
  <div className="App">
    <button onClick={handleClick}></button>
  </div>
 );
}

export default App;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

Spring初心 2025-01-24 11:43:09

理想情况下,应该这样写

import logo from './logo.svg';
import './App.css';
import useAuth from './useAuth'

function App() {
 const { loading , data ,error, dispatch} = useAuth()

 const handleClick = () => {
   dispatch({type:'USER_CLICKED'})
   console.log('check for your data', data)
 }

 return (
  <div className="App">
    <button onClick={handleClick}></button>
  </div>
 );
}

在您的 useAuth 挂钩中,您应该有一个在单击按钮时变为 true 的标志

const ACTION = {
 LOADING:'loading',
 SUCCESS:'success',
 ERROR:'error'
}

function reducer(state,action) {
 switch (action) {
    case ACTION.USER_CLICK:
        return {...state, userClicked: true}
    case ACTION.LOADING:
        return {loading:true,data:[]}
    case ACTION.SUCCESS:
        return {...state,loading:false,data:action.payload.data}
    case ACTION.ERROR:
        return {...state,loading:false,data:[],error:action.payload.error}
    default:
        break;
  }
}   

function useAuth(data) {
  const [state, dispatch] = useReducer(reducer, { data: [], loading: true, userClicked: false });
 
  useEffect(() => {
    if (state.userClicked) {
      dispatch({ type: ACTION.LOADING });
      const getData = async () => {
        try {
          const response = await axios.post("https://expamle.com", data);
          dispatch({
            type: ACTION.SUCCESS,
            payload: { data: response.data.data },
          });
        } catch (error) {
          dispatch({ type: ACTION.ERROR, payload: { data: error.response } });
        }
      };
      getData();
    }
  }, [userClicked]);
  return { state, dispatch }; 
}
export default useAuth;

Ideally, it should be written like this

import logo from './logo.svg';
import './App.css';
import useAuth from './useAuth'

function App() {
 const { loading , data ,error, dispatch} = useAuth()

 const handleClick = () => {
   dispatch({type:'USER_CLICKED'})
   console.log('check for your data', data)
 }

 return (
  <div className="App">
    <button onClick={handleClick}></button>
  </div>
 );
}

In your useAuth hook you should have a flag that becomes true upon button click

const ACTION = {
 LOADING:'loading',
 SUCCESS:'success',
 ERROR:'error'
}

function reducer(state,action) {
 switch (action) {
    case ACTION.USER_CLICK:
        return {...state, userClicked: true}
    case ACTION.LOADING:
        return {loading:true,data:[]}
    case ACTION.SUCCESS:
        return {...state,loading:false,data:action.payload.data}
    case ACTION.ERROR:
        return {...state,loading:false,data:[],error:action.payload.error}
    default:
        break;
  }
}   

function useAuth(data) {
  const [state, dispatch] = useReducer(reducer, { data: [], loading: true, userClicked: false });
 
  useEffect(() => {
    if (state.userClicked) {
      dispatch({ type: ACTION.LOADING });
      const getData = async () => {
        try {
          const response = await axios.post("https://expamle.com", data);
          dispatch({
            type: ACTION.SUCCESS,
            payload: { data: response.data.data },
          });
        } catch (error) {
          dispatch({ type: ACTION.ERROR, payload: { data: error.response } });
        }
      };
      getData();
    }
  }, [userClicked]);
  return { state, dispatch }; 
}
export default useAuth;
一束光,穿透我孤独的魂 2025-01-24 11:43:09

用户ducer 似乎太多了。我建议一个简单的 useasync

function useAsync(f, deps) {
  const [state, setState] = React.useState({fetching: false})
  const [ts, setTs] = React.useState(null)
  React.useEffect(_ => {
    ts && f()
      .then(data => setState({fetching: false, data}))
      .catch(error => setState({fetching: false, error}))
  }, [...deps, ts])
  return [
    state,
    _ => {
      setState({fetching: true, error: null, data: null})
      setTs(Date.now())
    }
  ]
}

您可以使用 useasync 进行任何异步行为。现在,您可以编写 useAuth

function useAuth(payload) {
  return useAsync(_ => { // ✅ useAsync
    return axios.post("https://example.com", payload) // ✅ auth request
  }, [payload.username, payload.password])
}

要在 app 中使用它,

function App() {
  const [username, setUsername] = useState("tommy")
  const [password, setPassword] = useState("pickle$")
  const [{fetching, error, data}, execute] = useAuth({ username, password }) // ✅ useAuth
  if (fetching) return <pre>Loading...</pre>
  if (error) return <pre>Error: {error.message}</pre>
  return <div>
    <input value={username} onChange={e => setUsername(e.target.value)} />
    <input value={password} onChange={e => setPassword(e.target.value)} />
    <button onClick={execute} children="login" /> // ✅ execute
    <pre>{JSON.stringify(data, null, 2)}</pre>
  </div>
}

请参见 useasync 的完整演示, this Q&amp; a

useReducer seems too much for this. I would suggest a simple useAsync,

function useAsync(f, deps) {
  const [state, setState] = React.useState({fetching: false})
  const [ts, setTs] = React.useState(null)
  React.useEffect(_ => {
    ts && f()
      .then(data => setState({fetching: false, data}))
      .catch(error => setState({fetching: false, error}))
  }, [...deps, ts])
  return [
    state,
    _ => {
      setState({fetching: true, error: null, data: null})
      setTs(Date.now())
    }
  ]
}

You can use useAsync for any asynchronous behavior. Now you can write useAuth,

function useAuth(payload) {
  return useAsync(_ => { // ✅ useAsync
    return axios.post("https://example.com", payload) // ✅ auth request
  }, [payload.username, payload.password])
}

To use it in your App,

function App() {
  const [username, setUsername] = useState("tommy")
  const [password, setPassword] = useState("pickle
quot;)
  const [{fetching, error, data}, execute] = useAuth({ username, password }) // ✅ useAuth
  if (fetching) return <pre>Loading...</pre>
  if (error) return <pre>Error: {error.message}</pre>
  return <div>
    <input value={username} onChange={e => setUsername(e.target.value)} />
    <input value={password} onChange={e => setPassword(e.target.value)} />
    <button onClick={execute} children="login" /> // ✅ execute
    <pre>{JSON.stringify(data, null, 2)}</pre>
  </div>
}

See a full demo of useAsync in this Q&A.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文