react render variable undefined - reactjs

for some reason that i dont understand, i cant seem to fetch a state value in my renderer, at first i thought it was a scoping issue, but even after changing to var, my variable is undefined.
constructor(props) {
super(props);
this.state = {
stuff: {}
};
componentDidMount(){
this.getatuff.then( (result) =>{
this.setState({
stuff: result.items[0]
});
});
console.log('REACT Coveo component DOM loaded');
}
render() {
var ReactMarkdown = require('react-markdown');
var project = this.state.stuff;
debugger;
if(!Object.entries(project).length === 0){
var asd = project.body[1].value; <---UNDEFINED
return (
<div className="block">
<ReactMarkdown source={asd} />
</div>
);
}
why is my Object array value undefined in the renderer?
Note: both const variables in the screenshot were changed to var, and the behavior persists.

You'd need to define state inside your class, or inside your constructor():
constructor(props) {
super(props);
this.state = {
project: // something
}
}
componentDidMount() {
// etc.

It's not clear on your example what you're trying to achieve, but it could be one of these reasons:
on your componentDidUpdate you have a typo in this.getatuff (I assume you're trying to say getStuff
project seem to be defined on your renderer, your screenshot shows that it has a key id:5 and some others, but it might not have a key body or body might not be an array, or the array might contain only 1 value [0] instead of two values [0] and [1]. I suggest you to debug the structure of project to get the right value

You got syntax error here: if(!Object.entries(project).length === 0) it should be if (!(Object.entries(project).length === 0))
render() {
var ReactMarkdown = require('react-markdown');
var project = this.state.stuff;
debugger;
if (!(Object.entries(project).length === 0)) {
var asd = project.body[1].value; <---UNDEFINED
return (
<div className="block">
<ReactMarkdown source={asd} />
</div>
);
}

Related

this.state.articles is undefined in React js

I am creating a news app for my project and what i am basically doing is fetching the arrays of news articles from the API and then storing it in a articles array in my local memory and then I am using the map method on that array and thus rendering the articles on my web page but now I'm getting this error:
image of the error
I looked at some similar question I think what I am having a problem with the scope of the constructor.
EDIT: I was wrong at the setstate part but i was just trying it out but the original code is this:
this.state = {
articles: this.articles
}
the above code is the original code in the place of setstate that was there before the below code is the complete code
articles = [];
constructor() {
super();
console.log(typeof articles);
this.state = {
articles: this.articles
}
// let category = 'top-headlines';
// let country = 'in';
}
async componentDidMount() {
const YOUR_API_KEY = 'cant show you this';
// let q = '';
let url = `https://newsapi.org/v2/top-headlines?country=in&apiKey=${YOUR_API_KEY}`;
const FetchNews = async () => {
const response = await fetch(url);
const parsedjson = await response.json();
console.log(parsedjson);
this.setState({ articles: parsedjson.articles });
}
FetchNews();
}
render() {
return (
<div className="container p-1" style={{ backgroundColor: '#F0CE57' }}>
<div className="noogle">
<h1>Noogle</h1>
<input type="text" placeholder='Search News' />
</div>
<div className="row">
{this.state.articles.map((element) => {
return <div className="col-md-4" key={element.url}>
<NewsCard title={element.title} desc={element.description} imgurl={element.urlToImage} url={element.url} />
</div>
})}
</div>
</div>
)
}
}
Because this.state doesn't have a property called articles. It has a property called this which is set to... whatever this.setState returns while trying to set the state to this.articles, which also doesn't exist. This isn't at all what you want:
this.state = {
this: this.setState({ articles: this.articles })
}
I don't know where you pieced together that example, but it's all wrong. Just initialize the state value to an empty array:
this.state = {
articles: []
}
Then on initial render you simply have no records to display. And when the AJAX operation completes and sets the new state value you'll have records on the next re-render.
You don't need (or want) your global articles variable. It's going to tempt you to mutate values directly and cause strange bugs.
Why are you setting the state inside constructor. Instead when you are calling this.setState() function , it will set the state of articles
Try this code
articles = [];
constructor() {
super();
console.log(typeof articles);
this.state = {
articles : []
}
// let category = 'top-headlines';
// let country = 'in';
}

undefiend state passed into the Component

I have a class component that looks like this:
interface MyState {
x_array: number[]
y_array: number[]
order_graph_1: number
}
class ItemsContainerOld extends React.Component<MyProps, MyState> {
constructor(props: MyProps) {
super(props)
this.state = {
x_array: [],
y_array: [],
order_graph_1: 1,
}
}
addingCoordinate(coord: {x: number; y: number}) {
const new_x_array = this.state.x_array.concat(coord.x)
const new_y_array = this.state.y_array.concat(coord.y)
this.setState({
x_array: new_x_array,
y_array: new_y_array,
})
}
setOrders(orders: number[]) {
this.setState({
order: orders[0],
})
}
render() {
return (
<div
>
<div>
<div>
<DrawerOld
addCoord={this.addCoord.bind(this)}
resetCoords={this.resetCoords.bind(this)}
/>
</div>
<div style={{paddingLeft: '70px', paddingTop: '50px'}}>
<Initial
x={this.state.x_array}
y={this.state.y_array}
deg={this.state.order_graph_1}
color={this.color_graph_1}
/>
</div>
</div>
</div>
)
}
}
export default ItemsContainerOld
I changed it to a functional component. However, upon using the functional component, within this component:
<Initial x={x_array} y={y_array}
I start getting errors like
TypeError: Cannot read property 'length' of undefined
How can I ensure that the correct values reach that component? Here's a codesandbox:
https://codesandbox.io/s/infallible-thunder-xsbrm?file=/src/ItemsContainerNew.tsx
you can always check if the value is undefined or not, like :
if(this.props.x.length===undefined){
return;
}
and if you want, you can use some default value there.
**this is the approach, every time you encounter this you have to put some condition as a workaround or set an initial default value.
Here is my sandbox: https://codesandbox.io/s/vigilant-perlman-rzwge?file=/src/Initial.tsx:932-973
and react lifecycle you should know:
Updated code, completely working : https://codesandbox.io/s/goofy-microservice-mw5j6?file=/src/Initial.tsx
You can fix the problem by changing
if (props.x.length === 0 && props.y.length === 0)
to
if (!props.x && !props.y)
Although this may cover up a deeper problem. It would be best to figure out why props.x or props.y is undefined.
You need to improve your state initialization.
Try not setting values as type 'any', be more precise, when initializing your x_arrayand y_array you could use
// Notice that not only the type has changed from "any" to "number[]",
// But the initial value is now an empty array, causing their initial values
// (and types) to be arrays since their starting point.
// When initialization happens without a starting value like "useState()",
// The state receives the value "undefined" which causes the error.
const [x_array, setXarray] = useState<number[]>([]);
const [y_array, setYattay] = useState<number[]>([]);

React: Passing a value from componentDidMount to render()

I am new to react and getting confused between react hooks. There are many similar questions asked and I tried a few answers but it didn't work. I am trying to use a value of flag which has been set in componentDidmount() in render(). But I am getting undefined. Here is my code. Can someone help me?
export default class Shop extends Component {
constructor(props) {
super(props);
this.state = {
isContentTypeShop1: false,
};
}
async componentDidMount() {
const basketContextData = await fetchBasketContext(); // returns json object
const basketContentType = basketContextData.basketContentType; //returns string 'shop1'
console.log(basketContentType)
if(basketContentType === 'shop1') {
this.isContentTypeShop1 = true;
}
console.log(this.isContentTypeShop1); // returns true
}
render() {
console.log(this.isContentTypeShop1); //returns undefined
return (
<ul className="progress-bar">
<li>
{(this.isContentTypeShop1) && ( // hence doesn't work
<span>
Shop 1
</span>
)}
</li>
</ul>
);
}
}
You need to make use of setState to trigger a re-render from componentDidMount. Also isContentTypeShop1 isn't a class variable but its a state
async componentDidMount() {
const basketContextData = await fetchBasketContext(); // returns json object
const basketContentType = basketContextData.basketContentType; //returns string 'shop1'
console.log(basketContentType)
if(basketContentType === 'shop1') {
this.setState({isContentTypeShop1: true});
}
}
render() {
// use it from state
console.log(this.state.isContentTypeShop1);
}
this.isContentTypeShop1 doesn't exist because isContentTypeShop1 is inside state. Try this instead:
console.log(this.state.isContentTypeShop1);
And to update isContentTypeShop1, you need to call setState:
this.setState({ isContentTypeShop1: true });
You need to use this.state.isContentTypeShop1 instead of this.isContentTypeShop1 & you can't set state using =
You need to use setState like this.setState({ isContentTypeShop1: true })
You need to read Using State Correctly part from the React docs
And for some additional reading :)

ReactJS: why is pushing value into this.state array causing type error?

I tried to push a value into a state array but I get an issue TypeError: Cannot read property 'state' of undefined at this.state.rows.push(a);
Know why? I am trying to push a new value into the array after i click a button.
App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
constructor() {
super();
this.state = {
name: '',
rows: ['hello',<p>gfdfg</p>,'mello']
}
}
handle(e){
e.preventDefault();
var a = "h";
this.state.rows.push(a);
alert("hello");
}
render() {
return (
<div className="App">
Button<br/>
<input type="submit" id="black" onClick={this.handle}/><br/>
<p>{this.state.rows}</p>
</div>
);
}
}
export default App;
There are couple of things that are wrong here:
you should NEVER change the state directly:
This is a big No No:
this.state.rows.push(a);
instead you should do something like this:
this.setState({ rows : [...this.state.rows, a] })
or without ES6:
const newArray = this.state.rows.slice();
newArray.push(a);
this.setState({ rows: newArray })
You should always replace the state with a new one.
this in a react component is not what you think it is, in order to make it work you can do one of two things:
a. change your method to an arrow function:
handle = (e) => {
e.preventDefault();
var a = "h";
this.state.rows.push(a);
alert("hello");
}
b. bind this to the method:
constructor() {
super();
this.state = {
name: '',
rows: ['hello',<p>gfdfg</p>,'mello']
}
this.handle = this.handle.bind(this);
}
the method handle does not have access to the context of the class i.e this; consider writing it as a fat arrow function
// class definition
handle = () => {
e.preventDefault();
var a = "h";
this.state.rows.push(a);
alert("hello");
}
render() {
// render logic
}
Having said this, mutating the state is not a good idea, consider using setState if you want your component to re-render as a result of state change
handle = () => {
e.preventDefault();
let { rows } = this.state;
var a = "h";
rows.push(a);
this.setState({
rows,
});
}
You are doing wrong, you have to use setState() method to push the value in the array:
handle = (e) => {
e.preventDefault();
var a = "h";
let tempRows = [...this.state.rows];
tempRows.push(a)
this.setState({rows:tempRows})
alert("hello");
}
You have two problems.
Event handlers require 'this' to be bound: https://reactjs.org/docs/handling-events.html So following this, you must either write: this.handle = this.handle.bind(this) in your contructor, or change handler to arrow function, if your build process supports transpilation of class fields.
React component will only update if component props change, or component state changes. Which is done by comparing references. In your case, when you push to the array, you are mutating the state, so the new reference is never created, and component does not re-render. If you want to verify that, just put console.log(this.state.rows) below this.state.rows.push(a) and you'll see that the array has received the new value, but component does not represent it. You need to use this.setState to create a new reference for your state, like so: this.setState({ rows: [...this.state.rows, a] })
Another way of returning a new array from the current array with additional elements and then pushing to state is to use the concat method.
Example :
this.setState({ users: this.state.users.concat(<Additonal Items Here>)}

loop data from state with react.js

I'm doing my first reactjs app and i have run into some troubles.
This is my feature (child) component that i call from my base file.
var ReactDOM = require('react-dom');
var React = require('react');
var ConfigurationService = require('../configurationService');
class Feature extends React.Component {
constructor(props) {
super(props);
this.state = {
features: null
};
this.getConfiguration();
}
getConfiguration() {
var self = this;
var config = ConfigurationService.getConfiguration('test', 'test').then(function (config) {
self.setState({ features: config.data.Features })
});
}
render() {
if (this.state.features) {
return (<div> {
this.state.features.map(function (feature) {
<span>feature.Description</span>
})
}
</div>)
}
else {
return <div>no data</div>
}
}
}
module.exports = Feature;
It calls my api and collects data that looks like this:
For like a 10th of a second it shows the "no data" but then i guess that it succeeds to grab the data and that this.state.features no longer is null.
But even though features isn't null it doesn't show anything in the browser.
Because you are not returning anything inside map body, 2nd you need to use {} to print feature.Description because its a dynamic data, and 3rd is you need to assign unique key to each element inside loop otherwise it will throw warning.
Use this:
if (this.state.features) {
return (
<div>
{
this.state.features.map(function (feature, i) {
return <span key={feature.Id}>{feature.Description}</span>
})
}
</div>
)
....
Or you can use arrow function also, like this:
if (this.state.features) {
return (
<div>
{
this.state.features.map((feature) => <span key={feature.Id}> {feature.Description} </span>)
}
</div>
)
....
That's not how map works. You need to have a return statement inside the map which is basically for each element of the array return so and so.
return (
<div> {
this.state.features.map(function (feature) {
return (<span key={feature.Id}>{feature.Description}</span>)
})
}
</div>
)
Here for example for each feature it is returning a span with the contents as feature.Description.
Also like Mayank Shukla pointed out a key is important. keys are basically used by react to compare the new DOM when state changes to the old DOM and make only those changes which are required (instead of re-rendering the entire component). The keys have to be unique so use the feature.Id as a key as that is unique. Don't use array indices as keys as the array might change and then the indices will point to wrong elements in the new array.

Resources