The HTTP request works as expected but I see an additional request to /%3Canonymous%3E that returns 404. This is causing the following error in Redux:
Unhandled Rejection (TypeError): Cannot read property 'data' of
undefined
I don't see the 404 in requests to other routes in other components, for example, /api/users from the user component. I have changed the get requests and routes to match that of the user's but the problem still persists. I have tried the request in postman and it responds with the expected result. The additional request to /%3Canonymous%3E only happens when making get requests to the order resource in the browser (from the app).
GET request:
export const getOrders = () => dispatch => {
axios
.get("api/orders/")
.then(res =>
dispatch({
type: GET_ORDERS,
payload: res.data
})
)
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
Order route:
router.get(
"/",
(req, res) => {
Order.find()
.then(orders => res.json(orders))
.catch(err => {
res.json(err);
});
}
);
getOrder Reducer:
case GET_ORDERS:
return {
...state,
allOrders:
action.payload
};
Entire Order Reducer:
import {
GET_ORDERS,
ADD_ORDER,
EDIT_ORDER,
SET_EDITING_ORDER
} from "../actions/types";
const initialState = {
editingOrder: {},
allOrders: [],
editedOrder: {}
};
export default function(state = initialState, action) {
switch (action.type) {
case SET_EDITING_ORDER:
return {
...state,
editingOrder: action.payload
};
case EDIT_ORDER:
return {
...state,
editedOrder: action.payload
};
case GET_ORDERS:
return {
...state,
allOrders: action.payload
};
case ADD_ORDER:
// state.allOrders.push(action.payload);
return {
...state,
allOrders: [...state.allOrders, action.payload]
// newOrder: action.payload [don't need this
};
default:
return state;
}
}
The data is returned and populated in the redux state but that additional, random request is causing the problem.
it happened to me recently, the problem was a wrong structured response from the same request
This happened to me and I wasn't able to find a fix until now. Might be a more specific issue depending on what you're working on but for me I was loading a component that I had made a ProtectedRoute Component that was checking my isAuthenticated state for permission to render. It would load when I clicked a link to the component because the current isAuthenticated state was true. However, when I refreshed the page it would throw an error and I noticed in Redux DevTools that my isAuthenticated state refreshed and went from false to true.
My Solution: I used a ternary statement to render inside my component. it looked like this:
return !isAuthenticated ? (<p>Loading</p>) : (<Component />)
This recently happend to me and the solution was to use unique action type.
Try to replace the action type variable name along with it value.
In your above code -
Replace -
GET_ORDERS
ADD_ORDER
EDIT_ORDER
SET_EDITING_ORDER
With some some name which is only using in this api request only.
It will fix the issue.
Related
I'm new in Redux React and creating web app where app interact with Lumen API framework. When a request go to server and return with error code 400, 404, 500 any status code (error) except 200 it shows console error and processed after that in React.
I tried pass value when get error at axois.catch({dispatch}) but value update in state by viewing redux-dev-tool but didn't get value at props.
As from API I passed as like:
if ($validator->fails()) {
return response()->json(['type'=> 'error','message'=> $validator->errors()->all()],400);
}
And in my action file as like:
export const loginRequest = formValues => async dispatch => {
await user.post('/user/create', { ...formValues, device_id: 12345}).then(response => {
dispatch ({type: LOGIN, payload: response.data});
}).catch(error => {
if(error.response) {
dispatch ({ type: ERRORS, payload: error.response.data.message });
}
});
}
and in reducer:
const INTIAL_STATE = {
isSignedIn: null,
accessToken: null,
loginMessage: null,
errorMessage: null
};
export default (state = INTIAL_STATE, action) => {
switch (action.type){
case ERRORS:
return { ...state, isSignedIn: false, accessToken:null , errorMessage: action.payload };
default:
return state;
}
};
as last in my component file:
const mapStateToProps = state => {
return {error: state.auth.errorMessage};
};
and console log in render method:
console.log(this.props);
I'm getting POST http://localhost:8000/user/create 500 (Internal Server Error) in my console but the errorMessage value updated in state as looked in redux-dev-tool and after error no code run.
Use the redux-saga-routines for dispatching actions, make your work easy with this module.
Here its documentation link https://github.com/afitiskin/redux-saga-routines
I am creating an async action in Redux with Redux-Thunk and while the action works and returns the data, when it is added to the state it is put inside an object with the same name as the reducer, like so...
The posts key should just have the array of items but it is instead an object with a posts key. What could be causing this issue? Here is the code for the async action...
export function getApiData() {
return dispatch => {
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json())
.then(json => dispatch(
{
type: ActionTypes.GET_API_DATA,
payload: json
}
))
}
}
Here is the code for the reducer...
function posts(state = [], action) {
switch (action.type) {
case ActionTypes.GET_API_DATA:
return {
...state,
posts: action.payload,
}
default:
return state
}
}
The other keys like user and prizes (which are not async actions) work perfectly fine and the new data is added as expected to them. What is leading to this behavior when it comes to the posts key and the async action?
Thanks in advance.
Thge error is in how you process the action in posts reducer. You need to return the array instead of an object
function posts(state = [], action) {
switch (action.type) {
case ActionTypes.GET_API_DATA:
return [
...state,
...action.payload,
] // or return action.payload if you don't want to merge data
default:
return state
}
}
Are you sure that the response is in the correct format? Could you try attach a debugger or a console.log to see the data? Or try send to dispatch the json.data.
I'm trying to build out a feature in my React application showing num of comments for a specific post. Since I don't have this information from backend ill try to make a .lengthon the returned state.
However, it seems like I have built out the reducer in a faulty way but I'm not sure whats wrong with it. Right now I'm just receiving undefined.
Built up as following
Action
export function getPostComments(id) {
const request = API.fetchPostComments(id)
return (dispatch) => {
request.then(({data}) => {
dispatch({type: COMMENTS_GET_POSTCOMMENTS, payload: data})
});
};
}
Reducer
export default function(state = {}, action) {
switch (action.type){
case COMMENTS_GET_POSTCOMMENTS:
return {...state, ...action.payload}
Component
componentWillMount() {
this.props.getPostComments(this.props.id);
}
....
<span>{`comments ${this.props.comments.length}`}</span>
....
function mapStateToProps(state) {
return {
comments: state.comments,
}
}
export default connect(mapStateToProps, {postPostVote, getPostComments})(PostComponent);
EDIT
I am retrieving information from the server if I change my reducer to be return action.payloadI will first receive a comment number of 2 but then this gets wiped replacing it with a 0 since the last post in the list doesn't have any comments. So I'm overwriting here? And that most be wrong aswell
Repo : https://github.com/petterostergren/readable
Thanks for now!
export function getAllCategories() {
return (dispatch) => {
API.fetchAllCategories()
.then(data => {
dispatch({type: CATEGORIES_GET_ALL_CATEGORIES, payload: data})
});
};
}
The call to your API fetchAllCategories is asynchronous, what you were doing before was that you were calling your API but not waiting for it's response. That is why you were getting undefined passed in payload.
So what you needed to do was Chain that fetch call with the another promise.
I am using redux-thunk in my app, and this is how I am using it. See the code below.
export function loadPayments () {
return dispatch => PaymentsAPI.getCustomerPaymentMethods()
.then((paymentMethods) => {
dispatch({
type: actionTypes.LOAD_PAYMENTS_SUCCESS,
payments: paymentMethods
})
})
.catch((error) => {
console.log('Error', error);
})
}
For API Calls I am using Fetch & Axios. You can use any you want. Both are good.
To update your reducer, so that it adds the previous value do the following
case actionTypes.LOAD_SAVED_CARDS_SUCCESS: {
return {
...state,
payments: [ ...state.payments, ...action.payments],
totalpayments: state.payments.length + action.payments.length
};
}
What the reducers will do here is that, it will append all your suppose payments methods i,e previous methods + new methods along with the count update as well.
When users navigate to /places/:slug in my React/Redux app, an ajax call is triggered to get the relavant place data from the database.
While this all works as expected I'm not sure how to show a 404 if no place was found. I have a 404 route setup for when users navigate to a non-route but how can I trigger a 404 using Redux?
In my container component I have:
this.props.dispatch(fetchPlace(this.props.match.params.slug))
and my action:
import axios from "axios";
export function fetchPlace(slug) {
return function(dispatch) {
dispatch({type: "FETCH_PLACE"});
axios.get("/server/places/" + slug)
.then((response) => {
dispatch({type: "FETCH_PLACE_FULFILLED", payload: response.data})
})
.catch((err) => {
dispatch({type: "FETCH_PLACE_REJECTED", payload: err})
})
}
}
and my reducer:
const reducer = (state={
place: {},
isFetching: false,
error: null
}, action) => {
switch(action.type) {
case "FETCH_PLACE" : {
return { ...state, isFetching: true }
}
case "FETCH_PLACE_REJECTED" : {
return { ...state, isFetching: false, error: action.payload }
}
case "FETCH_PLACE_FULFILLED" : {
return { ...state, isFetching: false, place: action.payload }
}
default:
return state
}
}
export default reducer
Ideas
I could use another state property in my reducer called notFound and initialize it to false. Then read the response data payload and detect whether a job has been returned. If not then set notFound to true. But how do I listen for notFound to be true and trigger a 404?
I would suggest returning a promise from fetchPlace action and catch it in container.
Action code which returns promise
export function fetchPlace(slug) {
return function(dispatch) {
dispatch({type: "FETCH_PLACE"});
return axios.get("/server/places/" + slug)
.then((response) => {
dispatch({type: "FETCH_PLACE_FULFILLED", payload: response.data})
})
.catch((err) => {
dispatch({type: "FETCH_PLACE_REJECTED", payload: err})
})
}
}
Container code (You must have access to react-router context or history object)
this.props.dispatch(fetchPlace(this.props.match.params.slug))
.then(() => {})
.catch(() => {
//Redirect to 404 page using history object from props
this.props.history.replace(`404`)
//Redirect to 404 page using context
this.context.router.history.replace(`404`)
})
Another approach would be checking notFound value in componentWillReceiveProps(nextProps) method.
I'm working on a React application to render content of a WordPress website using the WordPress Rest API, Redux and Thunk.
The WordPress API returns posts without detailed information about the category (name, slug, etc). All I'm getting is the id. I'm currently calling an additional action/function to get the detailed category information (output). Below an example of how I'm currently fetching my posts.
// Actions.js
import axios from 'axios'
export const fetchPosts = (page = 1) => {
return {
type: "FETCH_POSTS",
payload: axios.get(`${REST_URL}/wp/v2/posts?per_page=14&page=${page}`)
}
}
|
// PostsReducer.js
const initialState = {
posts: [],
fetching: false,
fetched: false,
error: null
}
export default function reducer(state=initialState, action) {
switch (action.type) {
case "FETCH_POSTS": {
return {
...state,
fetching: true
}
}
case "FETCH_POSTS_REJECTED": {
return {
...state,
fetching: false,
error: action.payload
}
}
case "FETCH_POSTS_FULFILLED": {
return {
...state,
fetching: false,
fetched: true,
posts: action.payload
}
}
}
return state
}
And this is how I'm fetching category information:
export const fetchCategory = (id) => {
return {
type: "FETCH_CATEGORY",
payload: axios.get(`${REST_URL}/wp/v2/categories/${id}`)
}
}
Is there a way to combine my fetchPosts() action with the fetchCategory() action, so it populates the post.categories, returned from fetchPosts() with the more detailed fetchCategory() information?
If you're referring for ajax calls chaining you can use this example to understand how thunk can work for you:
function loadSomeThings() {
return dispatch => {
fetchFirstThingAsync.then(data => { // first API call
dispatch({ type: 'FIRST_THING_SUCESS', data }); // you can dispatch this action if you want to let reducers take care of the first API call
return fetchSecondThingAsync(data), // another API call with the data received from the first call that returns a promise
})
.then(data => {
dispatch({ type: 'SECOND_THING_SUCESS', data }); // the reducers will handle this one as its the object they are waiting for
});
};
}
Basically when we call loadSomeThings we dispatch an new action as a function (fetchFirstThingAsync) as our first ajax call, redux-thunk will catch that before any reducer does as function are not the plain object that reducers can handle, thunk will invoke this function with dispatcher as an argument (along getState and some more args), we wait it out with .then and then we can dispatch a plain object that reducers can handle + returning another promise (fetchSecondThingAsync) that's your second ajax call, we wait it out with .then and again dispatching a plain object that reducers can handle.