[英]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:至于
React
的Only 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.