React Redux API call, data not making it back to component - javascript

In the last couple of days I have been working on my Redux api call. I am actually having a problem getting the data back to the view component. Currently I'm able to see the data in the in the action generator, so I know at least I'm able to get it. However, nothing is showing in the view. I imagine it may have something to do with when it's loading. This is why I tried to load it when the component is rendering.
https://djangoandreact.herokuapp.com/user/1 is what is not loading.
codesandbox: https://codesandbox.io/s/zlor60q3jm?from-embed
Should be able to go to /user/1 at the end similar to going to /1 brings up an article(Tough Hope)
Heres the view component:
import React from "react";
import { connect } from "react-redux";
import { fetchUser } from "../store/actions/userActions";
class UserDetailView extends React.Component {
componentDidMount() {
const userID = this.props.match.params.userID;
fetchUser(userID); //fixed
}
render() {
const { user } = this.props.user;
console.log(user);
return (
<div>
<h3>{user.username}</h3>
</div>
);
}
}
const mapStateToProps = state => ({
user: state.user
});
const mapDispatchToProps = (dispatch, ownProps) => ({
fetchUser: dispatch(fetchUser(ownProps.match.params.userID))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(UserDetailView);
Action generator
import axios from "axios";
import { thunk } from "react-redux";
export function fetchUser(userID) {
console.log(userID);
return dispatch => {
return axios.get(`/api/user/${userID}`).then(res => {
dispatch(fetchUserSuccess(res.data));
console.log(res.data); // loads data
});
};
}
// Handle HTTP errors since fetch won't.
function handleErrors(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}
export const FETCH_USER_BEGIN = "FETCH_USER_BEGIN";
export const FETCH_USER_SUCCESS = "FETCH_USER_SUCCESS";
export const FETCH_USER_FAILURE = "FETCH_USER_FAILURE";
export const fetchUserBegin = () => ({
type: FETCH_USER_BEGIN
});
export const fetchUserSuccess = user => ({
type: FETCH_USER_SUCCESS,
payload: { user }
});
export const fetchUserFailure = error => ({
type: FETCH_USER_FAILURE,
payload: { error }
});
Reducers(which are probably fine):
import {
FETCH_USER_BEGIN,
FETCH_USER_SUCCESS,
FETCH_USER_FAILURE
} from "../actions/actionTypes";
const initialState = {
user: {},
loading: false,
error: null
};
export default function userReducer(state = initialState, action) {
switch (action.type) {
case FETCH_USER_BEGIN:
return {
...state,
loading: true,
error: null
};
case FETCH_USER_SUCCESS:
return {
...state,
loading: false,
user: action.payload.user
};
case FETCH_USER_FAILURE:
return {
...state,
loading: false,
error: action.payload.error,
user: {}
};
default:
return state;
}
}

folks. I found it.
case FETCH_USER_SUCCESS:
return {
...state,
loading: false,
user: action.payload.user
};
user is supposed to be user:action.payload
Also, the user action was supposed to be
export const fetchUserSuccess = user => ({
type: FETCH_USER_SUCCESS,
payload: user
})
WOOOOW. But, honestly, I learned so much about Redux in the last two sleepless nights, it was worth the pain. Really was. Now, instead of copy pasta, I know what an action generator is and does, and reducer (obvi)

Related

Receives from mapStateToProps the old state, not the new one

I'm new to the site, and new to React. I would be very happy if someone would help me please.
I built a chat, which allows messages to be sent between users, and everything works great. But there is a problem with the mapStateToProps in my opinion, because when I add a new message, the state itself only changes when I refresh the page. I want it to change immediately, that I will see the conversation in chat immediately and not in the refresh of the page.
This is the code I wrote down, please I would be happy if someone would help me, if I need to add more code I will do it.
Explanation of the code, I have a chat component, where I do mapStateToProps, and use state what redux, I have another chatReducer file, which is responsible for managing the state of the chat, and in fact maybe there is a problem, because I update the state, I do not get it. Only after refreshing the page, I get it.
I have another chatAction file - through which I call chatReducer.
I think the problem is very small, I probably did not enter the correct code in ChatReducer or mapStateToProps, but other than that everything works.
Please I would be happy if anyone would help me, I am new here on the site.
Another thing, the problem is this, I have an array of conversations. It comes from the mapStateToProps, from the state.chat. Once I add a new call through the submitMessage function, I manage to add the message to the database, but the state itself is not updated directly, that's the problem. I want when someone sends a straight message that it will be updated in chat.
chat component
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getRealtimeUsers } from '../redux/actions/chatActions';
import { updateMessage } from '../redux/actions/chatActions';
import { getRealtimeConversations } from '../redux/actions/chatActions';
class chat extends Component {
state = {
message: '',
chatStarted: false,
chatUser: '',
userUid: null
}
componentDidMount() {
this.props.getRealtimeUsers();
}
initChat = (user) => {
this.setState({
chatStarted: true,
chatUser: user.handle,
userUid: user.handle
});
}
submitMessage = (e) => {
const msgObj = {
user_uid_1: this.props.user.credentials.handle,
user_uid_2: this.state.userUid,
message: this.state.message
}
if (this.state.message !== "") {
this.props.updateMessage(msgObj);
this.setState({ message: '' });
}
console.log(msgObj);
}
render() {
return (
<section>
<div>
<div>
{
this.state.chatStarted ?
this.props.chat.conversations.map(con =>
<div>
<p>{con.message}</p>
</div>)
: null
}
</div>
{
this.state.chatStarted ?
<div>
<textarea
value={this.state.message}
onChange={(e) => this.setState({ message: e.target.value })}
placeholder="Write Message"
/>
<button onClick={this.submitMessage}>Send</button>
</div> : null
}
</div>
</section>
);
}
}
const mapStateToProps = (state) => ({
data: state.data,
chat: state.chat,
user: state.user
});
export default connect(
mapStateToProps,
{ getRealtimeUsers, updateMessage, getRealtimeConversations }
)(chat);
chat reducer
import { userConstants } from "../types"
const initialState = {
users: [],
conversations: []
}
export default function (state = initialState, action) {
switch (action.type) {
case `${userConstants.GET_REALTIME_USERS}_REQUEST`:
return {
...state
}
case `${userConstants.GET_REALTIME_USERS}_SUCCESS`:
return {
...state,
users: action.payload
}
case userConstants.GET_REALTIME_MESSAGES:
return {
...state,
conversations: action.payload
}
case `${userConstants.GET_REALTIME_MESSAGES}_FAILURE`:
return {
...state,
conversations: []
}
default:
return state;
}
}
chat actions
import { userConstants } from "../types";
import axios from 'axios';
export const getRealtimeUsers = () => (dispatch) => {
dispatch({ type: `${userConstants.GET_REALTIME_USERS}_REQUEST` });
axios
.get('/realtimeUsers')
.then((res) => {
console.log(res);
dispatch({
type: `${userConstants.GET_REALTIME_USERS}_SUCCESS`,
payload: res.data
});
})
.catch((err) => console.log(err))
}
export const updateMessage = (msgObj) => (dispatch) => {
axios.post('/updateMessage', msgObj)
.then(() => {console.log("message added") })
.catch((err) => console.log(err));
}
export const getRealtimeConversations = (user) => (dispatch) => {
axios.get('/realtimeConversations',
{
params: {
user: JSON.stringify(user)
}
}
)
.then((res) => {
dispatch({
type: userConstants.GET_REALTIME_MESSAGES,
payload: res.data
});
})
.catch((err) => console.log(err))
}

Redux: Does it make sense to have successReducer and errorReducer to centralize http response management?

I have followed a tutorial that centralizes React app error management with Redux in an errorReducer file:
errorReducer.js
import { GET_ERRORS } from "../actions/types";
const initialState = {};
export default function (state = initialState, action) {
switch (action.type) {
case GET_ERRORS:
return action.payload;
default:
return state;
}
}
That way, whenever there's an error in any http request, the content of the error is stored only in one place in the app:
authActions.js
export const registeruser = (userData, history) => (dispatch) => {
axios
.post("/api/users/register", userData)
.then((res) => {
history.push("/login-page");
})
.catch((err) => {
dispatch({
type: GET_ERRORS,
payload: err.response.data,
});
});
};
And this is how the error response is managed in Register component:
RegisterPage.js
export class RegisterPage extends Component {
constructor() {
super();
this.state = {
errors: {},
};
}
componentWillReceiveProps(nextProps) {
if (nextProps.errors) {
this.setState({ errors: nextProps.errors });
}
}
render() {
const { errors } = this.state;
return (
<>
<div>
{Object.values(errors).map((value) => (
<p>{value}</p>
))}
</div>
</>
);
}
}
RegisterPage.propTypes = {
errors: PropTypes.object.isRequired,
};
const mapStateToProps = (state) => ({
errors: state.errors,
});
export default connect(mapStateToProps, { registeruser })(
withRouter(RegisterPage)
);
#NOTE: I only displayed the code that is relevant to error management.
The same logic is applied for all components.
Sometimes, I would like to display a text when a request is succesful and not necessarily perform an action like the one above:
history.push("/login-page");
What I would like to know is:
Does it make sense to create a success reducer, similar to the error reducer, that I can use to centralize the logic to be executed when any http request has been successful inside React components and display a message accordingly.

How do I reference a variable in redux store from a component index.js (react.js)

I'm a newbie in redux and react.js,
I am trying to make a button disappear on a component in react.js by putting an if condition on the state variable (articlesTable/index.js), which is connected to the redux library function on another file (actions/actionArticles.js), when a button on articlesTable/index.js is clicked, the component is connected with actions/actionArticles.js and dispatch a function in actions/actionArticles.js, which is called loadMoreData().
The function I am trying to configure the state in redux is,
in articlesActions.js
export const loadMoreArticles = () => async (dispatch, getState) => {
const lastArticleKey = Object.keys(getState().articlesMap).pop();
const lastArticle = getState().articlesMap[lastArticleKey];
console.log("articleMap", getState().articlesMap);
console.log("Last article", lastArticleKey, lastArticle);
let filteredArticles = {};
const uid = getState().auth.uid;
const userLevel = getState().profile.userLevel;
} else {
const filteredArticlesArray = [];
var lastArticleReached = false;
...
var lastArticleInArray = filteredArticlesArray[filteredArticlesArray.length-1];
if (lastArticleInArray[0]===lastArticleKey) {
console.log("Bingo, last article reached!");
lastArticleReached = true;
}
else if (lastArticleInArray[0]!== lastArticleKey)
{
console.log("Not last article");
lastArticleReached = false;
}
filteredArticles = Object.fromEntries(filteredArticlesArray.reverse());
}
dispatch({type: LAST_ARTICLE_REACHED, payload: lastArticleReached})
...
};
I dispatch this function with
dispatch({ type: LOAD_MORE_ARTICLES, payload: filteredArticles });
in the code snippet above
The root reducer looks like this,
reducers/index.js
import { combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form';
import articlesStatusReducer from './articlesStatusReducer';
const rootReducer = combineReducers({
...
articlesStatus: articlesStatusReducer,
form: formReducer,
...
});
export default rootReducer;
In articleStatusReducer,
import {LAST_ARTICLE_REACHED} from "../actions/types";
export default function(state = {}, action) {
switch(action.type) {
case(LAST_ARTICLE_REACHED):
console.log(action.payload);
return action.payload;
default:
return state;
}
}
In the articlesTable/index.js, I connect like this
const mapStateToProps = (state) => {
return {
articlesMap: state.articlesMap,
appStatus: state.appStatus,
profile: state.profile,
lastArticleReached: state.articlesStatus,
}
};
const mapDispatchToProps = (dispatch) => {
return {
getArticlesWithData: () => dispatch(getArticlesWithData()),
loadMore: () => dispatch(loadMoreArticles())
}
};
export default compose(
withRouter,
connect(mapStateToProps, mapDispatchToProps)
)(ArticlesTable)
For some reason, articleStatus isn't recognised and when I do
console.log(this.props.articleStatus)
state.articleStatus is undefined
How can I reference state.articleStatus which should be boolean ?
Edit:
For some reason when I put it in a conditional JSX brackets in the render method, it prints out undefined
render () => {
{
console.log(this.props.lastArticleReached),
!this.props.lastArticleReached
: <Button> </Button>
?
<div><div>
}
}``
In function mapStateToProps, you should map state.articleStatus to a props.
somethings like this:
const mapStateToProps = (state) => {
return {
articlesMap: state.articlesMap,
appStatus: state.appStatus,
profile: state.profile,
lastArticleReached: state.articlesStatus,
articleStatus: state.articleStatus
}
};
So this.props.articleStatus will works . :)
The problem is in your reducer. Each case of your reducer must return the state but in your case, your return action.payload.
try something like this.
case(LAST_ARTICLE_REACHED):
console.log(action.payload);
return {...state, articleStatus: action.payload};
like this, articlesStatus became an object with one props, articleStatus, your boolean.
I tried another name for the props but with similar method as Thomas Caillard,
Reducer.js
case(REACH_LAST_ARTICLE):
return {...state, lastArticleReached: action.payload}
in component index.js
const mapStateToProps = (state) => {
return {
...
lastArticleReached: state.articlesMap.lastArticleReached
...
}
};
Thanks for all the helps so far

