Redux/React: why `this.props` undefined - reactjs

My codes:
render() {
console.log( 'render, state' + Array.isArray(this.props.users) + ',' + JSON.stringify(this.props.users) );
return (
<section>
<div>testing</div>
<div>{JSON.stringify(this.props.users)}</div>
<ul>
{this.props.users.map( user => <li>{user.email}</li>)}
</ul>
</section>
);
}
Both <div>testing</div> and <div>{JSON.stringify(this.props.users)}</div> work fine (after removing <ul>...</ul>). But <ul>...</ul> did not work. the error is:
Uncaught TypeError: Cannot read property 'map' of undefined
Any comments welcomed. Thanks
UPDATE
The following codes work fine, this.props.users is an Array, and all the console logs look OK. I just need to know why <ul>{this.props.users.map( user => <li>{user.email}</li>)}</ul> not work. Why this.props.users in <ul>...</ul> is undefined.
render() {
console.log( 'render, state' + Array.isArray(this.props.users) + ',' + JSON.stringify(this.props.users) );
return (
<section>
<div>testing</div>
<div>{JSON.stringify(this.props.users)}</div>
</section>
);
}
the console output of the above codes (only two s):
render, statetrue,[{"_id":"5831e6df511e","updatedAt":"removed","createdAt":"removed","email":"removed","password":"xxxx","__v":0,"role":"xxx","profile":{"firstName":"test","lastName":"tester"}},{...}, {...}]
UPDATE
My codes:
import { connect } from 'react-redux';
import { fetchAllUsers } from '../../actions/index';
class HomePage extends Component {
componentWillMount() {
console.log( 'componentWillMount()' );
this.props.fetchAllUsers();
}
render() {
console.log( 'render(), ' + Array.isArray(this.props.users) + ',' + JSON.stringify(this.props.users) );
return (
<section>
<div>{JSON.stringify(this.props.users)}</div>
</section>
);
}
}
function mapStateToProps(state) {
console.log( 'state:' + JSON.stringify(state) );// working fine, and the console.log is OK. not added to this post
return {
users: state.admin.users
}
}
export default connect(mapStateToProps, {fetchAllUsers})(HomePage);
console.log (some details in the User objects removed.):
render(), true,[{"_id":"5831","profile":{"firstName":"test","lastName":"tester"}},{"_id":"5831e874cedf511f", "profile":{"firstName":"cccc","lastName":"cc"}},{"_id":"5831120","profile":{"firstName":"cccccccc","lastName":"ccc"}}]
And, on the webpage, the string of this.props.users is shown.
My question is why <ul>{this.props.users.map( user => <li>{user.email}</li>)}</ul> not work, but <div>{JSON.stringify(this.props.users)}</div> working fine.
I think I already described my questions clearly. If more information needed, please tell me.
More details
my admin_reducer.js
import { FETCH_ALL_USERS } from '../actions/types';
const INITIAL_STATE = { users:[] };
export default function (state = INITIAL_STATE, action) {
console.log( 'users is action.payload:'+JSON.stringify( { users:action.payload } ) );
return Object.assign( {}, state, {users:action.payload});
//return { ...state, users:action.payload };
}
my index.js
import adminReducer from './admin_reducer';
const rootReducer = combineReducers({
...
admin: adminReducer
});
export default rootReducer;
my action.js
export function fetchAllUsers() {
return function(dispatch) {
axios.get(`${API_URL}/user/all`)
.then(response => {
dispatch({
type: FETCH_ALL_USERS,
payload: response.data
});
})
.catch(response => dispatch(errorHandler(response.data.error)))
}
}
UPDATE
why console log of console.log(this.props.users) is [object Object],[object Object],[object Object]?

First of, according to react docs, async requests should be made inside the componentDidMount method.
Secondly, to make sure that you're not trying to map an un-initialized array, use this:
<ul>{(this.props.users || []).map( user => <li>{user.someProperty}</li> )}</ul>
This should cover it in case you forgot to initialize the array for some reason.

