Redux is executing the wrong action - reactjs

I am trying to execute the submitRecord action that POSTs data to a server. It is supposed to activate the SubmitRecordSuccess upon doing it so the data is posted to the server but is the SubmitRecordFail action that is executed instead as if the app wouldn't had posted the data to the server. However, when I check the data is online. What I'm a doing wrong so the wrong action is executed?
This is the action:
export const submitRecordSuccess = ( id, recordData ) => {
return {
type: actionTypes.SUBMIT_RECORD_SUCCESS,
recordId: id,
recordData: recordData
};
};
export const submitRecordFail = ( error ) => {
return {
type: actionTypes.SUBMIT_RECORD_FAIL,
error: error
};
};
export const submitRecordStart = () => {
return {
type: actionTypes.SUBMIT_RECORD_START
};
};
export const submitRecord = ( recordData ) =>{
return dispatch => {
dispatch(submitRecordStart());
axios.post( '/medicalRecords.json', recordData ).then( response => {
dispatch(submitRecordSuccess( response.data, recordData ));
} ).catch( error => {
dispatch(submitRecordFail( error ));
})
}
}
And this is the function that dispatches the action:
const mapDispatchToProps = dispatch => {
return {
onSubmitRecord: (recordData) => dispatch(actions.submitRecord(recordData))
};
};
Finally, this is the reducer:
const reducer = (state = initialState, action) => {
switch (action.type) {
case actionTypes.SUBMIT_RECORD_START:
return {
...state,
loading: true
};
case actionTypes.SUBMIT_RECORD_SUCCESS:
const newRecord = {
...action.recordData,
id: action.recordId
}
return {
...state,
medicalRecords: state.orders.concat(newRecord),
loading: false,
};
case actionTypes.SUBMIT_RECORD_FAIL:
return {
...state,
loading: false
};
default:
return state;
}
};
I console.log the error inside the .catch() and get
TypeError: Cannot read property 'concat' of undefined
at reducer (recordBuilder.js:24)

Initially, you state is initialised as:
state = {
medicalRecords: [],
loading: false,
}
You are getting the error because of this line.
medicalRecords: state.orders.concat(newRecord),
You are trying to access state.orders which is undefined and hence the error.
Please either add order: [] in initialState or use
medicalRecords: state.medicalRecords.concat(newRecord),
you can use spread operator for destructuring array also.
medicalRecords: [ ...state.medicalRecords, newRecord ],

Related

React useEffect doesn't dispatch the redux action after page refresh