React/Redux how to pass argument to modify API

I have been using Redux for the past two days, i'm getting to understand it more, however I encountered a problem which has stopped my progress.
I have an API which has interchangeable parameters.
e.g. api.example.com/data/{date}/.. and api.example.com/more-data/{regId}/..
My <Picker /> selects a value and that value should be passed to the URL, which calls the API and gives the selected data; in my case regionId.
The problem is changing the params without causing errors or getting CORS problem with the Api call. I also want to be able to set the regionId to have an initialState, so I can begin the request with a parameter in the url.
ReqService.js (just for async api calling)
class ReqService {
async getRequest(url) {
try {
let response = await (await fetch(url));
let responseJson = await response.json();
return responseJson;
} catch (error) {
console.error('Error: ', error);
}
}
}
export default new ReqService()
actions.js
import ReqService from '../ReqService';
export const IS_FETCHING = 'IS_FETCHING';
export const DATA_FETCHED = 'DATA_FETCHED';
export const ERROR_FETCHING_DATA = 'ERROR_FETCHING_DATA';
const BASE_URL = 'https://api.example.com/';
const DATE_TODAY = new Date().toISOString();
export const getTheData = (regionId) => {
// The regionId is the param i want to pass to the url
const url = `${BASE_URL}/${DATE_TODAY}/${regionId}`;
const request = ReqService.getRequest(url);
return dispatch => {
dispatch({ type: IS_FETCHING });
request
.then((data ) => {
dispatch({ type: DATA_FETCHED, payload: data });
})
.catch(error => {
dispatch({ type: ERROR_FETCHING_DATA, payload: error });
});
};
};
reducer.js
import { IS_FETCHING, DATA_FETCHED, ERROR_FETCHING_DATA } from '../Actions/actions';
const initialState = {
data: [],
fetching: false,
fetched: false,
error: null
};
export const myReducer = (state = initialState, action) => {
console.log(action);
switch (action.type) {
case IS_FETCHING:
return { ...state, fetching: true };
case DATA_FETCHED:
console.log('The Data Fetched ', action.payload);
return {
...state,
fetched: true,
fetching: false,
data: action.payload.data
};
case ERROR_FETCHING_DATA:
return { ...state, fetching: false, error: action.payload.error };
default:
return state;
}
};
The component where the param changes here:
import React, { Component } from 'react'
import {View, Text, Picker} from 'react-native'
import { connect } from '../../node_modules/react-redux';
import { getTheData } from './Actions/actions';
import { bindActionCreators } from "redux";
class FrontPage extends Component {
constructor(props){
super(props);
this.state = {
regionId:0
};
}
changeRegion = (regId) => {
this.props.getTheData(regId);
}
componentDidMount() {}
render() {
return (
<View>
<Text>Front Page</Text>
<Picker selectedValue={this.props.regionId}
onValueChange={itemValue => this.changeRegion(itemValue)}>
<Picker.Item label="One" value='1' />
<Picker.Item label="Two" value='2' />
</Picker>
</View>
)
}
}
const mapStateToProps = state => {
return {
data: state.data,
fetching: state.fetching,
error: state.error
};
};
const mapDispatchToProps = (dispatch) => {
return bindActionCreators({ getTheData }, dispatch);
};
export default connect(mapStateToProps, mapDispatchToProps)(FrontPage);
I dont know if I am doing this correct, I looked at different examples and implemented what seems right. Any help will be great.
From what you are sharing it looks like a good implementation of React and Redux.
If you'd like the Picker component initially have a selected value, then set your state to what it should be. In your case, set the state regionId in your FrontPage component.
this.state = {
regionId: 1 // this will pre-select the first value.
};
"The problem is changing the params without causing errors or getting CORS problem with the Api call."
I'm unsure which problems you have when the params are changed. Can you elaborate or include a screenshot?
As for the CORS error message. Have a look at the article How to fix CORS problems to gain a better understanding of it and what you need to change. When getting this error the problem isn’t in the client application but in the server application. To fix it, you need to enable CORS support at the server level.
You can do this by setting the Access-Control-Allow-Origin header. e.g.
Access-Control-Allow-Origin: *
This will allow any host to access the API, even when they are on a different domain or post.

