简体   繁体   English

带有useEffect的自定义react钩子,不能在非组件函数中使用

[英]Custom react hook with useeffect, cant use in non-component function

I made a custom react hook, which has a useEffect and, for now, returns a set of different states.我制作了一个自定义的 react 钩子,它有一个 useEffect 并且现在返回一组不同的状态。 It's a hook for axios, and the gist of it is this:这是 axios 的一个钩子,它的要点是这样的:

export default function useAxios({ url, data = {}, method = "GET"} ) {
  var [loading, setLoading] = useState(true)
  var [data, setData] = useState(null)

  useEffect(function() {
    (async function() {
      // do axios request and set loading and data states
    })()

    return () => // cleanup function that cancels axios request
  }, [])

  return {
    loading,
    data
  }
}

Now, in a simple component I can easily use this custom hook - but my question is: What if I want to use my hook inside an event handler, say:现在,在一个简单的组件中,我可以轻松地使用这个自定义钩子 - 但我的问题是:如果我想在事件处理程序中使用我的钩子怎么办,比如:

export default MyComponent() {
  function handleSubmit(e) {
    var { data } = useAxios({
      url: "/my-end-point",
      data: {
        testInput: e.target.testInput.value
      }
    })
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" name="testInput" />
      <button type="submit">Submit</button>
    </form>
  )
}

Problem is my useAxios hook has a useEffect, and so I cannot use it inside a non-component function, ie handleSubmit .问题是我的 useAxios 钩子有一个 useEffect,所以我不能在非组件函数中使用它,即handleSubmit So what is the work around?那么有什么工作呢? Is there even one?甚至有吗? Thanks in advance.提前致谢。

I would take a look at popular libraries like SWR ( useSWR ) and apollo-client ( useQuery) .我会看看流行的库,如 SWR ( useSWR ) 和 apollo-client ( useQuery) They're approach is something like this when making get requests他们在提出获取请求时的方法是这样的

const MyComponent = () => {
  const [shouldSkip, setShouldSkip] = useState(true);
  const queryResult = useQuery('my-url', {skip: shouldSkip});

  const handleSubmit = () => {
    setShouldSkip(false);
    // this will cause the component to rerender, and skip will now be false
  }

}

When making post requests, its something like this:在发出帖子请求时,它是这样的:

const MyComponent = () => {
  //useMutation returns a callable function whenever you want
  const callFunction = useMutation('my-url');

  const handleSubmit = () => {
    await callFunction()
  }

}

You can also take a look at axios-specific hooks like https://github.com/simoneb/axios-hooks , another common pattern they use is to include a refetch function as a result of the hook, that can be called at anytime (like in an event handler)您还可以查看特定于 axios 的钩子,例如https://github.com/simoneb/axios-hooks ,他们使用的另一种常见模式是包含一个作为钩子结果的refetch函数,可以随时调用(就像在事件处理程序中一样)

As to React 's Only Call Hooks from React Functions , you should always:至于ReactOnly Call Hooks from React Functions ,你应该总是:

✅ Call Hooks from React function components. ✅ 从 React 函数组件调用 Hook。
✅ Call Hooks from custom Hooks. ✅ 从自定义 Hooks 调用 Hooks。

Fail to satisfy these two rules leads to unexpected render result out of React .不满足这两个规则会导致React出现意外的渲染结果。

With those rules in mind, you should return a submitHanlder from react hook instead of just passing the hook function into another component as a callback function.考虑到这些规则,您应该从 react 钩子返回一个submitHanlder ,而不是将钩子函数作为回调函数传递给另一个组件。

I might guess that your intention is to trigger the axios request on the submit event.我可能猜测您的意图是在提交事件上触发axios请求。 If so, it is possible to achieve that without passing whole hook into event handler.如果是这样,则可以在不将整个钩子传递给事件处理程序的情况下实现这一点。

First of all, as the rules say, you have to make sure your hook got called in every render.首先,正如规则所说,你必须确保你的钩子在每次渲染中都被调用。 So the MyComponent can be rewrite in the below way:所以MyComponent可以用下面的方式重写:

export default function MyComponent() {
  var startRequest = useAxios({url: "/my-end-point"}) //<---- useAxios now returns the startRequest function, and will always be called on every render

  return (
    <form onSubmit={(e) => {
      e.preventDefault()
      startRequest({testInput: e.target.testInput.value})  // <----- call your startRequest here in the submit hanlder
      .then(data => {
        //process your data here
      })
    }}>
      <input type="text" name="testInput" />
      <button type="submit">Submit</button>
    </form>
  )
}

