React-redux - Cannot read property 'map' of undefined - reactjs

I'm learning React-Redux and have come to an issue when trying to render out a side navigation "sub section" item from a nested object.
Here is the component:
import React, {Component} from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
class SideNav extends Component {
render() {
return (
<div className="sidenav-cont">
<ul className="top-level-menu">
{
this.props.reducerSidenav.map((menuItem) => {
return (
<li key={menuItem.catId}>
{menuItem.parentCat}
<ul className="bottom-level-menu">
{
this.props.reducerSidenav.subCats.map((subMenuItem) => {
<li key={subMenuItem.subCatId}>{subMenuItem.subCatLabel}</li>
})
}
</ul>
</li>
)
})
}
</ul>
</div>
)
}
}
function mapStateToProps(state) {
return {
reducerSidenav:state.reducerSidenav
};
}
export default connect(mapStateToProps)(SideNav);
if I remove the second nested <ul> with the className "bottom-level-menu" then the app renders out my "parent section" list item no problem. But it's when I attempt to re-create the same function but for a nested objected that I get errors. Here is my reducer with the sidenav menu object:
export default function(){
return [
{
catId:"parMenu1",
parentCat:"Bass",
subCats:[
{
subCatId:"subMenu1",
subCatLabel:"base1"
},
{
subCatId:"subMenu2",
subCatLabel:"base2"
},
{
subCatId:"subMenu3",
subCatLabel:"base3"
},
{
subCatId:"subMenu4",
subCatLabel:"base4"
}
]
},
{
catId:"parMenu2",
parentCat:"treb",
subCats:[
{
subCatId:"subMenu1",
subCatLabel:"treb1"
},
{
subCatId:"subMenu2",
subCatLabel:"treb2"
},
{
subCatId:"subMenu3",
subCatLabel:"treb3"
},
{
subCatId:"subMenu4",
subCatLabel:"treb4"
}
]
},
{
catId:"parMenu3",
parentCat:"drums",
subCats:[
{
subCatId:"subMenu1",
subCatLabel:"drums1"
},
{
subCatId:"subMenu2",
subCatLabel:"drums2"
},
{
subCatId:"subMenu3",
subCatLabel:"drums3"
},
{
subCatId:"subMenu4",
subCatLabel:"drums4"
}
]
},
]
}
As you can see the nested object to be used for the sub navigation items is titled subCats. I would have thought that I could access the object within another object by referring to the sidenav reducer state like so: this.props.reducerSidenav.subCats.map((subMenuItem) => {... much like I did for the parent categories but I'm getting the "Cannot read property 'map' of undefined" error in console. Where have I gone wrong?

There is a bug in your code.
this.props.reducerSidenav.subCats.map((subMenuItem) => {
<li key={subMenuItem.subCatId}>{subMenuItem.subCatLabel}</li>
})
reducerSidenav is a table, probably you wanted to have something like this
menuItem.subCats.map((subMenuItem) => {
<li key={subMenuItem.subCatId}>{subMenuItem.subCatLabel}</li>
})

Quote your keys?
export default function(){
return [
{
"catId":"parMenu1",
"parentCat":"Bass",
"subCats":[
{
"subCatId":"subMenu1",
"subCatLabel":"base1"
},
{
"subCatId":"subMenu2",
"subCatLabel":"base2"
},
{
"subCatId":"subMenu3",
"subCatLabel":"base3"
},
{
"subCatId":"subMenu4",
"subCatLabel":"base4"
}
]
},
{
"catId":"parMenu2",
"parentCat":"treb",
"subCats":[
{
"subCatId":"subMenu1",
"subCatLabel":"treb1"
},
{
"subCatId":"subMenu2",
"subCatLabel":"treb2"
},
{
"subCatId":"subMenu3",
"subCatLabel":"treb3"
},
{
"subCatId":"subMenu4",
"subCatLabel":"treb4"
}
]
},
{
"catId":"parMenu3",
"parentCat":"drums",
"subCats":[
{
"subCatId":"subMenu1",
"subCatLabel":"drums1"
},
{
"subCatId":"subMenu2",
"subCatLabel":"drums2"
},
{
"subCatId":"subMenu3",
"subCatLabel":"drums3"
},
{
"subCatId":"subMenu4",
"subCatLabel":"drums4"
}
]
},
]
}

I think the "this" keyword inside your first map() function does not reference what you think it does. You need to bind "this" to the map() function to access the props in the outer object:
this.props.reducerSidenav.map((menuItem) => {
return (
<li key={menuItem.catId}>
{menuItem.parentCat}
<ul className="bottom-level-menu">
{
this.props.reducerSidenav.subCats.map((subMenuItem) => {
<li key={subMenuItem.subCatId}>{subMenuItem.subCatLabel}</li>
})
}
</ul>
</li>
)
}.bind(this))
}

Related

Own TreeStructure in React - RenderTree

I'm trying to make a tree structure in react.
I use redux to hold data. I'am trying render this render tree by method 'renderTree' but I do something wrong
Here is my code:
import React, { Component } from 'react'
import {treeActions} from '../_actions'
import { connect } from 'react-redux';
class TreePage extends Component {
componentDidMount() {
this.props.getTree()
}
renderTree(parentId) {
const nodes = this.items && this.items.filter(x => x.parentNodeId == parentId);
<ul>
{nodes && nodes.map((item) => {
<li>{item.name}</li>
item.subNodes && this.renderTree(item.NodeId)
})}
</ul>
}
render() {
const {items} = this.props.tree;
this.items = items
return(
<React.Fragment>
<h1>Hello World!</h1>
{this.renderTree()}
</React.Fragment>
)
}
}
function mapState(state){
return state;
}
const actionCreators = {
getTree: treeActions.getTree
}
const connectedApp = connect(mapState, actionCreators)(TreePage)
export {connectedApp as TreePage}
And here is the photo of items:
And here is the message from console
./src/TreePage/TreePage.js
Line 14:9: Expected an assignment or function call and instead saw an expression no-unused-expressions
Line 16:17: Expected an assignment or function call and instead saw an expression no-unused-expressions
Search for the keywords to learn more about each error.
What could be the problem?
The rendering can be simplified a bit, since the "root" of the tree in your case is already an array of nodes, and every node always as a subNodes property, which is always an array. Using the parentId can be avoided completely, since we're rendering from the root and always follow the subNodes.
import React, { Component } from 'react';
const tree = [
{
name: 'node1',
subNodes: [
{
name: 'node1-1',
subNodes: [],
},
{
name: 'node1-2',
subNodes: [],
},
],
},
{
name: 'node2',
subNodes: [],
},
{
name: 'node3',
subNodes: [
{
name: 'node3-1',
subNodes: [
{
name: 'node3-1-1',
subNodes: [],
},
{
name: 'node3-1-2',
subNodes: [],
},
],
},
{
name: 'node3-2',
subNodes: [],
}
],
},
];
class Tree extends Component {
renderSubNodes(subNodes) {
return (
<ul>
{subNodes.map((node) => (
<li>
{node.name}
{this.renderSubNodes(node.subNodes)}
</li>
))}
</ul>
);
}
render() {
return(
<React.Fragment>
<h1>Hello World!</h1>
{this.renderSubNodes(tree)}
</React.Fragment>
)
}
}
export default Tree;
The issue is that you haven't returned your JSX from within the renderTree function.
renderTree(parentId) {
const nodes = this.items && this.items.filter(x => x.parentNodeId == parentId);
return (<ul>
{nodes && nodes.map((item) => {
return (
<React.Fragment>
<li>{item.name}</li>
{item.subNodes && this.renderTree(item.NodeId)}
</React.Fragment>
)
})}
</ul>
)
}

Error in declaring a variable inside the component class and outside the render function to use it inside the render function in react

in the code bellow when I declare the varable characters via destructuring
const { characters } = this.state;
I get a Unexpected token error.
import React, { Component } from "react";
import Table from "./Table";
class App extends Component {
state = {
characters: [
{
name: "Charlie",
job: "Janitor"
},
{
name: "Mac",
job: "Bouncer"
},
{
name: "Dee",
job: "Aspring actress"
},
{
name: "Dennis",
job: "Bartender"
}
]
};
removeCharacter = index => {
const { characters } = this.state;
this.setState({
characters: characters.filter((character, i) => {
return i !== index;
})
});
};
const { characters } = this.state;
render() {
return (
<React.Fragment>
<div className="App">
<h1>Hello, React!</h1>
</div>
<div className="container">
<Table
characterData={characters}
removeCharacter={this.removeCharacter}
/>
</div>
</React.Fragment>
);
}
}
export default App;
here is a sandbox of the code:sandbox with the error, look at the app.js file
and when I put the declaration inside the render function there is no problem,
look at the correct code sandbox.
Also when I create a variable, outside of the render function (of the App.js file), without const or let for example:
x=1;
It works fine when I use it inside the render function as this.x, but when I declare it with let,const or var it throws me an Unexpected token error.
How do you explain that behaviour?
You have destructed characters outside the render method.
import React, { Component } from "react";
import Table from "./Table";
class App extends Component {
state = {
characters: [
{
name: "Charlie",
job: "Janitor"
},
{
name: "Mac",
job: "Bouncer"
},
{
name: "Dee",
job: "Aspring actress"
},
{
name: "Dennis",
job: "Bartender"
}
]
};
removeCharacter = index => {
const { characters } = this.state;
this.setState({
characters: characters.filter((character, i) => {
return i !== index;
})
});
};
render() {
const { characters } = this.state;
return (
<React.Fragment>
<div className="App">
<h1>Hello, React!</h1>
</div>
<div className="container">
<Table
characterData={characters}
removeCharacter={this.removeCharacter}
/>
</div>
</React.Fragment>
);
}
}
export default App;

Uncaught TypeError: Cannot read property 'onDismiss' of undefined

I have a React Component Posts:
import React, { Component } from 'react';
const list = [
{
title: 'React',
url: 'https://facebook.github.io/react',
author: 'Adam Beat',
num_comments: 4,
points: 3,
objectID: 10,
}, {
title: 'Rails',
url: 'https://www.rubyonrails.org',
author: 'DHH',
num_comments: 8,
points: 4,
objectID: 11,
}
]
class Posts extends Component {
constructor(props) {
super(props); // call the constructor of the extended class
this.state = { // bound state with the this object
list: list,
};
this.onDismiss = this.onDismiss.bind(this);
}
onDismiss(id) {
// const isNotId = item => item.objectID !== id;
// const updatedList = this.state.list.filter(isNotId);
const updatedList = this.state.list.filter(item => item.objectID !== id);
this.setState({ list: updatedList });
}
render() {
return (
<div className="home-page ui container">
{
this.state.list.map(function(item) {
return (
<ul key={item.objectID}>
<li>
<a href={item.url}>{item.title}</a>
</li>
<li>{item.author}</li>
<li>{item.num_comments}</li>
<li>{item.points}</li>
<li>
<button
onClick={ () => this.onDismiss(item.objectID)}
type="button">
Dismiss
</button>
</li>
</ul>
);
})
}
</div>
);
}
}
export default Posts;
And I'm trying to remove an item from the list by clicking on "dismiss" button.
But actually I get an error: "Uncaught TypeError: Cannot read property 'onDismiss' of undefined"
What am I missing?
You are using this.state.list.map(function(item) { ... }), so your context is that of a map function. If you change it to this.state.list.map(item => { ... }) it will work. This is because arrow functions automatically binds to the parents this scope. And inside your map function there is no onDismiss function.
When you bind this in a constructor, onDismiss function will receive your components context. However, you still must pass your components function and in this case you are not doing that.

My component isn't updating, am I mutating the state?

I have a container component that connects to the state which I made with immutable.js. When I update the state, my redux inspector tells me that the state is updated, but my component doesn't get the new updates and doesn't re-render.
My container component:
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { setCategoryActive } from '../actions'
import Category from '../components/Category'
class MenuList extends Component {
constructor(props) {
super(props)
this.categories = this.props.categories
this.subreddits = this.props.subreddits
this.allCategories = this.categories.get("allCategories")
this.byName = this.categories.get("byName")
}
printSubreddits(category) {
const subreddits = this.byName.get(category)
const subredditsByName = subreddits.get("subredditsByName")
const list = subredditsByName.map((subreddit, i) => {
return <p className="swag" key={i}>{subreddit}</p>
})
return list
}
isCategoryActive(category) {
const cat = this.byName.get(category)
return cat.get("active")
}
printCategory(category, i) {
console.log(this.isCategoryActive(category))
return (
<div className="category-container" key={i}>
<Category name={category}
active={this.isCategoryActive(category)}
setActive={this.props.setCategoryActive.bind(this, category)} />
{this.isCategoryActive(category) ? this.printSubreddits(category) : null}
</div>
)
}
render() {
return (
<div>
{this.allCategories.map((category, i) => {
const x = this.printCategory(category, i)
return x
}, this)}
</div>
)
}
}
const mapStateToProps = (state) => ({
categories: state.subredditSelector.get('categories'),
subreddits: state.subredditSelector.get('subreddits')
})
export default connect(mapStateToProps, {
setCategoryActive
})(MenuList);
My Category component
class Category extends Component {
printToggle(active) {
if (active) {
return <span> [-]</span>
} else {
return <span> [+]</span>
}
}
componentWillReceiveProps(nextProps) {
this.printToggle(nextProps.active)
}
render() {
const { setActive, active, name } = this.props
return (
<div className="category-container">
<a onClick={setActive}
href="#"
className="category-title">
{name}
{this.printToggle(active)}
</a>
</div>
)
}
}
export default Category
And my reducer
import { fromJS } from 'immutable'
import {
SET_SUBREDDIT_ACTIVE,
SET_CATEGORY_ACTIVE
} from '../actions'
import List from '../data/nsfw_subreddits.js'
const initState = fromJS(List)
const subredditSelector = (state = initState, action) => {
switch (action.type) {
case SET_SUBREDDIT_ACTIVE:
return state
case SET_CATEGORY_ACTIVE:
return state.updateIn(['categories', 'byName', action.payload.name],
x => x.set('active', !x.get('active')))
default:
return state
}
}
export default subredditSelector
A piece of my state that I have coded as a JSON object
const list = {
categories: {
byName: {
"example": {
name: "example",
id: 1,
active: true,
subredditsByName: ["example", "example"]
},
"example": {
name: "example",
id: 2,
active: true,
subredditsByName: ["example"]
},
"example": {
name: "example",
id: 3,
active: true,
subredditsByName: ["example", "example", "example"]
}
},
allCategories: ["example", "example", "example"]
},
subreddits: {
My guess is that my reducer is mutating the state? Though I am not sure how, since I am using the immutable js functions?
So I fixed this issue by changing the constructor from a constructor to a regular function and called it at the top of my render method!
You should still keep your constructor, but only have in it what needs to be done when the object is first created:
constructor(props) {
super(props)
}
And yes, having
cleanPropData(){
this.categories = this.props.categories
this.subreddits = this.props.subreddits
this.allCategories = this.categories.get("allCategories")
this.byName = this.categories.get("byName")
}
is fine. I haven't heard of someone invoking it at the top of a render function. Nice to know that works.

React rendering nested json

I have some json looking like this
[
{
"name": "Hello Kitty",
"items": [
{
"name": "Kitty Muu Muu"
},
{
"name": "Kitty smack"
}
]
},
{
"name": "Hello Pussy",
"items": [
{
"name": "World",
"items": [
{
"name": "Hello Pussy world"
}
]
}
]
}
]
it's nested JSON and my question is; where should the recursiveness be handled to render this?
I have 2 components
List component that calls the data and posts data changes back to the server
Item component that renders a single item
Should I handle this at the list level or item level?
The item renders an input field you can update and this should be possible both at parent and child level
You can use Item to show child items(in React you can do it with children property), however main work will be in List, for example
class Item extends React.Component {
render() {
return <li>
{ this.props.name }
{ this.props.children }
</li>
}
}
class List extends React.Component {
constructor() {
super();
}
list(data) {
const children = (items) => {
if (items) {
return <ul>{ this.list(items) }</ul>
}
}
return data.map((node, index) => {
return <Item key={ node.id } name={ node.name }>
{ children(node.items) }
</Item>
});
}
render() {
return <ul>
{ this.list(this.props.data) }
</ul>
}
}
Example
I would do it in List component. If your List component is large, i would put them in another helper file to require it.

Resources