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

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!

Related

in React Accessing a state via this.props returns function instead of state object

Scenario: I have two components, <Homepage> [self explanatory name] and <Country> [which shows a list of country accessed through an API].
<Homepage> imports <Country /> [Country is the child]
I dispatch an action to get list of country in . everything works fine, state gets updated as required. Also I'm using mapStateToProps and mapDispatchToProps. Below is the code for <Country />
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { userActions } from './../../_actions';
class Country extends Component {
constructor(props) {
super(props);
let packet = {};
this.props.country(packet);
}
render() {
const { country } = this.props;
console.log("1 ", country)
// when i access it here, instead of the state object i get a function
//ƒ () {
// return dispatch(actionCreator.apply(undefined, arguments));
//}
return(
<div className="container">
</div>
)
}
}
function mapStateToProps(state) {
const { country } = state; // country gets updated with latest value
return {
country
};
}
const mapDispatchToProps = {
country: userActions.country
}
const connectedCountryPage = connect(mapStateToProps, mapDispatchToProps)
(Country);
export { connectedCountryPage as Country };
AND if I change the code to this
class Country extends Component {
constructor(props) {
super(props);
let packet = {};
const { dispatch } = this.props;
dispatch(userActions.country(packet));
}
render() {
const { country } = this.props;
console.log("1 ", country);
// I get the valid state object here
return(
<div className="container">
</div>
)
}
}
function mapStateToProps(state) {
const { country } = state;
return {
country
};
}
const connectedCountryPage = connect(mapStateToProps)(Country);
export { connectedCountryPage as Country };
BUT When in SIMILAR manner I subscribe to the state in <Homepage /> and access the state object in render(), I get the valid the state object.
WHY do I get two different treatments in the two components at the same time for same state object?
PS: If you somehow get a question with an answer to this, post the link. I have searched for hours and not able to get an answer.
All I want is an explanation
PS: Do READ the comments in the code
You're using the same name country for both the dispatch function as well as state. It's easily visible, just think about what this.props.country is right now ? It could be the function from mapDispatchToProps or the state variable from mapStateToProps.
I suggest you rename the dispatch function to something like getCountry:
// .....
// your component code
// .....
const mapStateToProps = (state) => ({
country: state.country
});
// changed name
const mapDispatchToProps = {
getCountry: userActions.country
};
const connectedCountryPage = connect(mapStateToProps, mapDispatchToProps)(Country);
As an aside, you may avoid the complex code you have in the last 2 lines by simply not using an extra variable:
export connect(mapStateToProps, mapDispatchToProps)(Country); // you may or may not add 'default'

Know what is causing “Warning: setState(…): Cannot update during an existing state transition…”, but not sure where the cause should be moved to