Please note that now the hook returns a function startRequest which you can put in your handler, and trigger that handler any time appropriated.请注意,现在钩子返回一个函数startRequest ,您可以将其放入处理程序中,并在适当的时候触发该处理程序。

And rearrange your hook's code like below:并重新排列钩子的代码,如下所示:

export function useAxios({ url, method = "GET"} ) {
  var [loading, setLoading] = useState(true)  
                                            // <------ no setData here

  var startRequest = async function(body = {}) { // <------ Move your input here
    // do axios request and set loading and data states
    setLoading(true)
    await data = axios.post(body)
    setLoading(false)
    return data             // <------- return data as promise
  }

  var cancelRequest = () => // cleanup function that cancels axios request

  useEffect(function() {
    return cancelRequest
  }, []) // useEffect only helps your cancel request on unmounted.

  return startRequest
}

The useEffect now only helps you cleanup axios request without the need to start one, since firing a request should be an event handler's job. useEffect现在只帮助您清理 axios 请求而无需启动,因为触发请求应该是事件处理程序的工作。

And since the data return by axios is in a promise, you don't need to explicitly setData to store your response data so I removed the line of useState(null) .并且由于 axios 返回的数据在承诺中,您不需要显式setData来存储您的响应数据,因此我删除了useState(null)

The point of the hook is not to make the request for you, the point of the hook is to communicate the internal state of stuff (the axios request, in your case) to the component, so that you can render stuff based around that state (like loading states, or the data).钩子的重点不是为您发出请求,钩子的重点是将内容的内部状态(在您的情况下为 axios 请求)传达给组件,以便您可以根据该状态呈现内容(例如加载状态或数据)。

In your case, you can change the value of the query based on the component state, and have the hook return the data to the component based on its parameters.在您的情况下,您可以根据组件状态更改查询的值,并让钩子根据其参数将数据返回给组件。 Something like this:像这样的东西:

const useAxios = ({ query }) => {
    var [loading, setLoading] = useState(true)
    var [data, setData] = useState(null)

    useEffect(function () {
        (async function () {
            setLoading(true)
            // do axios request and set loading and data states
            const request = await axios.get('endpoint', { query })

            setData(request.data)
            setLoading(false)
        })()

        return () => { }// cleanup function that cancels axios request
    }, [])

    return {
        loading,
        data
    }
}

const Component = () => {
    const [query, setQuery] = useState('')
    const { loading, data } = useAxios({ query });

    const submitHandler = (event) => { setQuery(event.target.testInput.value) }

    return (
        <>
            <form onSubmit={submitHandler}>
                <input name="testInput" />
                <input type="submit" />
            </form>
            {loading && (
                <>a spinner</>
            )}
            {data && (
                <DataRenderer data={data} />
            )}
        </>
    )
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 React Hook &quot;useEffect&quot; 在函数 &quot;shoes&quot; 中调用,它既不是 React 函数组件,也不是自定义 React Hook - React Hook "useEffect" is called in function "shoes" which is neither a React function component or a custom React Hook React redux 实例化非组件类 - React redux instantiate non-component class 如何在反应中创建非组件文件 - How to create a non-component file in react React Hook “React.useEffect” 在 function “selectmenu” 中调用,它既不是 React function 组件也不是自定义 React Hook ZC1C425268E68385D1AB5074F1477 - React Hook “React.useEffect” is called in function “selectmenu” which is neither a React function component or a custom React Hook function 在 React Native 中卸载组件期间,清理功能在 UseEffect Hook 中不起作用 - Cleaning Function not Working in UseEffect Hook During Component Unmount in React Native 如何在反应中使用useeffect钩子? - How to use useeffect hook in react? 输入验证与功能组件和 useEffect 挂钩 - Input validation in react with functional component and useEffect hook 当我需要从 Link 组件获取路径名道具时,我可以使用 useEffect 钩子和 function 组件吗? - Can I use useEffect hook and function component when I need to get the pathname props from Link component? 如何从非组件辅助函数访问redux的存储? - How can you access redux's store from a non-component helper function? 在既不是 React 函数组件也不是自定义 React Hook 函数的函数中调用 React Hook “useAxios” - React Hook "useAxios" is called in function that is neither a React function component nor a custom React Hook function
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM