How to properly get message count on form submission using reactjs.
For hours now am on this trying to find the possible solution.
The Application below submits and displays message info and everything works fine.
Now I have task of implementing a message counter each time message is submitted by a user.
This is what I have done to implement counter for the user
var senderId ='Nancy101';
const {textCount} = this.state;
var count = textCount[senderId] == undefined ? 1 : textCount[senderId] + 1;
textCount[senderId] = count;
alert('am counting: ' +count);
Here is my issue after adding the textcount method above
Each time I submit the form am having error
Uncaught TypeError: Cannot read property 'Nancy101' of undefined
at bundle.js:109988
here is the line of code that causes the issue
var count = textCount[senderId] == undefined ? 1 : textCount[senderId] + 1;
Here is the code
import React, { Component, Fragment } from "react";
import { render } from "react-dom";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
message: '',
};
this.sendMessage = this.sendMessage.bind(this);
}
componentDidMount() {
var textCount = [];
}
sendMessage = (message_text) => {
alert(message_text);
this.setState({
data: [
{ id: "1", name: "Nancy101", message: "Hello from User1"}
]
});
}
render() {
return (
<div>
<input type="text" placeholder="Message" value={this.state.message} onChange={ev => this.setState({message: ev.target.value})}/>
<br/>
<span onClick={ () => this.sendMessage(this.state.message)}>Submit</span>
{this.state.data.map((message, i) => {
if (message.message !=='' && message.name !=='') {
//var senderId=message.name;
var senderId ='Nancy101';
const {textCount} = this.state;
var count = textCount[senderId] == undefined ? 1 : textCount[senderId] + 1;
textCount[senderId] = count;
alert('am counting: ' +count);
return (
<div key={i}>
<div>
{message.name}: {message.message}
</div>
</div>
)
} else {
//alert nothing.
}
})}
</div>
);
}
}
The problem here is happening because you trying to get textCountfrom state. However, your state doesn't have a key named textCount.
This is what you are doing.
const { textCount } = this.state;
It's mean this.
const textCount = this.state.textCount;
Which is return you an undefined because your state object doesn't have that key.
Then you are trying to get the value of the key named Nancy101 from undefined object that's why you get that error.
You can fix it by add textCount in your initial state inside constructor like this.
constructor(props) {
super(props);
this.state = {
data: [],
message: '',
textCount: {},
};
Instead of get undefined here const {textCount} = this.state;, now you got an object. {}
Also, you can update this line.
`var count = textCount[senderId] == undefined ? 1 : textCount[senderId] + 1;`
To this.
`let count = !textCount[senderId] ? 1 : textCount[senderId] + 1;`
with this !textCount[senderId] it gonna check your data that is it equal 0, undefined or ''.
Related
I am new to react. My problem is that my variables keep saying that it is undefined. What I am trying to do is to display those variable but fail to destructure it. A filter function is executed and return a single tour. The data is successfully retrieved. By destructuring this, some variable contains an array can not be displayed. Does anyone know how to fix this?
TypeError: Cannot read property '0' of undefined
My data looks like this.
[
{
"_id": "12345",
"name": "I am first tour",
"startLocation": {
description: "USA",
type: "point"
},
"startDates": [
"2021-06-19T09:00:00.000Z",
"2021-07-20T09:00:00.000Z",
],
"imageUrl": [
"https://something1.jpg",
"https://something2.jpg",
"https://something3.jpg",
],
},
//.....rest data
]
import React, { Component } from 'react';
import './Tour.css';
import { connect } from 'react-redux';
class Tour extends Component {
constructor(props) {
super(props)
this.findSingletour = this.findSingletour.bind(this);
}
findSingletour = (tourId) => {
const notYetFilterTours = this.props.tours.tourState.data;
let filtered = [];
if (notYetFilterTours) {
filtered = notYetFilterTours.find((tour) => {
if (tour.id === tourId) {
return filtered;
}
return filtered;
});
}
return filtered;
};
render() {
const tourId = this.props.match.params._id;
let SingleTour = this.findSingletour(tourId);
const {
name,
startLocation,
startDates,
imageUrl
} = SingleTour;
return (
<div>
<span>{name}</span> // successfully rendered
<span>{startLocation[0]}</span> // undefined
<span>{startDates[0]}</span> // undefined
<span>{imageUrl[0]}</span> // undefined
</div>
)
}
}
const mapStateToProps = (state) => ({
tours: state.tourContainer,
});
export default connect(
mapStateToProps,
)(Tour);
Need to do validation just in case:
class Tour extends Component {
// some code
render() {
const {
name,
startLocation,
startDates,
imageUrl
} = SingleTour;
return (
<div>
<span>{name}</span> // successfully rendered
<span>{startLocation && startLocation.length > 0 ? startLocation[0] : ''}</span> // undefined
<span>{startDates && startDates.length > 0 ? startDates[0] : ''}</span> // undefined
<span>{imageUrl && imageUrl.length > 0 ? imageUrl[0] : ''}</span> // undefined
</div>
)
}
}
You can provide default values, and it is generally a good idea to have sensible defaults in case data is not loaded and UI is rendered.
So something like this would prevent such errors:
const {
name = '',
startLocation = [],
startDates = [],
imageUrl = ''
} = SingleTour;
Now if your UI renders and tries to get 0 of startLocation, it won't fail. It will of course find nothing, and display nothing except the UI skeleton, but the app will not error out.
i have a variable named data which is a array of objects as below,
data = [
{ attributes: [],
info: '',
meshes: [],
}
{attributes: [],
info: '',
meshes: [],
}
.....so on....
]
When the info is defined will display message info available..if info undefined will display message info unavailable.
So i do it like below within render function of the component
export default class DataInfo extends React.Purecomponent {
state = {
data: null,
};
componentdidMount() {
load_data();
}
load_data = () => {
/*send a request to server for fetching data and
set data state */
}
render = () => {
return (
{this.loading &&
<div className="spinner"/>}
{!this.data || (this.data && this.data.every((data.info) =>
!data.info)) &&
<div>No info available</div>}
{this.data && this.data.some((data.info) => data.info) &&
<div>info available</div>}
);
}
}
Now withing the conditionals rather than using the below statements,
this.data.every((data.info) => !data.info)
this.data.some((data.info) => data.info)
I want to have them defined as some explanatory variables...like has_some_info and has_no_info.
So to achieve it, within render function i tried using something like below,
const has_some_info = this.data ? this.data.some((data.info) =>
data.info): 'false';
const has_no_info = this.data ? this.data.every((data.info) =>
!data.info): 'false';
But this is not correct. it doesnt work fine..i don't want to initialise it to intialise variables to false....
Could someone help me to defined these variables....thanks.
In the first place, you should realize that every and some are the opposites and you don't have to calculate them both:
const infoAvailable = (this.state.data || []).some(data => data.info);
const noInfoAvailable = !infoAvailable;
In other words:
render() {
if (this.state.loading) {
return <div className="spinner"/>;
}
const infoAvailable = (this.state.data || []).some(data => data.info);
return infoAvailable
? <div>Info available</div>
: <div>No info available</div>;
}
(also note I have used this.state.data to access data).
export default class DataInfo extends React.Purecomponent {
state = {
data: null,
};
componentdidMount() {
load_data();
}
load_data = () => {
/*send a request to server for fetching data and
set data state */
}
render() {
const {loading, data = []} = this.state;
return (
{loading &&
<div className="spinner"/>}
{data.map(ele => ele.info
? <div>info available - {ele.info} </div>
: <div>No info available</div>
}
);
}
}
If you need self explanatory variables for info you could use !!ele.info which gives whether data is present or not.
I have a series of data in which there is a form in every item. I try to use index in onSubmit event and when I check index of for loop in console, it shows the correct index, but when I check index of
handleSubmitR=(e, DetailsRoom, index)=>{console.log(index)}
it is different from the index in for loop.
For example, if I have 3 forms in one item the result of the index in for loop is
'01','02' ,'03'
but in onsubmit event the result of index is **
'03','03','03'
Any suggestions?
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
resultEdit: {},
};
$.ajax({
url:"/json.bc",
type:"post",
success: (result) => {
this.setState({data: eval(result)});
}
})
}
renderHotel(){
return this.state.data.sort((a, b) => a.total - b.total).map((item,i)=>{
return (
<div class="items">
{this.renderDetailsRoom(item,i)}
</div>
)
})
}
renderDetailsRoom(DetailsRoom,i){
let lenfamilies = DetailsRoom.families.length
var indents =[];
for(var j = 0 ;j <lenfamilies;j++){
var numF = i;
var numS = j;
var stingF = numF.toString();
var stingS = numS.toString();
var index= stingF+stingS
**////
<!-----e.g. For two forms the result of consol.log(index) = '00' and '01'----->
/////**
indents.push(<form method="post" key={index} action={this.renderAction(DetailsRoom)} onSubmit={e => this.handleSubmitR(e, DetailsRoom, index)}><div class="Result">{this.state.resultEdit[index]}</div></form>)
}
return(
indents
)
}
handleSubmitR=(e, DetailsRoom, index)=>{
////
<!-----but the result of consol.log(index) in this part for both form are '01'----->
/////
console.log(index)
e.preventDefault();
return this.setState( prevState => ({
resultEdit: { ...prevState.resultEdit, [index]:'submitted'},
})) }
render() {
return (
<div>{this.renderHotel()}</div>);
}
}
ReactDOM.render(<App/>, document.getElementById('Result'));
This question already has answers here:
cannot get the parent property this property when I have two inner loop
(2 answers)
Closed 5 years ago.
I'm building a simple electron-react component.
This component queries enabled Network Interfaces, pushes the data to the state and renders it to the DOM. The component doesn't work because of this error:
"Uncaught TypeError: Cannot read property 'state' of undefined" at this line:
"data = this.state.data.slice();"
Why is this.state undefined? did I miss binding something? Thanks for any advice.
import React from "react";
import os from "os";
export default class GetInterfaces extends React.Component {
constructor(props) {
super(props);
this.state = {
data: []
};
};
componentDidMount() {
let ifaces = os.networkInterfaces();
Object.keys(ifaces).forEach(function (ifname) {
let data;
ifaces[ifname].forEach(function (iface) {
if (iface.internal === true || iface.family === "IPv6") {
return;
}
let networkInterface = {
name: ifname,
mac: iface.mac,
ip: iface.address
};
data = this.state.data.slice();
data.push(networkInterface);
this.setState({ data: data });
});
});
}
render() {
const networkInterfaces = this.state.data.map((networkInterface, index) =>
<ListGroupItem key={index}>
<i class="fa fa-wifi"></i>
<span>{networkInterface.name}</span>
<input type="checkbox"></input>
</ListGroupItem>
);
return (
<div>
{networkInterfaces}
</div>
);
}
}
I think "this" is becoming unbound in the .forEach function, so you can try the trick of saving the context before it like this
componentDidMount() {
let ifaces = os.networkInterfaces();
let that = this; // Note the saving of the context
Object.keys(ifaces).forEach(function (ifname) {
let data;
ifaces[ifname].forEach(function (iface) {
if (iface.internal === true || iface.family === "IPv6") {
return;
}
let networkInterface = {
name: ifname,
mac: iface.mac,
ip: iface.address
};
data = this.state.data.slice();
data.push(networkInterface);
that.setState({ data: data }); // And then using the saved context
});
});
}
this happens because you are losing your this context inside forEach call
as docs says you can pass additional parameter to forEach, which is thisArg:
componentDidMount() {
let ifaces = os.networkInterfaces();
Object.keys(ifaces).forEach(function (ifname) {
...
}, this);
}
I have a gallery component that showcases images from the components prop.
The gallery component shows 1 image at a time, but has 2 loaded randomly form the array for animation purposes.
Everything sounds great, but I'm getting this error in console
Uncaught TypeError: Cannot read property 'currentIndex' of null
Don't know why this would be null.
Here is the ViewGallery.js component:
import React from 'react';
class ViewGallery extends React.Component {
getInitialState(){
return {
currentIndex: 0,
count: 0
};
}
generateImagesDOM(){
return imagesDOM;
}
handleClick() {
let currentIndex = this.state.currentIndex;
if(currentIndex === this.state.count - 1){
currentIndex = 0;
} else {
currentIndex ++;
}
this.setState({
currentIndex: currentIndex
});
$(this.images[currentIndex]).removeClass('active');
}
render() {
let images = this.props.images;
let startWith = this.props.startWith;
let count = this.props.count;
let imagesDOM = [];
let i = startWith || 0;
if(typeof count === 'number' && count > 0){
images = images.slice(0, count - 1);
}
let length = images.length;
if(startWith > 0 && startWith < length){
let images0 = images.slice(0, startWith);
let images1 = images.slice(startWith);
images = images1.concat(images0);
}
for ( ; i<length ; i+=1 ) {
let className = "image";
if(i === this.state.currentIndex){
className += " active";
}
imagesDOM.push(<div className={className}><img src={images[i]} /></div>);
}
this.state.count = length;
return <div className="gallery" onClick={this.handleClick}>{imagesDOM}</div>
}
}
ViewGallery.propTypes = {
images: React.PropTypes.array.isRequired,
startWith: React.PropTypes.number,
count: React.PropTypes.number
}
export default ViewGallery;
When using the class pattern for creating React components, you should set the initial state in the constructor, not in getInitialState().
class ViewGallery extends React.Component {
constructor(props) {
super(props);
this.state = {
currentIndex: 0,
count: 0
};
}
/* rest of your code */
}
Also, your methods, unlike the previous createClass pattern, are no longer bound automatically the component instance, you have to bind them yourself. Either:
onClick={this.handleClick.bind(this)}
or
onClick={e => this.handleClick(e)}
See this blog post from 2015 January on the differences between extends React.Component and React.createClass() component creation patterns.