Redux Cannot read property 'payload' of undefined

I'm loading data from an API using Redux & React. Despite successfully pulling the data and applying it to the state, it's throwing an error:
Uncaught TypeError: Cannot read property 'payload' of undefined.
This occurs after the FETCH_PRODUCT_LISTINGS_PENDING action type in the console.
React Component:
import React from 'react';
import { connect } from 'react-redux';
import store from '../../../store';
import * as ProductListingActions from '../actions/ProductListingActions';
#connect((store) => {
return {
productListing: store.productListing.products
}
})
export default class ProductListingContainer extends React.Component {
constructor(data) {
super();
this.props = data;
this.props.dispatch(ProductListingActions.fetchProductListings());
}
render() {
return <div></div>;
}
}
Reducer:
import CookieHandler from '../../../modules/CookieHandler';
const cookieHandler = new CookieHandler;
export default function reducer(
state = {
products: [],
fetching: false,
fetched: false,
error: null
}, action) {
switch(action.type) {
case "FETCH_PRODUCT_LISTINGS_PENDING":
return {
...state,
fetching: true,
}
break;
case "FETCH_PRODUCT_LISTINGS_REJECTED":
return {
...state,
fetching: false,
error: action.payload
}
break;
case "FETCH_PRODUCT_LISTINGS_FULFILLED":
return {
...state,
fetching: false,
fetched: true,
products: action.payload.data.default
}
break;
}
return state;
}
Actions:
import Config from '../../../Config.js';
import store from '../../../store.js';
import axios from 'axios';
export function fetchProductListings() {
store.dispatch({
type: "FETCH_PRODUCT_LISTINGS",
payload: axios.get(Config.getConfigAPIUrl() + '/cartel/products')
})
}
Any help would be appreciated
You're dispatching a call to dispatch, rather than dispatching an object.
this.props.dispatch(ProductListingActions.fetchProductListings());
function fetchProductListings() {
store.dispatch({
type: "FETCH_PRODUCT_LISTINGS",
payload: axios.get(Config.getConfigAPIUrl() + '/cartel/products')
})
}
if you inline this:
this.props.dispatch(
store.dispatch({
type: "FETCH_PRODUCT_LISTINGS",
payload: axios.get(Config.getConfigAPIUrl() + '/cartel/products')
})
)
Your action creator should not be calling dispatch, it should just return an action:
export function fetchProductListings() {
return {
type: "FETCH_PRODUCT_LISTINGS",
payload: axios.get(Config.getConfigAPIUrl() + '/cartel/products')
}
}
Keep in mind though, axios.get is asynchronous, so payload will be promise. You may want to consider adding redux-thunk to handle the fulfillment of the promise.
I was recently using redux-toolkit for fetching api, and I faced the same problem. When I checked the api result, I saw my payload value was undefined.
I solved this problem by simply returning the result of my api data.
export const getPosts = createAsyncThunk("posts/getPosts", async ()=> {
const res = await axios.get(`${baseURL}/posts/1`)
return res.data;
});

Categories