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

How to use the custom hook on click event which contains useEffect and useReducer?

我正在使用包含用于 API 调用的 useEffect 和 useReducer 的自定义挂钩,我在单击按钮时调用此自定义挂钩,但出现此错误(React Hook“useAuth”在函数“handleClick”中被调用既不是 React 函数组件也不是自定义 React Hook 函数)。 代码如下

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;

理想情况下应该这样写

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;

useReducer 似乎太多了。我会建议一个简单的 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 的完整演示。