Cannot read property 'map' of undefined ( ReactJS & AJAX) - javascript

Try show in table my array. Array get from AJAX request in Action. I'm using Redux
class IncomeProfile extends Component {
constructor(props) {
super(props)
}
componentDidMount() {
this.props.IncomeListProfile();
}
render() {
var elems = this.props.items.course_list;
console.log(elems);
return (
<div>
<table>
{elems.map((item) => (
<tr key={item.course_id}>
<td>{item.name}</td>
</tr>
))}
</table>
</div>
)
}
}
const mapDispatchToProps = function(dispatch) {
return {
IncomeListProfile: () => dispatch(IncomeProfileList())
}
}
const mapStateToProps = function(state) {
var mystore = state.toArray();
//console.log(mystore[6]);
return {
items: mystore[6]
};
}
export default connect(mapStateToProps, mapDispatchToProps)(IncomeProfile);
Console.log first print "undefined" then it print this:
Try add condition in render method if (elems) { } not helps

Make use of the following
var elems = this.props.items['course_list'];
var copy = Object.assign({}, elems);
console.log(elems.course_list);

Related

Parent scope not triggering child rerender in React

i have a prent comp and a child cmponent. as follows
parent
export class ExpressionMenu extends Component {
constructor(props) {
super(props)
}
state = {
apiArray: [],
}
updateStateFromChild = (arrayType, propertyType, value) => {
let { apiArray } = this.state
let currentArray = []
let idx = apiArray.findIndex((q) => q.id === id)
currentArray = apiArray
switch(propertyType) {
case 'updateObject': {
currentArray = value
break;
}
}
this.setState({
apiArray: currentArray
})
}
render () {
const {
apiArray
} = this.state
return (
<React.Fragment>
<div >
<div>
<ApiPanel
apiArray={apiArray}
updateStateFromChild={this.updateStateFromChild}
/>
</div>
</div>
</React.Fragment>
)
}
}
ExpressionMenu.propTypes = {
styleOverride: PropTypes.object,
eventHandler: PropTypes.func,
};
export default ExpressionMenu;
child
export class ApiPanel extends Component {
constructor(props) {
super(props),
}
removeApi = (id) => {
let { apiArray } = this.props
apiArray = apiArray.filter((q) => q.id !== id);
this.props.updateStateFromChild('api', 'updateObject', apiArray)
};
addApi = () => {
let { apiArray } = this.props
const id = uniqid();
let obj = {}
obj.id = id
apiArray.push(obj)
this.props.updateStateFromChild('api', 'updateObject', apiArray)
};
render() {
const { apiArray } = this.props
return (
<React.Fragment>
{
apiArray.map((apiObj, i) =>
<div key={i} >
<span onClick={() => this.removeApi(apiObj.id) } className={[classes.deleteRow,'material-icons'].join(' ')}>
close
</span>
<div>
<label><b>Hi</b></label>
</div>
<div onClick={this.addApi}>+Add Api</div>
}
</React.Fragment>
)
}
}
ApiPanel.propTypes = {
apiArray: PropTypes.array,
updateStateFromChild: PropTypes.func
}
export default ApiPanel
Now when i call addApi(), it updates the parent but doesnt rerenders the child.
But when i call removeApi() , it updates parent as well as rerenders the child component properly.
in the first case when i manually reload the componnt i can see the change.
Dont understand why this is happening
Try to change your addApi function.
addApi = () => {
let { apiArray } = this.props
this.props.updateStateFromChild('api', 'updateObject', [...apiArray, {id : uniqid()} ])
};
You need to return an enriched copy of your array
Whenever we are updating the stating using arrays, objects. We need to always create a new array [...array], a new object {...obj}. Because if we update value in the array or obj without creating it won't change the reference value hence it assumes the state is not update and won't re-render.

In React Context, how can I use state variables in state functions?

I have a React Context which looks like this:
import React, { Component } from 'react'
const AlertsContext = React.createContext({
categoryList: [],
setCategoryList: () => {}
})
export class AlertsProvider extends Component {
state = {
categoryList: [],
setCategoryList: categoryString => (
this.categoryList.includes(categoryString)
? this.setState({ categoryList: this.categoryList.filter(value => value !== categoryString) })
: this.setState({ categoryList: this.categoryList.concat([categoryString]) })
)
}
render() {
const { children } = this.props
const {categoryList, setCategoryList } = this.state
return (
<AlertsContext.Provider value={{categoryList, setCategoryList}}>
{children}
</AlertsContext.Provider>
)
}
}
export const AlertsConsumer = AlertsContext.Consumer
So, categoryList is an array of strings, each representing a category. setCategoryList should take a string; if that string is already in the array, it removes it, and if it's not in the array it adds it.
In one of my components the user can select categories from a list of checkboxes. When a checkbox is clicked, the AlertsContext setCategoryList should be called with the value of the clicked box:
import React, { Component } from 'react'
import { AlertsConsumer } from '../../../context/alerts-context'
class AlertFilters extends Component {
constructor(props) {
super(props)
this.state = {
categories: props.categories
}
}
render() {
const { categories } = this.state
return (
<AlertsConsumer>
{({ categoryList, setCategoryList }) => (
<>
{
categories.map(category => (
return (
<div key={category.id}>
<Checkbox id={category.id} value={category.value} onChange={e => setCategoryList(e.target.value)} checked={categoryList.includes(category.value)} />
<label htmlFor={category.id}>{category.value}</label>
</div>
)
))
}
</>
)}
</AlertsConsumer>
)
}
}
export default AlertFilters
This compiles ok, but when I run it and click a checkbox I get the following error:
alerts-context.jsx:77 Uncaught TypeError: Cannot read property 'includes' of undefined
This is in the line:
this.categoryList.includes(categoryString)
in the Context Provider, suggesting that "this.categoryList" is undefined at this point.
I tried changing it to
this.state.categoryList.includes(categoryString)
but it said I had to use state destructuring, so I changed to:
setCategoryList: (categoryString) => {
const { categoryList } = this.state
categoryList.includes(categoryString)
? this.setState({ categoryList: categoryList.filter(value => value !== categoryString) })
: this.setState({ categoryList: categoryList.concat([categoryString]) })
}
which highlighted the ternary operator and gave the following lint error:
Expected an assignment or function call and instead saw an expression.
What am I doing wrong?
Use if/else syntax to update the state.
setCategoryList: categoryString => {
const { categoryList } = this.state;
if (categoryList.includes(categoryString)) {
this.setState({
categoryList: categoryList.filter(value => value !== categoryString)
});
} else {
this.setState({ categoryList: categoryList.concat([categoryString]) });
}
};

How to call child's method from parent without using Refs?

Let's say I've a parent component A and a child B:
A:
class A {
constructor() {
this.state = {data: []};
}
handleClick = () => {
// api call
// set data state to the returned value from api
// call B's createTable method
}
render() {
return(
<div>
<button onClick={()=> this.handleClick()}>Fetch data</button>
<B data={this.state.data} />
</div>
}
}
B:
class B {
constructor() {
this.state = {...};
}
createTable = () => {
const { data } = this.props;
// do smth
}
render() {
return(...);
}
}
I want to call createTable method from A without using Refs.
What I've done so far is using componentDidUpdate life cycle method in B to check if data prop has changed or not, If it changed call createTable method but I want to know is this right? or there's a better way of doing it because I feel it is kinda hacky or maybe bad design.
class B {
constructor() {
this.state = {...};
}
componentDidUpdate(prevProps) {
const { data } = this.props;
if (data !== prevProps.data) {
this.createTable();
}
}
createTable = () => {
const { data } = this.props;
// do smth
}
render() {
return(...);
}
}
NOTE I don't want to use hooks either just class based component.
The following example might be useful
class Parent extends Component {
render() {
return (
<div>
<Child setClick={click => this.clickChild = click}/>
<button onClick={() => this.clickChild()}>Click</button>
</div>
);
}
}
class Child extends Component {
constructor(props) {
super(props);
this.getAlert = this.getAlert.bind(this);
}
componentDidMount() {
this.props.setClick(this.getAlert);
}
getAlert() {
alert('clicked');
}
render() {
return (
<h1 ref="hello">Hello</h1>
);
}
}

Map function not return item in render reactjs?

here is code, why my map function not return item in div.
I have use array of object in state function.
Here is my simple code.
I have XML data in componentwiillrecieveprops. is there any issue by componentwillmount. I do not understand why map function in map my array of state.
import React from 'react';
import TextField from 'material-ui/TextField';
var self;
export default class NewAuthoring extends React.Component {
constructor(props) {
super(props);
self = this;
this.state = {
sampleState : "OriginalState",
task : [
{event:"First data",eventpara:"First Data"},
{event:"Second data",eventpara:"Second Data"},
{event:"Third data",eventpara:"Third Data"}
]
}
}
componentWillReceiveProps(nextProps) {
console.log(nextProps.xml)
if(this.props != nextProps) {
//Do Something when any props recieve
this.setState({sampleState:nextProps.xml});
}
}
componentWillMount() {
//Do something before component renders
let xml ="<div type=”timeline/slideshow”><section><header></header><article></article></section><section><header></header><article></article></section><section><header></header><article></article></section><section><header></header><article></article></section></div>";
self.props.getChildXml(xml);
}
componentDidMount() {
//Do Something when component is mounted
}
handleChange(e) {
//getChildXml function will update the xml with the given
//parameter and will also change the xml dialog value
let xml ="<div type=”timeline/slideshow”><section><header></header><article></article></section><section><header></header><article></article></section><section><header></header><article></article></section><section><header></header><article></article></section></div>";
self.props.getChildXml(xml);
}
render() {
const myStyle = {
mainBlock:{
fontWeight:"bold",
margin:"2px"
}
}
const div_style = {
border:'1px solid black',
margin:10
}
{
this.state.task.map((item,contentIndex) => {
return (<div>
hello
{item.event}
{item.eventpara}
</div>)
})
}
}
}
Any help will be appreciated.
You are not returning element from map callback. Also i see that tasks is an array of object, and you are directly rendering object by writting {item}. You need to return the element and should avoid rendering object directly like this
{
this.state.task.map((item,contentIndex) => {
return (<div>
hello
{item.event}
{item.eventpara}
</div>)
})
}
Alternatively you can also avoid use of {} brackets to return the element without writting return keyword.
{
this.state.task.map((item,contentIndex) => (
<div>
hello
{item.event}
{item.eventpara}
</div>
))
}
UPDATE: You need to return something from render function
render() {
const myStyle = {
mainBlock:{
fontWeight:"bold",
margin:"2px"
}
}
const div_style = {
border:'1px solid black',
margin:10
}
return (
<div>
{
this.state.task.map((item,contentIndex) => {
return (<div>
hello
{item.event}
{item.eventpara}
</div>)
})
}
</div>
)
}
Since the map pattern is very common in React you could also do something like this:
1: Create a Map/Iterator component
const Iterator = (props) => {
//you could validate proptypes also...
if(!props.array.length) return null
return props.array.map(element => props.component(element))
}
2.return it passing the component as props:
render() {
const myStyle = {
mainBlock:{
fontWeight:"bold",
margin:"2px"
}
}
const div_style = {
border:'1px solid black',
margin:10
}
return <Iterator
array={this.state.task}
component={
item=>(<div key={item.event}>hello{item.event}
{item.eventpara} } </div>
/>
}
Because you're returning nothing in render(). Use it as follows:
render(){
return(
{this.state.task.map((item,contentIndex) => {
return <SomeElement />;
}
);
}

How can I delete the item if it is stored as an array in reactjs?

Have to display the items via web API. I got the response successfully and displayed the items. But I don't know how to delete the item from the list.
Displayed the delete icon to every item, but I am not able to delete the item from the state. What did I wrong? Please help me out.
import React from 'react';
class App extends React.Component {
constructor() {
super();
this.state = {
posts: []
}
this.UserList = this.UserList.bind(this);
this.setStateHandler = this.setStateHandler.bind(this);
this.setDeleteHandler = this.setDeleteHandler.bind(this);
}
componentDidMount() {
this.UserList();
}
setStateHandler() {
alert(0)
var item = {
"url": "http://www.imdb.com/title/tt4937812/",
"title": "PAMMAL K SAMBANTHAM",
"imdb_id": "tt4937812",
"type": "Film",
"year": "2015"
};
var myArray = this.state.posts;
myArray.push(item)
console.log(myArray)
this.setState({posts: myArray})
};
setDeleteHandler() {
alert("idris")
};
UserList() {
alert(1)
fetch('http://www.theimdbapi.org/api/person?person_id=nm0352032').then(response => {
return response.json();
}).then(data => {
const posts = data.filmography.soundtrack;
this.setState({posts});
});
}
render() {
return (
<div>
<div>
{
this.state.posts.map((item, i) => {
return <Content item={item} key={i}/>
})
}
</div>
<button onClick = {this.setStateHandler}>SET STATE</button>
</div>
);
}
}
class Content extends React.Component {
render() {
return (
<div>
<table>
<tbody>
<tr>
<td>{this.props.item.title}</td>
<td>{this.props.item.type}</td>
<td>{this.props.item.type}</td>
<td><button onClick = {this.setDeleteHandler}>Delete</button></td>
</tr>
</tbody>
</table>
</div>
);
}
}
export default App;
Can anyone please help on this?
Thank you.
Make your setDeleteHandler like this
setDeleteHandler(index) {
return () => {
let posts = [...this.state.posts];
posts.splice(index,1);
this.setState({posts});
}
};
Then pass this function to your content component like this
this.state.posts.map((item, i) => {
return <Content item={item} key={i} deleteHandler={this.setDeleteHandler(i)}/>
})
And then in your content component call this function like this
<td><button onClick = {this.props.deleteHandler}>Delete</button></td>

Categories

Resources