Need Help Showing Content From Redux Store
Solution 1:
The reason it's happening is because the action you are dispatching is Async (dispatch(getThread(props.match.params.id));
) and hence code execution will not wait for the API result it will execute the next statement which is setLoading(false)
.
You are making your loading stop before API response and hence you are getting undefined error.
Answer :
Reduder:
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;
}
}
Action:
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'));
}
};
};
Component:
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;
Solution 2:
I would suggest to have defined initialState for the reducer
instead
state = {}
const initialState = {
current:{},
threads: [],
success: false
}
function threadReducer(state = initialState, action)
this will help you managing the in-between state.
Also loading state in the component is not always aligned. Consider using
isLoading = useSelector(loadingSelector)
instead useState
Solution 3:
You can simplify Redux async flows implementing the repository pattern without a middleware to handle an API call and dispatch a state. Api calls can be also encapsulated into hooks. For example, take a look to this piece of code, it's not related to your project, but you can use it as a starting point:
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};
};
You can take a look to the full working example here to simplify your code.
Post a Comment for "Need Help Showing Content From Redux Store"