Estoy tratando de recuperar información de mi backend y mostrarla. Puedo obtener los datos en mi tienda redux bien, pero cuando trato de mostrarlos en la página, aparece un 'título' de propiedad de error indefinido que no se puede leer. No siempre ocurre, pero cada vez que actualizo lo hace. ¿Algunas ideas? Aquí está el archivo en cuestión. Feliz de compartir otra información, pero estoy bastante seguro de que tanto mi backend como redux funcionan correctamente porque puedo recuperar y mostrar datos en otros lugares.
import '../../styles/article.css'; import { useEffect, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { getThread } from '../../store/actions/forum_actions'; import { clearThread } from '../../store/actions'; const Thread = (props) => { const [loading, setLoading] = useState(true); const threads = useSelector((state) => state.threads); const thread = threads?.current; const dispatch = useDispatch(); useEffect(() => { dispatch(getThread(props.match.params.id)); setLoading(false); }, [dispatch, props.match.params.id]); useEffect(() => { return () => { dispatch(clearThread()); }; }, [dispatch]); return ( <> {loading ? ( <> <p>Loading</p> </> ) : ( <> <p>{thread.title}</p> </> )} </> ); }; export default Thread;Aquí está el reductor:
import { ADD_THREAD, GET_THREADS, GET_THREAD, CLEAR_THREAD } from '../types'; export default function threadReducer(state = {}, action) { switch (action.type) { case ADD_THREAD: return { ...state, lastThreadAdded: action.payload, success: true }; case GET_THREADS: return { ...state, threads: action.payload }; case GET_THREAD: return { ...state, current: action.payload }; case CLEAR_THREAD: return { ...state, current: null }; default: return state; } }Aquí está la acción
import * as threads from './index'; import axios from 'axios'; import { getAuthHeaders } from '../../components/utils/tools'; axios.defaults.headers.post['Content-Type'] = 'application/json'; export const getThread = (id) => { return async (dispatch) => { const request = await axios.get(`/forum/thread/${id}`); dispatch(threads.getThread(request.data)); try { } catch (error) { dispatch(threads.errorGlobal('Error retrieving thread')); } }; };La razón por la que sucede es porque la acción que está enviando es Async ( dispatch(getThread(props.match.params.id)); ) y, por lo tanto, la ejecución del código no esperará el resultado de la API, ejecutará la siguiente declaración que es setLoading(false) . Está haciendo que su carga se detenga antes de la respuesta de la API y, por lo tanto, está obteniendo un error indefinido.
Solución:
Redutor:
import { ADD_THREAD, GET_THREADS, GET_THREAD, CLEAR_THREAD, LOADING_THREAD } from '../types'; export default function threadReducer(state = {}, action) { switch (action.type) { case LOADING_THREAD: return { ...state, loadingThreads: action.payload }; //modified case ADD_THREAD: return { ...state, lastThreadAdded: action.payload, success: true }; case GET_THREADS: return { ...state, threads: action.payload }; case GET_THREAD: return { ...state, current: action.payload }; case CLEAR_THREAD: return { ...state, current: null }; default: return state; } }Acción:
import * as threads from './index'; import axios from 'axios'; import { getAuthHeaders } from '../../components/utils/tools'; axios.defaults.headers.post['Content-Type'] = 'application/json'; export const getThread = (id) => { return async (dispatch) => { try { dispatch(threads.loadingThread(true)); const request = await axios.get(`/forum/thread/${id}`); dispatch(threads.getThread(request.data)); dispatch(threads.loadingThread(false)); } catch (error) { dispatch(threads.loadingThread(false)); dispatch(threads.errorGlobal('Error retrieving thread')); } }; };Componente:
import '../../styles/article.css'; import { useEffect, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { getThread } from '../../store/actions/forum_actions'; import { clearThread } from '../../store/actions'; const Thread = (props) => { const threads = useSelector((state) => state.threads); const loading = useSelector((state) => state.loadingThreads); //modified const thread = threads?.current; const dispatch = useDispatch(); useEffect(() => { dispatch(getThread(props.match.params.id)); setLoading(false); }, [dispatch, props.match.params.id]); useEffect(() => { return () => { dispatch(clearThread()); }; }, [dispatch]); return ( <> {loading ? ( <> <p>Loading</p> </> ) : ( <> <p>{thread.title}</p> </> )} </> ); }; export default Thread;Sugeriría haber definido initialState para el reductor
en cambio
state = {}
const initialState = { current:{}, threads: [], success: false } function threadReducer(state = initialState, action)esto lo ayudará a administrar el estado intermedio.
Además, el estado de carga en el componente no siempre está alineado. Considere usar
isLoading = useSelector(loadingSelector)
en lugar useState
Puede simplificar los flujos asíncronos de Redux implementando el patrón de repositorio sin un middleware para manejar una llamada API y enviar un estado. Las llamadas API también se pueden encapsular en enlaces. Por ejemplo, echa un vistazo a este fragmento de código, no está relacionado con tu proyecto, pero puedes usarlo como punto de partida:
export const useCustomerRepository = () => { const dispatch = useDispatch<Dispatch<CustomerAction>>(); const customerState = useSelector((state: RootState) => state.customerState); const customerApi = useCustomerApi(); const list = async () => { try { dispatch({ type: 'CUSTOMER:LISTING', flag: true }); const customers = await handleAxiosApi<Customer[]>(customerApi.list()); dispatch({ type: 'CUSTOMER:LIST', customers }); } catch (error) { dispatch({ type: 'CUSTOMER:LIST_FAILED', message: getResponseErrorMessage(error) }); } finally { dispatch({ type: 'CUSTOMER:LISTING', flag: false }); } }; return {...customerState}; };Puede echar un vistazo al ejemplo de trabajo completo aquí para simplificar su código.