I am running into this error. Numerous SO questions on it, but still having difficulty figuring out where I need to move the call to the action to get around the error.
Warning: setState(...): Cannot update during an existing state transition (such as within render or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to componentWillMount.
I have two sibling containers that I am trying to share a Redux store between: update the store in one via an action so that I can use it another one. One container is a search/filter <BasicQuerySearch />box and the other container is a list of results <BasicQueryPositionList />.
The results are narrowed down as the user types. As the field is reduced a <div> below the search box states how many current results there are. That number is determined in the list of results container where it should be passed through the action, update the store, so I can call it in the search box container. The update is happening, but not without this error polluting the browser console.
I know what the cause of the error is: updating the store in render() with:
getCount(n) {
this.props.posCount(n.length);
}
It is just unclear to me where to call it outside of the render() that will permit updating after the container has mounted. Thus, componentDidMount()) wouldn't work because it won't update after the container has mounted and the user has started typing.
Here is the parent container and the two siblings:
// parent container
// ./query.js
import _ from 'lodash';
import React, { Component } from 'react';
import { connect } from 'react-redux';
// imports for the basic query when selected
import BasicQueryPositionList from './bq_position_list';
import BasicQuerySearch from './bq_search';
// imports for the advanced query when selected
// .... these are to come
import { getQuery, clearQuery } from '../../actions/results';
class Query extends Component {
componentWillUnmount() {
this.props.clearQuery();
}
// render the main element of the container
render() {
return (
<div className='panel panel-default'>
<div className='panel-heading'>
<h4><strong>Compensation Data Results Query</strong></h4>
</div>
<div className='panel-body'>
{!this.props.pos_list
?
(
<div>Loading...</div>
)
:
this.props.pos_list.length == 1
?
(
<h5>The query is currently not available for this survey.</h5>
)
:
(
<div>
<BasicQuerySearch />
<hr />
<BasicQueryPositionList />
</div>
)
}
</div>
</div>
);
}
}
// map the state in the Redux store to props
function mapStateToProps(state) {
return {
pos_list: state.results.pos_list,
}
}
export default connect (mapStateToProps, { clearQuery })(Query);
// sibling #1 container
// ./bq_search.js
import _ from 'lodash';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { searchTerm } from '../../actions/results';
class BasicQuerySearch extends Component {
// Declare the state "term"
constructor(props) {
super(props);
this.state = {
term: '',
};
}
// When someone enters into the search box, it narrows down
// the list of positions; also filters out speciality chars
onInputChange(term) {
var cleanString = term.replace(/([`~!##$%^&*()_|+\-=?;:''",.<>\{\}\[\]\\\/]+)/g, "");
term = cleanString.toLowerCase();
if (!term) {
term = ''
}
this.setState({ term });
this.props.searchTerm(term);
}
// renders the search box that filters down the list of positions
render() {
const { pos_count } = this.props;
console.log(pos_count);
return (
<div>
<input
className='form-control'
placeholder='Enter Keyword or Position Code'
value={this.state.term}
onInput={event => this.onInputChange(event.target.value)}
/>
<h5><b>{pos_count}</b> {pos_count == 1 ? 'Position' : 'Positions'} Found</h5>
</div>
)
}
}
function mapStateToProps(state) {
return {
pos_count: state.results.pos_count,
}
}
export default connect (mapStateToProps, { searchTerm })(BasicQuerySearch);
// sibling #2 container
// ./bq_position_list.js
import _ from 'lodash';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { clearQuery, getQuery, posCount } from '../../actions/results';
class BasicQueryPostionList extends Component {
// renders the options and also filters down the results
renderPositionsOptions(pos) {
// generate the <option> list
return _.map(pos, p => {
var option = p.FinalCode + ' - ' + p.PosTitle
return (
<option
key={p.FinalCode}
value={p.FinalCode}
>
{option}
</option>
);
})
}
// filter down the list of positions
filterPositions(term, pos) {
const filtered_items = _.filter(pos, p =>
(p.FinalCode + ' - ' + p.PosTitle).toLowerCase().match(term)
)
return filtered_items;
}
// count how many positions are in the list
getCount(n) {
this.props.posCount(n.length);
}
// renders the search box that filters down the list of positions
render() {
const { search_term, pos_list } = this.props;
const filtered_positions = this.filterPositions(search_term, pos_list);
this.getCount(filtered_positions);
return (
<div>
<h4>Position:</h4>
<select className='form-control'>
<option></option>
{this.renderPositionsOptions(filtered_positions)}
</select>
</div>
);
}
}
// map the state in the Redux store to props
function mapStateToProps(state) {
return {
pos_list: state.results.pos_list,
survey_id: state.results.survey_id,
search_term: state.results.search_term
}
}
export default connect (mapStateToProps, { clearQuery, getQuery, posCount })(BasicQueryPostionList);
There is a more fundamental problem here with the structuring of this code. You are in a way duplicating state. The state representing the number of filtered results is really derived state from the pos_list itself, and you shouldn't be storing state derived from other state back into the state tree.
What I would recommend doing, is moving the filterPositions method out from the one component into a shared location. Then import it in both your BasicQuerySearch and BasicQueryPostitionList components and use it in their mapStateToProps function to get the filtered list as well as the count.
This will mean you are no longer duplicating state and also stop the above warning from happening.
The downside of this is that you will be calling the filterPositions function twice instead of once, but the solution for that is to look at making filterPositions a memoized selector (with something like the reselect library).
why not put your code in componentDidUpdate
...
getCount(n) {
this.props.posCount(n.length);
}
// renders the search box that filters down the list of positions
render() {
const { search_term, pos_list } = this.props;
const filtered_positions = this.filterPositions(search_term, pos_list);
return (
<div>
<h4>Position:</h4>
<select className='form-control'>
<option></option>
{this.renderPositionsOptions(filtered_positions)}
</select>
</div>
);
}
componentDidUpdate(prevProps , prevState){
const { search_term, pos_list } = this.props;
const filtered_positions = this.filterPositions(search_term, pos_list);
this.getCount(filtered_positions);
}
...

Error Retrieving Data from React Redux Store and Mapping to Props

I have a React component which is fetching data via API to retrieve an product Object by using the ID passed in as a prop to the component.
I have a React / Redux app and I am fairly new to Redux flow.
I have the Products (array with one Product object) data loading via my Action / Reducer to the store.
I am trying to pass this from state to props using the mapStateToProps pattern.
I am getting the following error when it is rendering the { this.props.product.title }
Uncaught TypeError: Cannot read property '__reactInternalInstance$z9gkwvwuolc' of null
I think its due to it being data thats asynchronous.
What's the best way to solve for this?
Below is my code --
class ProductListItem extends Component {
componentDidMount() {
this.props.dispatch(fetchProduct(this.props.id));
}
render() {
return (
<div>
<h1>{ this.props.product.title }</h1>
</div>
);
}
}
// Actions required to provide data for this component to render in sever side.
ProductListItem.need = [() => { return fetchProduct(this.props.id); }];
// Retrieve data from store as props
function mapStateToProps(state, props) {
return {
product: state.products.data[0],
};
}
ProductListItem.propTypes = {
id: PropTypes.string.isRequired,
dispatch: PropTypes.func.isRequired,
overlay: PropTypes.string,
};
export default connect(mapStateToProps)(ProductListItem);
You need to check if the product exist, you will access the inner data only if it exist. This a common pattern:
class ProductListItem extends Component {
componentDidMount() {
this.props.dispatch(fetchProduct(this.props.id));
}
render() {
const { product } = this.props;
return (
<div>
{ product &&
<h1>{product.title}</h1>
}
</div>
);
}
}
If the product exist, then the component will render the <h1>.
In your redux reducer you can define the default state, set the default state and then you can do some ternary checking
export default function reducer(state={ title : undefined}, action) {
//ternary checking in render() on component
product.title !== undefined ? product.title : undefined
This means, if product.title isn't undefined, then render product.title else undefined.

Adding Redux to an existing React app

I've been working on a React app and have gotten to a point where I'll need Redux to handle some aspects of it.
After reading a bunch of tutorials, I'm fairly stuck on how to make my "smarter" components "dumber" and move functions into my actions and reducers.
So, for example, one aspect of the app is more of a to-do list style.
One of my classes starts like this:
export default class ItemList extends React.Component {
constructor() {
super();
this.state = { items: [],
completed: [],
};
this.addItem = this.addItem.bind(this);
this.completeItem = this.completeItem.bind(this);
this.deleteItem = this.deleteItem.bind(this);
}
addItem(e) {
var i = this.state.items;
i.push({
text: this._inputElement.value,
paused: false,
key: Date.now()
});
this.setState({ items: i });
e.preventDefault();
this._inputElement.value = '';
this._inputElement.focus();
}
completeItem(e) {
this.deleteItem(e);
var c = this.state.completed;
c.push({
text: e.target.parentNode.parentNode.getElementsByClassName('item-name')[0].innerHTML,
paused: false,
key: Date.now()
});
this.setState({ completed: c });
}
deleteItem(e) {
var i = this.state.items;
var result = i.filter(function(obj) {
return obj.text !== e.target.parentNode.parentNode.getElementsByClassName('item-name')[0].innerHTML;
});
this.setState({ items: result });
}
// ... more irrelevant code here ...
// there's a function called createTasks that renders individual items
render() {
var listItems = this.state.items.map(this.createTasks);
return <div className="item-list">
<form className="form" onSubmit={this.addItem}>
<input ref={(a) => this._inputElement = a}
placeholder="Add new item"
autoFocus />
<button type="submit"></button>
</form>
{listItems}
</div>;
}
}
So, as you can see, it's very logic-heavy. I've started adding Redux by adding a <Provider> in my index file, and made a basic reducers file that is fairly empty so far:
import { combineReducers } from 'redux';
const itemList = (state = {}, action) => {
};
// ... other irrelevant reducers
const rootReducer = combineReducers({
itemList,
// ...
});
export default rootReducer;
...and I've made an actions file that doesn't have much in it yet either.
I've been struggling to figure out:
Most actions I've seen examples of just return some kind of JSON, what do I return in the reducer that uses that JSON that my component can use?
How much of my component logic is reusable, or should I just forget it? What is the best way to go about this to reuse as much code as I've written as possible?
First of all you need to understand the overall picture of how redux works with react.
Before coming to that lets first understand what are smart components and dumb components.
Smart Components
All your code logic needs to be handled here
They are also called containers.
They interact with the store(aka state management) to update your components.
Dumb Components
They just read props from your containers and render you components
This is just the UI view and should not contain any logic.
All styling/html/css comes in your dumb components.
Here is an amazing piece of article which you can go through to understand smart and dumb components if you still have doubts.
Ok, now lets try understanding how redux works:-
Your smart components(aka containers) interact with your redux store
You fire actions from your containers.
Your actions call your apis
The result of your action updates the store through a reducer
You containers read the store through mapStateToProps function and as soon as value in store changes it updates your component.
Now lets consider your todo example
TodoListContainer.js
class TodoListContainer extends Component {
componentWillMount () {
// fire you action action
}
render () {
return (
<Todos todos=={this.props.todos} />
)
}
}
function mapStateToProps(state) {
const {todos} = state;
return {
todos;
}
}
export default connect(mapStateToProps)(TodoListContainer)
TodoList.js
class TodoList extends Component {
renderTodos() {
return this.props.todos.map((todo)=>{
return <Todo todo={todo} key={todo.id} />
})
}
render () {
return () {
if (this.props.todos.length === 0) {
return <div>No todos</div>
}
return (
<div>
{this.renderTodos()}
</div>
)
}
}
}
export default class TodoList
Todo.js
class Todo extends Component {
render () {
return (
<div>
<span>{this.props.todo.id}</span>
<span>{this.props.todo.name}</span>
</div>
)
}
}
Reducer
export default function todos(state={},action) {
switch (action.type) {
case 'RECEIVE_TODOS':
return Object.assign(state,action.todos);
}
}
action
function fetchTodos() {
return(dispatch) => {
axios.get({
//api details
})
.then((res)=>{
dispatch(receiveTodos(res.todos))
})
.catch((err)=>{
console.warn(err)
})
}
}
function receiveTodos(todos) {
return {
type: 'RECEIVE_TODOS',
todos
}
}
Now if you have read redux documentation you would see that actions return objects then how would i call my api there which returns a function instead of an object. For that I used redux thunk about which you can read here.
I gave you an example in which you can fetch todos. If you want to do other operations like deleteTodo, addTodo, modifyTodo then you can do that in appropriate components.
DeleteTodo - you can do in TodoListContainer.
AddingTodo - you can do in TodoListContainer.
Changing State(completed/Pending) - you can do in TodoListContainer.
ModifyingTodo - you can do in TodoContainer.
You can also check out here for a detailed example, but before that I would say just should go through basics of redux which you can find here
P.S: I wrote code on the fly so it might not work properly but it should work with little modification.

How to map encapsulated ui state in a redux store to props with react-redux?

I'm having trouble figuring out how to compose ui widget reducers and react render trees in tandem, in a way that I can later map the resulting redux store for the reducers to props in the render tree using react-redux.
Suppose a setup like this. I'm trying to make a NameWidget that I can use anywhere in any app:
NameView.jsx:
...
render() {
return <div> Name: {this.props.name} </div>;
}
...
====
NameAction.js:
...
export const CHANGE_NAME = 'CHANGE_NAME';
...
export function changeNameAction(newName) {
return {
type: CHANGE_NAME,
payload: newName,
};
}
====
NameReducer.js:
import { CHANGE_NAME } from './NameAction';
function ui(state = {name: ''}, action) {
switch(action.type) {
case(CHANGE_NAME):
return Object.assign({}, state, {name: action.payload});
break;
default:
return state;
}
}
export default ui;
====
NameWidget.js:
import { connect } from 'react-redux';
import { NameView } from './NameView';
const mapStateToProps = (state) => {
return {
name: state.name,
};
};
const NameWidget = connect(
mapStateToProps
)(NameView);
export default NameWidget;
If I use NameWidget directly I'll have no problem:
NameApp.jsx:
import { createStore } from 'redux';
import app from './NameReducers';
let store = createStore(app);
...
function render() {
return (
<Provider store={store}>
<NameWidget/>
</Provider>
);
}
However, I don't see how to make this modular. I can't actually put this Widget into anything else. Suppose I wanted to do this:
EncapsulatingView.jsx:
...
render() {
return (
<div>
<NameWidget/>
<SomeOtherReduxWidget/>
</div>
);
}
...
====
EncapsulatingReducer.js:
import { combineReducers } from 'redux'
import nameWidget from '../name/NameReducer';
import someOtherWidget from '../someOther/SomeOtherReduxWidgetReducer';
export default combineReducers({nameWidget, someOtherWidget});
(I expect that the remaining code including connect() and createStore() for Encapsulating* is obvious.)
This almost works, as long as all the actions across all encapsulated views have unique types. But the problem is NameWidget's mapStateToProps; it needs to change from above to use a new path to its section of the store:
...
const mapStateToProps = (state) => {
return {
name: state.nameWidget.name,
};
};
...
And so on - depending on how ancestors combineReducers() to define their ever-grander super widgets and apps, the descendants somehow have to change how they mapStateToProps() to compensate. This breaks encapsulation and reusability of code, and I don't see how to get around it in react-redux. How can I define widgets by composing combineReducers(), and then map the resulting store to those widgets' props, in a way that's write-once-use-anywhere?
(Apropos of nothing, it seems strange that repeated combineReducers() creates a shape that's bottom up, but that mapStateToProps() seems to assume instead a top-down "absolute path" approach to looking up into that shape.)
Interesting question. I wonder if it would serve your purpose to require a guid be generated by the parent and passed down through props, and then you simply use that as the index of your state object for that instance of your widget. So whenever you are about to create a new instance of this widget you need to create an ID for it in the parent, and then pass that down in the props, then it will be available to the connect method to use in both mapStateToProps and mapDispatchToProps. I suppose in theory you could even create this parent component yourself and it could possibly be transparent when it is being used, and simply use componentDidMount to generate a new guid to store in component state and pass down to the child.
I gave John the credit for this question because he essentially provided the right magic for the answer, passing store "routing" logic via mapStateToProps()'s ownProps function arg. But I preferred my own spin on it. Essentially, whenever you combineReducers() in an EncapsulatingReducer.js, you have to remember to pass a uiStorePath prop in EncapsulatingView.js:
New NameWidget.js:
const _ = require('lodash');
import { connect } from 'react-redux';
import { NameView } from './NameView';
const mapStateToProps = (state, props) => {
const uiStore = (
(ownProps && _.has(ownProps, 'uiStorePath') && ownProps.uiStorePath &&
ownProps.uiStorePath.length) ?
_.get(state, ownProps.uiStorePath) : // deep get...old versions of lodash would _.deepGet()
state
);
return {
name: uiStore.name,
};
};
const NameWidget = connect(
mapStateToProps
)(NameView);
export default NameWidget;
====
EncapsulatingView.js:
...
render() {
const uiStorePathBase = ((this.props.uiStorePath &&
this.props.uiStorePath.length) ?
this.props.uiStorePath + '.' :
''
);
return (
<div>
<NameWidget uiStorePath={uiStorePathBase+"nameWidget"}/>
<SomeOtherWidget uiStorePath={uiStorePathBase+"someOtherWidget"/>
</div>
);
}
...

Resources