I'm trying to render the data from the following object of data which is coming from an API.
{
"code": 0,
"c": "verified",
"d": "verified",
"leaseInfo": {
"infoId": 6
},
"cpfPrice": "500.00",
"carCurrentLocation": {
"id": 1,
"carId": "df47a56a395a49b1a5d06a58cc42ffc4"
},
"n": "verified",
"p": "false",
"ownerCarInfo": {
"brand": "Ferrari",
"model": "0"
},
"serviceFeeRate": 0.10,
"depositPrice": "100.00",
"pics": [
{
"picid": 49,
"carId": "df47a56a395a49b1a5d06a58cc42ffc4"
},
],
"items": {
"itemid": 5,
"carId": "df47a56a395a49b1a5d06a58cc42ffc4"
}
}
I'm using react-redux to dispatch an action, where I will be provided with the data under a state named 'carDetails'.
However, when I try to access the data, if my component is refreshed, carDetails becomes undefined and hence gives "Cannot read property ownerCarInfo of undefined."
I'm obtaining and de-structuring the data of carDetails like this in my React component:
import React, {useEffect} from 'react';
import { useDispatch, useSelector } from 'react-redux';
const CarInfo = ({ match }) => {
const dispatch = useDispatch();
const details = useSelector((state) => state.carDetails);
const { loading, carDetails } = details;
const {pics, carCurrentLocation, items, ownerCarInfo} = carDetails;
useEffect(() => {
dispatch(getCarDetails(match.params.id));
}, [dispatch, match]);
return (
<div>
{loading ? (
<Loader></Loader>
) : (
<>
<p>{d.depositPrice}</p>
<p>{ownerCarInfo.brand}</p>
</>
)}
</div>
);
)
}
As long as the component or the React application is not refreshed, it retrieves data and displays it correctly. The carDetails becomes an empty array as soon as the page is refreshed.
This is the getCarDetails() action:
export const getCarDetails = (id) => async (dispatch, getState) => {
try {
dispatch({
type: CAR_DETAILS_REQUEST,
});
const { userLogin } = getState();
const { userInfo } = userLogin;
const config = {
headers: {
Authorization: userInfo.token,
'Content-Type': 'application/json',
},
};
const { data } = await axios.get(
`${BASE_API}/car/info/getDetails/${id}/${userInfo.bscId}`,
config
);
dispatch({
type: CAR_DETAILS_SUCCESS,
payload: data,
});
} catch (error) {
dispatch({
type: CAR_DETAILS_FAIL,
payload:
error.response && error.response.data.msg
? error.response.data.msg
: error.msg,
});
}
};
This is my reducer:
export const carsDetailsReducer = (state = { carDetails: [] }, action) => {
switch (action.type) {
case CAR_DETAILS_REQUEST:
return { loading: true };
case CAR_DETAILS_SUCCESS:
return { loading: false, carDetails: action.payload };
case CAR_DETAILS_FAIL:
return { loading: false, error: action.payload };
default:
return state;
}
};
This is how I declare carDetails in the redux store.
const reducer = combineReducers({
carDetails: carsDetailsReducer,
});
What is the cause for carDetails becoming undefined and the useEffect not running on page refresh?
If you are using axios your action should look like this with async function and await while you are calling API.
If you are passing API car id in the api link then pass the id in the parameters:
import axios from "axios";
export const loadData = (id) => async (dispatch) => {
dispatch({
type: "CAR_DETAILS_REQUEST",
});
const detailData = await axios.get("http:\\****/id");
dispatch({
type: "CAR_DETAILS_SUCCESS",
payload: {
success: detailData.data,
},
});
};
Reducer:
const initailState = { carDetails: [], loading: true };
export const carsDetailsReducer = (state = initailState, action) => {
switch (action.type) {
case CAR_DETAILS_REQUEST:
return { ...state,
loading: true
};
case CAR_DETAILS_SUCCESS:
return {...state,
loading: false,
carDetails: action.payload
};
case CAR_DETAILS_FAIL:
return { ...state,
loading: false,
error: action.payload };
default:
return ...state;
}
};
Your useEffect should only work when data is fetched:
import React, {useEffect} from 'react';
import { useDispatch, useSelector } from 'react-redux';
const CarInfo = ({ match }) => {
const dispatch = useDispatch();
const details = useSelector((state) => state.carDetails);
const { loading, carDetails } = details;
const {pics, carCurrentLocation, items, ownerCarInfo} = carDetails;
useEffect(() => {
dispatch(getCarDetails(id));
}, [dispatch]);
return (
<div>
{loading ? (
<Loader></Loader>
) : (
<>
<p>{d.depositPrice}</p>
<p>{ownerCarInfo.brand}</p>
</>
)}
</div>
You can also use it without a useEffect by making an onclick() function like this:
const loadDetailHandler = () => {
dispatch(getCarDetails(id));
};
return (
<div onClick={loadDetailHandler} >
</div>
If carDetails initial state is an array, then why are you destructuring object properties from it in your UI? Question for another time...
If after reloading the page the state reverts back to the initial state, an empty array is still a defined object. You need to track down what is causing your state.carDetails.carDetails to become undefined. If you examine your reducer notice that your CAR_DETAILS_REQUEST case wipes the carDetails state out and it becomes undefined. Honestly I'm surprised you aren't seeing this issue when your code runs normally without a page reload.
You need to hold on to that state. For good measure, you should always shallow copy the existing state when computing the next state object unless you've good reason to omit parts of state.
export const carsDetailsReducer = (state = { carDetails: [] }, action) => {
switch (action.type) {
case CAR_DETAILS_REQUEST:
return {
...state, // <-- shallow copy existing state
loading: true,
};
case CAR_DETAILS_SUCCESS:
return {
...state, // <-- shallow copy existing state
loading: false,
carDetails: action.payload
};
case CAR_DETAILS_FAIL:
return {
...state, // <-- shallow copy existing state
loading: false,
error: action.payload,
};
default:
return state;
}
};
for me, I think you should save the state in the
`case CAR_DETAILS_REQUEST:
return {
...state, // <-- shallow copy existing state
loading: true,
};
`
to be able to use it before o when you want to use a reducer you should each case
have the old state the reducer return the same sharp of initial state that put it you also used is loading and that not found in the initial state
so try to make the shape of the state
state={
isloading:false,
carDetails: []
}
also try each time to same the state by {...state ,is loading:true}
The problem is in CAR_DETAILS_REQUEST. You only return { loading: true }; so carDetails will be lost and become undefined.
Just update your reducer like this:
case CAR_DETAILS_REQUEST:
return { ...state, loading: true };

React-Redux conditional variable

I'm struggling with react-redux variable for hours...hope someone can help.
The conditional returns to me that the variable order.name is not defined, although everything goes as it should in the reducer and action.
When isLoading === true, it continues rendering {order.name} and I know it is not defined at that point because it takes some time. At that time i set Loader to do it's job..
So it’s not clear to me why he continues to render even though there’s a conditional one that shouldn’t allow it... until isLoading === false.
Here is console.log of orderDetails
import { getOrderDetailsAction } from "../actions/orderAction";
const OrderScreen = ({ match }) => {
const orderId = match.params.id;
const dispatch = useDispatch();
useEffect(() => {
dispatch(getOrderDetailsAction(orderId));
}, [dispatch, orderId]);
const orderDetails = useSelector((state) => state.orderDetails);
const { order, isLoading } = orderDetails;
return isLoading ? <Loader /> : <>{order.name}</>;
};
export default OrderScreen;
Reducer
export const orderDetailsReducers = (
state = { isLoading: true, orderItems: [], shippingAddress: {} },
action
) => {
switch (action.type) {
case ORDER_DETAILS_REQUEST:
return {
...state,
isLoading: true,
};
case ORDER_DETAILS_SUCCESS:
return {
isLoading: false,
order: action.payload,
};
case ORDER_DETAILS_FAILED:
return {
isLoading: false,
error: action.payload,
};
default:
return { state };
}
};
Action
export const getOrderDetailsAction = (id) => async (dispatch, getState) => {
try {
dispatch({
type: ORDER_DETAILS_REQUEST,
});
//Getting TOKEN
const {
userLogin: { userInfo },
} = getState();
//Passing TOKEN
const config = {
headers: {
"auth-token": `${userInfo.token}`,
},
};
const { data } = await axios.get(`/api/orders/${id}`, config);
dispatch({
type: ORDER_DETAILS_SUCCESS,
payload: data,
});
} catch (error) {
dispatch({
type: ORDER_DETAILS_FAILED,
payload: error.response.data.msg,
});
}
};
Check redux action for how isLoading state is changed from redux dev tools
Check reducer name -> state.orderDetails (does this exists ?)
const orderDetails = useSelector((state) => state.orderDetails);
Also, we can correct this
state = { isLoading: true, orderItems: [], shippingAddress: {}, order: {} }
// return entire state and not just isLoading and order
case ORDER_DETAILS_SUCCESS:
return {
...state, <--------
isLoading: false,
order: action.payload,
};
case ORDER_DETAILS_FAILED:
return {
...state, <---------
isLoading: false,
error: action.payload,
};

How to dispatch multiple actions in react redux?

Here is a simple flow of what I want to achieve:
And here is the components executions:
onClick function:
this.props.FetchQueryData({dateRange: this.props.dateData.dateRange, selections: selections});
Redux FetchQueryData function:
export const fetchSidebBatData = (sideBarData) => {
return dispatch => {
dispatch(fetchSidebBatDataBegin()); //For turning on isLoading
console.log("Started");
//For fetching the data
Object.keys(data).forEach(element => { //Here Data is an object to get the right endpoint
axios.post(`${ApiEndPoints.getDropDownHeadersByAdvertiserId}${element}`, sideBarData)
.then(response => {
data[element] = response.data;
//Once the action is completed turning off isLoading
dispatch(fetchSidebBatDataEnd());
})
.catch(e => {
console.log(e);
//If there's an error turning off isLoading
dispatch(fetchSidebBatDataEnd());
});
},console.log(data));
console.log("Ended");
};
};
Here is my reducer:
const reducer = ( state = initState, action ) => {
switch ( action.type ) {
case actionTypes.FETCH_SIDEBAR_DATA: return fetchSideBarData(state, action);
case actionTypes.FETCH_SIDEBAR_DATA_START: return fetchSideBarDataBegin(state);
case actionTypes.FETCH_SIDEBAR_DATA_END: return fetchSideBarDataEnd(state);
default: return state;
}
};
and here is my fetchSideBarDataBegin and fetchSideBarDataEnd functions:
const fetchSideBarDataBegin = (state) => {
const newState = state;
newState.isFetching = true;
return newState;
};
const fetchSideBarDataEnd = (state) => {
const newState = state;
newState.isFetching = false;
return newState;
};
I know I am missing something critical as I am not at all dispatching the data but I am totally new to redux and don't have a good context of how do you dispatch multiple actions within a reducer. Your help is highly appreciated. If you feel this is kind of complex example you can pick your own and just demonstrate the flow as I have shared in image which would be extremely helpful for me in understanding what to do next.
Note my major problem is that I want to set isLoading to true before fetching the data and that change should reflect in component and once the process is over then I want to turn it back to false.
Thanks in Advance
You can update the reducer with the terminology that is quite common in redux:
const initState = {
data: [],
error: false,
isLoading: false
};
const reducer = ( state = initState, action ) => {
switch ( action.type ) {
case actionTypes.FETCH_SIDEBAR_DATA_REQUEST: {
return {
...state,
data: [], // reset data if previous
error: false, // clear previous error if any
isLoading: true,
}
};
case actionTypes.FETCH_SIDEBAR_DATA_SUCCESS: {
return {
...state,
data: action.data,
isLoading: false,
}
};
case actionTypes.FETCH_SIDEBAR_DATA_ERROR: {
return {
...state,
error: true,
isLoading: false,
}
};
default: return state;
}
};
Notice to have three different action types: FETCH_SIDEBAR_DATA_REQUEST, FETCH_SIDEBAR_DATA_SUCCESS, and FETCH_SIDEBAR_DATA_ERROR
Edit: It seems you are doing multiple request in the thunk, you can handle that as:
export const fetchSidebBatData = (sideBarData) => {
return dispatch => {
dispatch(fetchSidebBatDataRequest());
const requests = Object.keys(data).map(element => {
return axios.post(`${ApiEndPoints.getDropDownHeadersByAdvertiserId}${element}`, sideBarData)
.then(response => {
return { [element]: response.data };
});
};
Promise.all(requests).then(data => {
dispatch(fetchSidebBatDataSuccess(data));
}).catch(error) {
dispatch(fetchSidebBatDataError(error));
}
};
};

Redux-saga is not updating store states

I'm using redux-saga to fetch an endpoint and want to present it on first page load using useEffect(). But mine is not fetching anything. The screen is blank and reduxDevTools is also not showing anything. I can't understand what did I miss.
My saga:
export function* watcherSaga() {
yield takeLatest("FETCH_TOP_NEWS_REQUEST", workerSaga);}
function fetchTopNews() {
return axios({
method: 'get',
url: 'https://newsapi.org/v2/top-headlines?country=us&apiKey=API_KEY'
});}
function* workerSaga() {
try{
const response = yield call(fetchTopNews);
const news = response.data.articles;
yield put({ type: "FETCH_TOP_NEWS_SUCCESS", news });
}
catch (error) {
yield put({ type: "FETCH_TOP_NEWS_ERROR", error });
}
}
I defined 3 actions:
const initialState = {
fetching: false,
error: null,
news: []
};
const NewsReducer = (state=initialState, action) => {
switch(action.type){
case types.fetchTopNewsRequest:
return { ...state, fetching: true, error: null };
case types.fetchTopNewsSuccess:
return { ...state, fetching: false, news: action.news[0] };
case types.fetchTopNewsError:
return { ...state, fetching: false, news: null, error: action.error };
default:
return state;
}
}
export default NewsReducer;
At last my component, I imported the fetchTopNewsRequest() action here:
const TopHeadline = (props) => {
const { news, getTopNews } = props;
useEffect(() => {
getTopNews();
}, [getTopNews]);
return (
<div className="newsItem">
<h1>Title: {news.title}</h1>
</div>
);}
const mapStateToProps= (state) => {
return {
news: state.news,
};
};
const mapDispatchToProps = (dispatch) => {
return {
getTopNews: () => dispatch( fetchTopNewsRequest() )
};
};
export default connect(mapStateToProps, mapDispatchToProps)(TopHeadline);
I'm trying to fetch only the articles.title.
DevTools shows it's successfully fetching the data:
Buy my states are not updating:

in order to be iterable non-array objects must have a symbol.iterator () method react native

Problem:
In my React native application, I have created redux-thunk action to access the user's current location. But when Running it give me this error called in order to be iterable non-array objects must have a symbol.iterator () method. This is how I have wrote my actions.
import Geolocation from 'react-native-geolocation-service';
export const GET_CURENT_POSITION = 'location/GET_CURENT_POSITION';
export const GET_CURENT_POSITION_SUCCESS =
'location/GET_CURENT_POSITION_SUCCESS';
export const GET_CURENT_POSITION_FAILED = 'location/GET_CURENT_POSITION_FAILED';
const getCurrentPosition = (payload) => ({
type: GET_CURENT_POSITION,
payload,
});
const getCurrentPositionSuccess = (response) => ({
type: GET_CURENT_POSITION_SUCCESS,
response,
});
const getCurrentPositionFailed = (response) => ({
type: GET_CURENT_POSITION_FAILED,
response,
});
const getposition = () => {
return (dispatch) => {
dispatch(getCurrentPosition({msg: 'startgetting'}));
return Geolocation.getCurrentPosition(
(position) => {
dispatch(getCurrentPositionSuccess(position));
},
(error) => {
dispatch(getCurrentPositionFailed(error));
},
{enableHighAccuracy: true, timeout: 15000, maximumAge: 10000},
);
};
};
export default {
getposition: getposition,
};
This is how I have wrote reducers.
import {locationTypes as types} from '_store/actions';
const location = (
state = {
hasError: false,
errorCode: '',
error: '',
location: '',
},
payload,
) => {
switch (payload.type) {
case types.GET_CURENT_POSITION:
return {
...state,
hasError: false,
error: '',
location: '',
};
case types.GET_CURENT_POSITION_SUCCESS:
return {
...state,
hasError: false,
location: payload,
};
case types.GET_CURENT_POSITION_FAILED:
return {
...state,
hasError: false,
error: payload,
};
default:
return state;
}
};
export default location;
This is how I have dispatched the function in connect.
const mapDispatchToProps = (dispatch) => {
return {
createpatient: (patient) => dispatch(patientActions.createpatient(patient)),
getposition: () => dispatch(locationActions.getposition()),
};
};
I tried a lot to figure out what is wrong but I was unable to do so. Can someone help me with this problem? Thank you very much.

Resources