#reactjs #typescript #react-hooks
Вопрос:
Я все. Я пытаюсь опубликовать простую форму входа в систему с пользовательским крючком для выборки. Вход в систему работает нормально, если я хорошо аутентифицируюсь в первый раз. Но в случае ошибки, если я попытаюсь повторно проверить форму, компонент, похоже, размонтирован (вызывается функция очистки крючков), и я не могу повторно настроить свой api. Почему этот компонент размонтирован, я не понимаю ? Спасибо за вашу помощь
signin.ts (код был упрощен)
import React, { useState } from 'react'
import useFetch from '../shared/hooks/useFetch'
const Signin: React.FunctionComponent = () => {
const [postData, setPostData] = useState({
url: "",
options: {}
})
type ResponseT = {
id: string,
firstname: string,
lastname: string,
roles: string[],
accessToken: string
} amp; string
const { data, error } = useFetch<ResponseT>(postData.url, postData.options)
const handleSubmit = (e: any) => {
if (e) e.preventDefault()
setPostData({
url: `${process.env.REACT_APP_API_HOST}/signin`,
options: {
method: "POST",
body: JSON.stringify({
email: "testt@test.fr",
password: "bla"
})
}
})
}
return (
<form>
<div>
<div className="form-group">
<button onClick={handleSubmit} className="btn btn-primary btn-block">Sign In</button>
{error amp;amp; <p>{error}</p>}
{data === '' amp;amp; <p style={{ height: '100px', backgroundColor: 'red' }}>Loading...</p>}
</div>
</div>
</form>
)
}
export default Signin
Полезная информация.ts
import { useEffect, useReducer, useRef } from 'react'
interface State<T> {
data?: T
error?: Error
}
type Cache<T> = { [url: string]: T }
type Action<T> =
| { type: 'loading' }
| { type: 'fetched'; payload: T }
| { type: 'error'; payload: Error }
function useNiceFetch<T = unknown>(url?: string, options?: RequestInit): State<T> {
const cache = useRef<Cache<T>>({})
const cancelRequest = useRef<boolean>(false)
const initialState: State<T> = {
error: undefined,
data: undefined,
}
const fetchReducer = (state: State<T>, action: Action<T>): State<T | any> => {
switch (action.type) {
case 'loading':
return { ...initialState, data: '' }
case 'fetched':
return { ...initialState, data: action.payload }
case 'error':
return { ...initialState, error: action.payload }
default:
return state
}
}
const [state, dispatch] = useReducer(fetchReducer, initialState)
useEffect(() => {
if (!url) return
const fetchData = async () => {
if (cancelRequest.current) return
dispatch({ type: 'loading' })
if (cache.current[url]) {
dispatch({ type: 'fetched', payload: cache.current[url] })
return
}
try {
const response = await fetch(url, options) as any
const responseJson = await response.json()
if (!response.ok) {
throw responseJson
}
cache.current[url] = responseJson as T
dispatch({ type: 'fetched', payload: responseJson })
} catch (error) {
if (cancelRequest.current) return
dispatch({ type: 'error', payload: error as Error })
}
}
fetchData()
return () => {
cancelRequest.current = true
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [url, options])
return state
}
export default useFetch
Комментарии:
1. Я не думаю, что это из-за того, что компонент отключается, а скорее из-за того, что запускается повторная передача, и это устанавливает значение
cancelRequest
ref true, и у вас нет никакого способа сбросить его. Я думаю, вам следует пересмотреть структуру кода, поскольку в настоящее время у вас нет возможности повторно запросить что-либо без повторного подключения компонента.useFetch
Крючок должен возвращать функцию выборки для вызова, а также значение ответа. Пользовательская функция выборки должна использовать для запроса маркер отмены, а не ссылку React для любого вида отслеживания жизненного цикла.2. Спасибо вам за ваш ответ. я постараюсь это сделать.