You can call this without error..
JSON.stringify(this.props.users)
...because it just outputs "undefined"). That is valid. However, if you try to call a method on the undefined that was returned, of course that fails...
// Following tries to call undefined.map(), which is of course wrong
this.props.users.map( user => <li>{user.email}</li>)
Common shorthand to address your scenario uses a "short-circuit operator" (like a quickie "if (is true)")...
<ul>{this.props.user && this.props.users.map( user => <li>{user.email}</li>)}</ul>
^^ Note the "this.props.user && ", which prevents the following this.props.user.map() from being called if this.props.user is null or undefined

Related

Cannot read propery of .includes undefined - react

I'm trying to map this array but the it keeps saying .includes is undefined. I assume maybe its because I've mapped is wrong? I'm not sure but I will list everything I have.
Here is what my api data looks like when I console.log(this.props.tournaments it from from my redux store:
console.log
You see the undefined in the first index of the array, I've used an Object.keys(tournaments).map(key => tournaments[key]) and .map (which you can see in the component snippet below) twice which when I console.log I get the correct results but I still get an error when its passes through the .filter function.
Here is what it looks like after I've formatted the array:
console.log after formatting
but I'm still getting an error...
This the error I'm getting:
Error message
Here is the component in question:
import React from 'react';
import { connect } from 'react-redux';
import { fetchTournaments } from '../actions/tournaments';
class Search extends React.PureComponent {
// State only needs to hold the current filter text value:
componentDidMount() {
this.props.fetchTournaments();
}
state = {
filterText: ''
};
handleChange = event => {
this.setState({ filterText: event.target.value });
};
render() {
let tourns = this.props.tournaments.map(tore =>
tore.map(room => room.name)
);
console.log(tourns, 'here');
const filteredList = tourns.filter(item =>
item.name.includes(this.state.filterText)
);
return (
<React.Fragment>
<input onChange={this.handleChange} value={this.state.filterText} />
<ul>{filteredList.map(item => <li key={item.id}>{item.name}</li>)}</ul>
</React.Fragment>
);
}
}
function mapStateToProps({ tournaments }) {
return {
tournaments: Object.keys(tournaments).map(key => tournaments[key])
};
}
export default connect(mapStateToProps, {
fetchTournaments
})(Search);
My data is coming from redux like so:
Reducer:
import _ from 'lodash';
import {
FETCH_TOURNAMENT,
from '../actions/types';
export default (state = {}, action) => {
switch (action.type) {
case FETCH_TOURNAMENT:
return { ...state, [action.payload.id]: action.payload };
default:
return state;
}
};
action:
import {
FETCH_TOURNAMENT,
} from './types';
import { API_TOURNAMENTS_URL } from '../constants/api';
import axios from 'axios';
export const fetchTournament = id => async dispatch => {
const response = await axios.get(`http://localhost:4000/tournaments/${id}`);
dispatch({ type: FETCH_TOURNAMENT, payload: response.data });
};
I think you could treat you response.data before setting to state to reflect more how you will be handling your application. I would imagine that you would rather have an array of objects.
you could try passing to payload Object.values(response.data).flat() instead which would give you an array of of objects based on your response.
Edit
below is without implementing Object.values(response.data).flat(). if you follow the suggestion you shouldnt need to treat
about the issue at filter, item is an array containing name value at index 0. this is why you get undefined. this happens because of tore.map(room => room.name) returns an array.
if you change as below you may get your array of tourns:
let tourns = this.props.tournaments.flat();
but I would consider to treat your response which would avoid all these changes on component level. pick one which suits you better.

API Call Returns Data But Not Render

My console.log shows axios call returns data [object object] but it shows undefined when I try to render the data. Any ideas?
```
class CourseDetail extends Component {
state={
ID: this.props.match.params.ID,
course:[]};
componentDidMount(){
this.runSearch();
}
runSearch=async()=>{
const response= await axios.get('API\?{this.props.match.params.ID}')
this.setState({course: response.data});
//console.log shows course=[object object]
console.log("course="+response.data);
}
render(){
//course is undefined below
const course= this.state.course.map(item=> <div>(item.SUBJECT)</div>)
return (
<div>
{course}
</div>
); }
};
export default CourseDetail;
As #HolyMoly mentioned in the comments, you may need to stringify the response as JSON as well before logging it depending on what the API is returning. There's also a syntax error in your map function, you are using parentheses instead of curly braces to render the value. Depending on the structure of your data response, something like this may work:
class CourseDetail extends Component {
state={
ID: this.props.match.params.ID,
course:[]};
componentDidMount() {
axios.get('API\?{this.props.match.params.ID}')
.then(res => res.json())
.then(res => {
console.log("course="+res.data);
this.setState({course: res.data})
})
}
render(){
//course is undefined below
const course= this.state.course.map(item=> <div>{item.SUBJECT}</div>)
return (
<div>
{course}
</div>
); }
};
export default CourseDetail;
This depends on whether or not the data you are mapping is an object or an array of course. If it is in fact an object, you can map over the keys instead:
const course= Object.keys(this.state.course).map(item=> <div>{item.SUBJECT}</div>)

props value come as undefined but print on browser console

I have this strange issue, not sure what I am doing wrong. I pass a flags object to this components and it is available when I log it on console but it doesn't enter the loop observe logging Object.keys(flags) is undefined. Even when trying to access the value of an object directly, it is undefined.
class Home extends React.Component {
render() {
const allflags = [];
console.log('in Home component flags.. ', this.props.flags);
const {flags} = this.props;
console.log('Object.keys ', Object.keys(flags));
Object.keys(flags).forEach(key => {
console.log('key => ', key);
console.log('value => ', ''+ flags[key]);
allflags.push(<div>{key} : {''+ flags[key]}</div>);
});
console.log('Home Props flags', flags);
console.log('displayWallet >>>>>>>>> ', ''+flags['displayWallet']);
return (
<div>
<h1> All flags</h1>
{allflags}
</div>
);
}
}
Actual Result:
App.js:8 in Home component flags.. {}displayLogonStatus:true displayWallet: true __proto__: Object
App.js:10 Object.keys []length: 0__proto__: Array(0)
App.js:16 Home Props flags {}displayLogonStatus: false displayWallet: true __proto__: Object
App.js:17 displayWallet >>>>>>>>> undefined
Expected Result:
App.js:10 Object.keys []length: 0__proto__: Array(0) - Shouldn't be empty
App.js:17 displayWallet >>>>>>>>> undefined - Should be undefined when it is printed just above.
I think the problem you are having is how you are passing the props to the component.
I can suggest you to debug
console.log(this.props);
and check what is going on inside.
Also, I paste a working example of your code.
class Home extends React.Component {
render() {
const { flags } = this.props;
const allflags = [];
Object.keys(flags).forEach(key => {
allflags.push(<div>{key} : {''+ flags[key]}</div>);
});
return (
<div>
<h1>All flags</h1>
{allflags}
</div>
);
}
}
class App extends Component {
render() {
return (
<Home flags={{ displayLogonStatus: true, displayWallet: true }} />
);
}
}
I hope this helps you

Why can't I display array data in React Application?

I am quite new to development with React and I am currently trying to get my head around some basic react and redux things. Unfortunately I am experiencing an issue which I cannot fix on my own.
I have written a mock-api which returns players (profileUrl, username, realname, id). I am dispatching an action which successfully gets me one of those players and I can also pass it to my components props using redux' mapStateToPropsfunction. But I cannot render any of that data in my render function. The react devtools even show me that the single player is getting returned as an array.
The component:
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as playerActions from '../../actions/playerActions';
class SinglePlayer extends React.Component {
componentDidMount() {
this.props.actions.loadPlayer(this.props.match.params.playerid);
}
/**
* Render the component.
*/
render() {
return (
<div>
{ this.props.currentPlayer.username }
</div>
)
}
}
/**
* Defines the state which is exposed to this component.
*
* #param { object } reduxStore The entire redux store.
* #param { object } ownProps The properties which belong to the component.
*/
const mapStateToProps = (reduxStore, ownProps) => {
return {
currentPlayer: reduxStore.playerReducer.currentPlayer
}
}
/**
* Defines which actions are exposed to this component.
*
* #param { function } dispatch This function is used to dispatch actions.
*/
const mapDispatchToProps = (dispatch) => {
return {
actions: bindActionCreators(playerActions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(SinglePlayer);
React DevTools:
Screenshot of React Devtools props
Redux DevTools:
Screenshot of Redux Devtools data
As you can tell from the image above, the currentPlayer props is inside the playerReducer object.
I have also tried looping over the array like so, with no success either. I just get the error-message stating that .map() is not a function.
this.props.currentPlayer.map(function(player, index) {
return <p>{ player.username }</p>
)}
Error when using .map():
TypeError: this.props.currentPlayer.map is not a function
Can someone tell me what I am doing wrong?
You set your current player by params id at componentDidMount . Your render takes place before that currentPlayer is set hence the error. Add a recheck in your render like below.
render() {
return (
<div>
{
this.props.currentPlayer &&
this.props.currentPlayer.map(function(player, index) {
return <p>{ player.username }</>
)}
}
</div>
)
}
Or
render() {
return (
<div>
{
this.props.currentPlayer ?
this.props.currentPlayer.map(function(player, index) {
return <p>{ player.username }</>
)}
:
null
}
</div>
)
}
Either way it should work. That way this.props.currentPlayer will not be rendered or accessed until its available.
Update
Udate your mapStateToProps to
const mapStateToProps = (state) => {
return {
currentPlayer: state.currentPlayer
}
}
I think from your reduxDev tool, currentPlayer is not under any object.
in first render this.props.currentPlayer is empty!
set empty array "currentPlayer" in state and insert insert this.props.currentPlayer in this.state.currentPlayer and render from state
I managed to solve the issue myself now. The posts here kinda inspired me to try some new things. It was my mock-api which returned the data in a strange and unexpected (at least for me it was unexpected) way.
The dataset:
const players = [
{
profileUrl: 'https://profile.url',
username: 'player1',
realname: 'Max Muster',
steamId: 'player1'
},
{
profileUrl: 'https://profile.url',
username: 'player2',
realname: 'Max Mustermann',
steamId: 'player2'
},
{
profileUrl: 'https://profile.url',
username: 'player3',
realname: 'Maxime Musterfrau',
steamId: 'player3'
},
];
The filtermethod used:
var player = players.filter(function(el) {
return el.steamId === 'player1';
});
If you assume an array of multiple players like above, the shown filtermethod extracts the correct object but still keeps it wrapped in an array. That was producing the mistake...
Thanks a lot for the help guys!

Component props will not update after state if redux state changes

So I have read through numerous post which seem to cover the same topic as me, but after trying all their solutions, I simply cant make it work, so here it goes.
I have a simple search filter, that I use to filter some recipes by their name. I have a Redux Store to handle the state(value) of the search filter. When entering values in the input field, my actions goes fine to the reducer, which updates the state (I can see that with both console.logs and Redux DevTools). But somehow, this doesn't update my props, which are mapped with connect and mapStateToProps
All previous answers I have come across, points to mutating state, but I have tried to go over multiple iterations of my reducer, but then again, I might just be overlooking something.
My filter action dispatch:
filter(e){
Store.dispatch({
type: 'SET_NAME_FILTER',
payload: e.target.value
})
}
My reducer:
const filterNameReducer = (state = [""], action) => {
if (action.type === 'SET_NAME_FILTER') {
return [
action.payload
];
}
return state;
}
My RecipeListContainer:
class RecipeListContainer extends React.Component {
render() {
return (
<RecipeList recipes={ this.props.recipes } filterName={ this.props.filterName }/>
)
}
}
const mapStateToProps = (state) => {
return {
filterName: state.filterNameState,
}
}
module.exports = connect(mapStateToProps)(RecipeListContainer)
My RecipeList component:
class RecipeList extends React.Component {
render() {
return (
<ul>
{this.props.recipes.filter(this.filters, this).map(({ node: recipe }) => {
return (
<RecipeListItem key={ recipe.id } id={ recipe.id } value={ 0 } name={ recipe.name } time={ recipe.time } type={ recipe.type } tags={ recipe.tags } ingredients={ recipe.ingredients } />
);
})}
</ul>
)
}
filters() {
if (this.props.filterName[0].length != 0) {
return true;
} else {
return false;
}
}
}
I know that my filter doesn't do that much yet, but I've made it simple, so that i just could see if it gets updated. And it filter correct, if i change the initial value of my reducer. So shouldn't be that.
Also, Im using it in a GatsbyJS, but according to their documentation and examples, this should not be a problem. But i could be wrong?
Lots of thanks in advance! If I have missed something for you to find a solution, let me know, Im new to posting on SO.

Resources