React Redux action object is undefined - javascript

I am new to React-Redux and I have a problem with redux action object. When I am printing the object to the console, it displays correctly as shown below:
Please check my codes.
FeedPage.jsx
class FeedPage extends React.Component {
componentDidMount() {
const { id } = this.props.match.params;
this.props.dispatch(feedActions.getById(id));
console.log("props", this.props);
}
render() {
const { user, feed } = this.props;
...
function mapStateToProps(state) {
const { feed, authentication } = state;
const { user } = authentication;
console.log(JSON.stringify(feed));
return {
user,
feed
};
}
const connectedFeedPage = connect(mapStateToProps)(FeedPage);
export { connectedFeedPage as FeedPage };
reducer.js
export function feeds(state = {}, action) {
switch (action.type) {
case feedConstants.GETBYID_REQUEST:
return {
loading: true
};
case feedConstants.GETBYID_SUCCESS:
console.log("GETBYID_SUCCESS: " + JSON.stringify(action.feed));
return {
feed: action.feed
};
case feedConstants.GETBYID_FAILURE:
return {
error: action.error
};
...
service.js
function getById(id) {
const requestOptions = {
method: 'GET',
headers: authHeader()
};
return fetch(`${config.apiUrl}/feed/${id}`, requestOptions).then(handleResponse);
}
actions.js
function getById(id) {
return dispatch => {
dispatch(request());
feedService.getById(id)
.then(
feed => dispatch(success(feed)),
error => dispatch(failure(error.toString()))
);
};
function request() { return { type: feedConstants.GETBYID_REQUEST } }
function success(feed) { return { type: feedConstants.GETBYID_SUCCESS, feed } }
function failure(error) { return { type: feedConstants.GETBYID_FAILURE, error } }
}
UPDATE:
root reducer
import { combineReducers } from 'redux';
import { authentication } from './authentication.reducer';
import { registration } from './registration.reducer';
import { users } from './users.reducer';
import { alert } from './alert.reducer';
import { feeds } from './feeds.reducer';
const rootReducer = combineReducers({
authentication,
registration,
users,
alert,
feeds
});
export default rootReducer;
This is the screenshot of the logs:
So it is clear that feed object is not empty. But when I am referencing it, it is undefined. Please help as I am stuck and cannot move forward. Any help is great appreciated. Thank you!

In your root reducer, you are saying that the item "feeds" will contain what your feedReducer gives it. In your feedReducer you return "feed: action.feed".
So, in your mapStateToProps, you should be mapping "feeds", not "feed". When you then read the feeds value, it will contain an object such as { feed: xxx } where xxx is what your action originally had in it after your API call.

There's an issue with your reducer. You're mutating component state as it store doesn't get updated when we mutate the state and component doesn't re render
case feedConstants.GETBYID_SUCCESS:
console.log("GETBYID_SUCCESS: " + JSON.stringify(action.feed));
return {
...state,
feed: action.feed
};
in your case Redux doesn't detect a difference in state and won't notify your components that the store has changed and you should return a new copy of state with the necessary change. Hope this would solve the issue
Let me know if the issue still persists.

Related

Redux reducers not being called when using dispatch() in Component

I'm pretty new to react/redux (and javascript in general) so please bear with me on my use of the terminology here...
I am trying to understand how Components and Reducers work so currently I am practicing on a small app that I mostly copied/pasted from a tutorial. The issue I am having is that I am trying to dispatch an action from my Component which alters the Redux state but I am not even seeing my console.log() messages inside my reducer function(s) when I dispatch an action from my Component.
This is what I currently have:
TwApp.js
import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import { TWAPP_USERDATA_AUTH_TOKEN } from '../Constants'
import { loginSuccess } from '../actions'
class TwApp extends Component {
constructor(props) {
super(props)
this.handleChange = this.handleChange.bind(this)
this.handleRefreshClick = this.handleRefreshClick.bind(this)
}
componentDidMount() {
console.log("TwApp componentDidMount")
this.props.loginSuccess() // This is where I want to dispatch an action
}
componentDidUpdate() {
}
handleChange() {
}
handleRefreshClick(e) {
e.preventDefault()
this.props.loginSuccess()
}
render() {
const { loggedIn } = this.props;
console.log("Rendering TwApp.")
if (!loggedIn) {
console.log("user not logged in. loggedIn = " + loggedIn)
}
else {
return (
<div>
<p>Success</p>
</div>
)
}
}
}
function mapStateToProps(state) {
// nothing for now
}
function mapDispatchToProps(dispatch) {
return {
loginSuccess: () => { dispatch(loginSuccess) }
}
}
export default connect(mapStateToProps, mapDispatchToProps)(TwApp)
actions.js
export const USER_LOGIN_SUCCESS = 'USER_LOGIN_SUCCESS'
export function loginSuccess() {
return {
type: USER_LOGIN_SUCCESS,
}
}
reducers.js
// Contains a bunch of stuff that isn't being used yet
import { combineReducers } from 'redux'
import {
USER_LOGIN_SUCCESS, INVALIDATE_SUBREDDIT,
REQUEST_POSTS, RECEIVE_POSTS
} from './actions'
function reducer1(state = {}, action) {
console.log("reducer1: state =" + JSON.stringify(state) + ", action = " + JSON.stringify(action))
switch (action.type) {
case USER_LOGIN_SUCCESS:
console.log("Reducer USER_LOGIN_SUCCESS")
state.loggedIn = true
return state
case RECEIVE_POSTS:
case REQUEST_POSTS:
default:
return state
}
}
function reducer2(state = {}, action) {
console.log("reducer2: state =" + JSON.stringify(state) + ", action = " + JSON.stringify(action))
switch (action.type) {
case INVALIDATE_SUBREDDIT:
case RECEIVE_POSTS:
case REQUEST_POSTS:
default:
return state
}
}
const rootReducer = combineReducers({
reducer1,
reducer2
})
export default rootReducer
Neither reducer1's nor reducer2's console.log() message appears in console. When I call dispatch() from my TwApp component, do all reducers (reducer1 and reducer2) get called? Am I misunderstanding something?
Thanks
You are dispatching 'loginSuccess' which is a function, or by redux terms an action creator.
You should dispatch actions, which are plain objects, to your reducers.
What you want to do is dispatch the action that loginSuccess will create for you:
loginSuccess: () => { dispatch(loginSuccess()) }
comonentDidUpdate as you have it already:
componentDidMount() {
console.log("TwApp componentDidMount")
this.props.loginSuccess()
}
then remove your mapDispatchToProps function completely and export the following:
export default connect(mapStateToProps, {loginSuccess})(TwApp)

React Native: TypeError: undefined is not an object (evaluating '_this.props.data.map')

I wonder if React Native has a bug that needs fixing that gives the following error:
React Native: TypeError: undefined is not an object (evaluating
'_this.props.data.map')
I am pretty good at this and yet I cannot seem to resolve why I am getting this error when I put together this component:
import React, { Component } from "react";
import { View, Animated } from "react-native";
class Swipe extends Component {
renderCards() {
return this.props.data.map(item => {
return this.props.renderCard(item);
});
}
render() {
return <View>{this.renderCards()}</View>;
}
}
export default Swipe;
I have checked and double checked through various debugging practices that the problem is not with my action creator or reducer and after various refactors I got those working correctly.
I decided to do the above component from scratch whereas before I was reusing another component and yet I still get the above error.
I ask if it's a bug with RN because someone else posted a similar problem but they did not get the answer they needed.
It is not a scope issue with this because if I refactor it like so:
renderCards = () => {
return this.props.data.map(item => {
return this.props.renderCard(item);
});
};
It does absolutely nothing for me, same error message. The message saying is not an object is confusing too, it's an array and map() can only iterate through arrays, so not sure what not being an object has to do with it.
The above component is being called in this screen:
import React, { Component } from "react";
import { View, Text } from "react-native";
import { connect } from "react-redux";
import Swipe from "../components/Swipe";
class DeckScreen extends Component {
renderCard(job) {
return (
<Card title={job.title}>
<View style={styles.detailWrapper}>
<Text>{job.company}</Text>
<Text>{job.post_date}</Text>
</View>
<Text>
{job.description.replace(/<span>/g, "").replace(/<\/span>/g, "")}
</Text>
</Card>
);
}
render() {
return (
<View>
<Swipe data={this.props.jobs} renderCard={this.renderCard} />
</View>
);
}
}
const styles = {
detailWrapper: {
flexDirection: "row",
justifyContent: "space-around",
marginBottom: 10
}
};
function mapStateToProps({ jobs }) {
return { jobs: jobs.listing };
}
export default connect(mapStateToProps)(DeckScreen);
This is what the action creator looks like:
import axios from "axios";
// import { Location } from "expo";
import qs from "qs";
import { FETCH_JOBS, LIKE_JOB } from "./types";
// import locationify from "../tools/locationify";
const JOB_ROOT_URL = "https://authenticjobs.com/api/?";
const JOB_QUERY_PARAMS = {
api_key: "5634cc46389d0d872723b8c46fba672c",
method: "aj.jobs.search",
perpage: "10",
format: "json"
};
const buildJobsUrl = () => {
const query = qs.stringify({ ...JOB_QUERY_PARAMS });
return `${JOB_ROOT_URL}${query}`;
};
export const fetchJobs = (region, callback) => async dispatch => {
try {
const url = buildJobsUrl();
let { data } = await axios.get(url);
dispatch({ type: FETCH_JOBS, payload: data });
callback();
} catch (e) {
console.log(e);
}
};
export const likeJob = job => {
return {
payload: job,
type: LIKE_JOB
};
};
and reducer:
import { FETCH_JOBS } from "../actions/types";
const INITIAL_STATE = {
listing: []
};
export default function(state = INITIAL_STATE, action) {
switch (action.type) {
case FETCH_JOBS:
return action.payload;
default:
return state;
}
}
and the combineReducer is setup correctly as well:
import { combineReducers } from "redux";
import auth from "./auth_reducer";
import jobs from "./jobs_reducer";
import likedJobs from "./likes_reducer";
export default combineReducers({
auth,
jobs,
likedJobs
});
The listing: [] is based off the structure of the response I get back. When I console.log(data);, the actual data I care about is inside of listing property. So I set up the INITIAL_STATE to default listing to be an empty array with the intent to ensure I could map over the array and not worry about the case where I have not yet fetched the list of jobs. When I go to the API endpoint directly you can see it below:
I think the problem is simply that this.props.jobs is undefined. Your initial state is defined as { listing: [] }, however you mapStateToProps do { jobs: ... }.
Try changing initialState to { jobs: [] }, so that it always work on your first rendering.
I think your mapStateToProps should be:
mapStateToProps = (state) => {
return { jobs: listings.listing }
}
EDIT
Actually, it could be even better if you 'name' your state correctly in your reducer, like:
const INITIAL_STATE = { jobs: [] }
export default function(state = INITIAL_STATE, action) {
switch (action.type) {
case FETCH_JOBS:
const jobs = action.payload.listings.listing
return { ...state, jobs };
default:
return state;
}
}
Then in your mapStateToProps:
mapStateToProps = ({ jobs }) => {
return { jobs }
}
The issue is in your reducer. Please refer the below changes:
import { FETCH_JOBS } from "../actions/types";
const INITIAL_STATE = {
listing: []
};
export default function(state = INITIAL_STATE, action) {
switch (action.type) {
case FETCH_JOBS:
const { listings } = action.payload
return {...state, listing: listings.listing}
default:
return state;
}
}
Hope this will help.
function mapStateToProps({ jobs }) {
return { jobs: jobs.listing };
}
the above is making confusion for you try the below one
try to put
function mapStateToProps( state ) {
return { jobs: state.jobs.listing };
}
as you have defined your reducer as follow
export default combineReducers({
auth,
jobs,
likedJobs
});
jobs is your variable to access jobs reducer

Cannot access data request Axios, React-Redux

I am trying to make an API request using Axios in React-Redux environment. On the console everything seems to be fine, however if I try to access any of the data I either get undefined or empty array.
This is my component:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { discoverMovie } from '../actions'
//Home component
class Home extends Component {
//make request before the render method is invoked
componentWillMount(){
this.props.discoverMovie();
}
//render
render() {
console.log('movie res ',this.props.movies.movies.res);
console.log('movie ',this.props.movies);
return (
<div>
Home
movie
</div>
)
}
};
const mapStateToProps = (state) => {
return{
movies : state.movies
}
}
export default connect(mapStateToProps, { discoverMovie })(Home);
This is my action
import { DISCOVER_MOVIE } from '../constants';
import axios from 'axios';
//fetch movie
const fetchMovie = () => {
const url = 'https://api.themoviedb.org/3/discover/movie?year=2018&primary_release_year=2018&page=1&include_video=false&include_adult=false&sort_by=vote_average.desc&language=en-US&api_key=72049b7019c79f226fad8eec6e1ee889';
let result = {
res : [],
status : ''
};
//make a get request to get the movies
axios.get(url).
then((res) => {
result.res = res.data.results;
result.status = res.status;
return result;
});
//return the result after the request
return result;
}
//main action
const discoverMovie = () =>{
const result = fetchMovie();
//return the action
return {
type : DISCOVER_MOVIE,
payload : result
}
}
export default discoverMovie;
This is the reducer
import { DISCOVER_MOVIE } from '../constants';
//initial state
const initialState = {
movies : {},
query : '',
};
//export module
export default (state = initialState, actions) =>{
switch(actions.type){
case DISCOVER_MOVIE :
return {
...state,
movies : actions.payload
};
default :
return state;
}
}
this is the log that I get from the console
as you can see if I log the entire object I see all data, however if go deep and try to access the result I either get an undefined or an empty array and using redux-dev-tools I noticed that the state does not contain any value.
I read on internet including this portal similar issue but could not find any solution for my issue.
Solution
From official docs:
You may use a dedicated status field in your actions
Basically you need to dispatch action for each state to make an async action to work properly.
const searchQuery = () => {
return dispatch => {
dispatch({
type : 'START',
})
//make a get request to get the movies
axios.get(url)
.then((res) => {
dispatch({type : 'PASS', payload : res.data});
})
.catch((err) => {
dispatch({type : 'FAILED', payload : res.error});
});
}
With redux-thunk it's pretty simple to set up. You just have to make some changes to your store. Out the box, I'm pretty sure redux isn't the most friendly with async and that's why thunk is there.
import { ..., applyMiddleware } from "redux";
import thunk from "redux-thunk";
...
const store = createStore(reducer, applyMiddleware(thunk));
...
Then in your action you'll need to return dispatch which will handle your logic for your axios call.
const fetchMovie = () => {
return dispatch => {
const url = //Your url string here;
axios.get(url).then(res => {
dispatch(discoverMovie(res.data.results, res.status);
}).catch(err => {
//handle error if you want
});
};
};
export const discoverMovie = (results, status) => {
return {
type: DISCOVER_MOVIE,
payload: results,
status: status
};
};
Your reducer looks fine, though with the way my code is typed you'll have status separately. You can combine them into it's own object before returning in discoverMovie, if you need status with the results.
This is my first answer on stack so let me know if I can clarify anything better!

Not able to access response object via this.props | React/ Redux

When I try to access to the response object in my component it doesnt throw me an error but it wont either print. I do get access to the response in the component but thats it, i cant actually print something.
Actions File
import axios from 'axios';
import { FETCH_USERS, FETCH_USER } from './types';
const BASE_URL = "http://API_URL/endpoint/"
export function fetchUsers(id,first_name, last_name, dob) {
const request = axios.post(`${BASE_URL}member-search?patientId=${id}&firstName=${first_name}&lastName=${last_name}&dateOfBirth=${dob}&length=10`).then(response => { return response; })
return {
type: FETCH_USERS,
payload: request
};
}
export function fetchUser(id) {
const request = axios.get(`${BASE_URL}members/${id}/summary/demographics`).then(response => { return response; })
return{
type: FETCH_USER,
payload: request
};
}
My reducer file
import _ from 'lodash';
import {
FETCH_USERS, FETCH_USER
} from '../actions/types';
export default function(state = [], action) {
switch (action.type) {
case FETCH_USER:
return { ...state, [action.payload.data.member.id]: action.payload.data.member };
// return [ action.payload.data.member, ...state ];
case FETCH_USERS:
return _.mapKeys(action.payload.data.searchResults, 'id');
}
return state;
}
And finally my component where Im trying to render some results of the response.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { fetchUser } from '../actions';
class PatientWrapper extends Component{
componentDidMount() {
const { id } = this.props.match.params;
this.props.fetchUser(id);
}
render(){
const { user } = this.props;
console.log('this.props response: ',user);
if(!user){
return <div>loading...</div>;
}
return(
<div>
Name: {user.firstName}
Last Name: {user.lastName}
</div>
)
}
}
function mapStateToProps({ users }, ownProps) {
// return { users };
return { user: users[ownProps.match.params.id] };
}
export default connect (mapStateToProps, { fetchUser })(PatientWrapper);
I uploaded a Screenshot img of the response : http://prntscr.com/fbs531
What is wrong with my code?
The issue is that in fetchUser action you use a Promise and return it in payload field. This promise does not contain any information you need like response data. So to fix the issue you need to dispatch action only when response is retrieved (e.g. in then success callback).
To implement it you need to pass mapDispatchToProps in the second argument in connect function for your component and pass dispatch function to your action:
function mapDispatchToProps(dispatch) {
return {
fetchUser: id => fetchUser(id, dispatch)
}
}
Then in the action just do the following
function fetchUser(id, dispatch) {
const request = axios.get(`${BASE_URL}/${id}`)
.then(response => dispatch({
type:FETCH_USER,
payload: response
}));
}
For complete example see JSFiddle

react redux-thunk component doesn't render this.props

I'm new to react and I'm trying to understand on to make async ajax request. I was able to get the request completed and the data returned added to my state, but I can't render the data to my component. Here's my setup.
UserPage component
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { getUsers } from '../../actions';
class User extends Component {
displayName: 'User';
componentWillMount() {
this.props.getUsers();
}
renderUsers() {
return this.props.users.map(user => {
return (
<h5>{user.Name}</h5>
);
});
}
render() {
var component;
if (this.props.users) {
component = this.renderUsers()
} else {
component = <h3>asdf</h3>;
}
return (
<div>
{component}
</div>
);
};
};
function mapStateToProps(state) {
return {
users: state.all
};
}
export default connect(mapStateToProps, { getUsers })(User);
Action
import request from '../helpers/request';
import { GET_USERS } from './types';
export function getUsers() {
return request.get(GET_USERS, 'Person/GetPeople');
}
With the get function from request.js module
get: function (action, url) {
return (dispatch) => {
axios.get(`${ROOT_URL}${url}`)
.then(({ data }) => {
dispatch({
type: action,
payload: data
});
});
};
}
User_reducer
import { GET_USERS } from '../actions/types';
export default function (state = {}, action) {
switch (action.type) {
case GET_USERS:
console.log(action);
return { ...state, all: action.payload }
default:
return state;
};
}
When I load the UserPage component, I can see the request being done and the state being updated in the redux dev tool, but I can't display this.props.users.
If I take out the if(this.props.users) in the render() method of the User component, I get an undefined on this.props.users.
What am I doing wrong?
Any help greatly appreciated! Thank you
SOLVED
The solution was to set the users property to state.users.all in the mapStateToProps function.
function mapStateToProps(state) {
return {
users: state.users.all
};
}
Thank you
Could you check, do you have this.renderUsers() defined in that render function? It might be you forgot to bind it, and that function is undefined?
Try to add this.renderUsers = this.renderUsers.bind(this); in your constructor and see, if it helps.

Categories

Resources