Hice un gancho de reacción personalizado, que tiene un efecto de uso y, por ahora, devuelve un conjunto de estados diferentes. Es un gancho para axios, y la esencia es esta:
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 } }
Ahora, en un componente simple, puedo usar fácilmente este gancho personalizado, pero mi pregunta es: ¿Qué pasa si quiero usar mi gancho dentro de un controlador de eventos, digamos:
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> ) }
El problema es que mi gancho useAxios tiene un efecto de uso, por lo que no puedo usarlo dentro de una función que no sea un componente, es decir, handleSubmit
. Entonces, ¿cuál es el trabajo? ¿Hay incluso uno? Gracias por adelantado.
En cuanto a los únicos ganchos de llamada de React desde las funciones de React React
siempre debe:
✅ Call Hooks de los componentes de la función React.
✅ Call Hooks desde Hooks personalizados.
Si no se cumplen estas dos reglas, se produce un resultado inesperado de React
.
Con esas reglas en mente, debe devolver un submitHanlder
desde el enlace de reacción en lugar de simplemente pasar la función de enlace a otro componente como una función de devolución de llamada.
Podría suponer que su intención es activar la solicitud de axios
en el evento de envío. Si es así, es posible lograrlo sin pasar todo el gancho al controlador de eventos.
En primer lugar, como dicen las reglas, debes asegurarte de que tu gancho sea llamado en cada renderizado. Entonces MyComponent
se puede reescribir de la siguiente manera:
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> ) }
Tenga en cuenta que ahora el enlace devuelve una función startRequest
que puede colocar en su controlador y activar ese controlador en cualquier momento apropiado.
Y reorganiza el código de tu gancho como se muestra a continuación:
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 }
El useEffect
ahora solo lo ayuda a limpiar la solicitud de axios sin la necesidad de iniciar una, ya que disparar una solicitud debería ser el trabajo de un controlador de eventos.
Y dado que la devolución de datos por parte de axios es una promesa, no necesita setData
explícitamente para almacenar sus datos de respuesta, por lo que eliminé la línea de useState(null)
.
Echaría un vistazo a bibliotecas populares como SWR ( useSWR
) y apollo-client ( useQuery)
. Su enfoque es algo como esto cuando hacen solicitudes de obtención
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 } }
Al realizar solicitudes de publicación, es algo como esto:
const MyComponent = () => { //useMutation returns a callable function whenever you want const callFunction = useMutation('my-url'); const handleSubmit = () => { await callFunction() } }
También puede echar un vistazo a los ganchos específicos de axios como https://github.com/simoneb/axios-hooks , otro patrón común que usan es incluir una función de refetch
como resultado del gancho, que se puede llamar en cualquier momento (como en un controlador de eventos)
El objetivo del enlace no es realizar la solicitud por usted, el objetivo del enlace es comunicar el estado interno de las cosas (la solicitud de axios, en su caso) al componente, para que pueda representar las cosas en función de ese estado. (como estados de carga, o los datos).
En su caso, puede cambiar el valor de la consulta según el estado del componente y hacer que el enlace devuelva los datos al componente según sus parámetros. Algo como esto:
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} /> )} </> ) }