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';
}
Related
Yet another one of these questions. I know there are several here, but I can't see my mistake.
My code is based on "create-react-app" via Visual Studio 2019, so the boilerplate should look familiar.
I'm passing a function called "updateTodoCheckbox" from the parent FetchData component to my TodoItem component via a property called "onCheckboxUpdate.
When handleInputChange is called via the onChange for the checkbox (which has the name property isComplete), I get:
TypeError: this.props.onCheckboxUpdate is not a function
I have called bind on the function in the constructor. I've also tried passing it as an arrow function. Same result, so I don't think it's a binding issue.
Any help appreciated to see my error. I'm sure it's a simple one.
import React, { Component } from 'react';
export class TodoItem extends Component {
constructor(props) {
super(props);
this.state = {
isComplete: props.data.isComplete
}
this.handleInputChange = this.handleInputChange.bind(this); // otherwise this.setState will be undefined
}
handleInputChange(inputEvent) {
const target = inputEvent.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
if (name == "isComplete")
{
this.props.onCheckboxUpdate(); // This fails
}
this.setState({
[name]: value
});
}
render() {
return (
<div class="form-group" key={this.props.data.id}>
<div className="form-check">
<input class="form-check-input" type="checkbox" name="isComplete" checked={this.state.isComplete} id={this.props.data.id} onChange={this.handleInputChange}/>
<label class="form-check-label" for={this.props.data.id}>{this.props.data.name} - {this.props.data.categoryId}</label>
</div>
</div>
)
}
}
export class FetchData extends Component {
static displayName = FetchData.name;
constructor(props) {
super(props);
this.state = { todoData: [], loading: true };
this.updateTodoCheckbox = this.updateTodoCheckbox.bind(this);
}
componentDidMount() {
this.populateTodoData();
}
static renderTodoData(todoData) {
return (
<form>
{todoData.map(todo =>
<TodoItem data={todo}
onCheckboxUpdate={this.updateTodoCheckbox} />
)}
</form>
);
}
render() {
let contents = this.state.loading
? <p><em>Loading...</em></p>
: FetchData.renderTodoData(this.state.todoData);
return (
<div>
<h1 id="tabelLabel" >TODO</h1>
<p>A list of things to do</p>
{contents}
</div>
);
}
async populateTodoData() {
const response = await fetch('api/TodoItems');
const data = await response.json();
this.setState({ todoData: data, loading: false });
}
async updateTodoCheckbox() {
console.log(test);
// some await call will go here
}
}
Problems looks to be the renderTodoData(todoData) render function as it is marked as static. Functions that have been marked as static do not have access to class scope variables/functions as they. Code placed in static scope can only acres other variables/functions that is also static (other static functions for example).
You should be ae to remove the static keyword from renderTodoData(todoData). You may need to bind it as you did with handleInputChange in your constructor.
Problem is static renderTodoData(todoData) function which is static and which, because of that, doesn't know about "this" (it can only access to the other static methods). Try not having that function as static.
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 :)
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>
);
}
I am brand new to Reacjs, I visited several question with a similar title but none helped me.
Why this code doesn't change my state?
componentWillMount()
{
/** I am using superagent to fetch some data but it doesn't matter */
var url = "http://www.omdbapi.com/?s=star&apikey=mykey";
Request.get(url).then((response) => {
this.setState({ messages: response.body.Search});
});
}
My render method
render() {
return (
<div>
<ListaMensagens messages={this.state.messages} /> this.state is null here.
</div>
...
How can I change my state with the retrieved data and pass it to a child component?
In general you can use setState in componentWillMount without getting into trouble, BUT...
in your case you are setting the state after the response of the request which is causing problems.
Three solutions for your problem:
1: Use the constructor to initialize the state.
constructor(props){
super(props);
this.state = {
messages: []
};
}
2: Fire setState in componentWillMount without waiting for any Promise
componentWillMount() {
this.setState({
messages: []
});
request(...);
}
3: In the render function check if the state is set properly
render(){
<div>
{ this.state && this.state.messages && <ListMensagens ... /> }
</div>
}
Whenever I try to fetch my results from componentWillMount the application hits render() first it comes out and then it hits componentWillMount fetches my results, sets the state and then hits render again.
componentWillMount= () => {
let team = gh.getRepo('Microsoft', 'vscode');
team.getContributors(function(err, members){
}).then(response => {
this.setState({
data: response.data
});
});
}
render() {
var ghList = this.state.data;
const names = ghList.map(name => { //when it runs the first time this map in invalid since ghList is null from my this.setState= {data:null}
})
return (
<div className="flip-container" onClick={this.onTileClick}>
<div className="flipper">
<div className="front">
<img className="circle" src={this.state.avatar_url} alt={this.state.login}/>
</div>
<div className="back">
</div>
</div>
</div>
)
If I understood correctly, it's the expected behavior. What is happening:
componentWillMount(): triggers an async request (getContributors).
render() first time, with this.state.data = undefined.
callback from getContributors is called (the response has come), and, then, setState is invoked: setState schedules a new rendering.
render() second time, with populated this.state.data
You'll have to handle your initial state rendering (how your component will render before the ajax returns: maybe some loading spinner..). So, in the render method, you can check if this.state.data is null/undefined and bypass your logic. Or, in constructor, set your data to an empty array:
constructor(props) {
super(props);
this.state = {
data: []
}
}
This way, at least ghList.map will not thrown an error.