React JS this.props.state is not a function? - javascript

Don't understand what is wrong with my statement here, I keep looking up the error message on google but i really don't understand the problem.
componentDidMount()
{
fetch('http://192.168.1.33:8080/getprojects/')
.then(response => response.json())
.then(data => {
this.props.state({
projects: data.name
});
});
}
according to chrome's console it says that
Uncaught (in promise) TypeError: _this2.props.state is not a function
and points to this:
this.props.state({
projects: data.name
});
I am lost here. New to React JS trying to create a website that fetches data constantly (here i'm trying to fill a list under the format ul li by getting names on my Node Express server)
EDIT
here is the complete code before the Return inside the Render function :
class ProjectList extends Component {
constructor (props) {
super (props);
this.state = {
projects: [],
};
}
componentDidMount()
{
fetch('http://192.168.1.33:8080/getprojects/')
.then(response => response.json())
.then(data => {
this.setState({projects: data.name})
});
}
render () {
let projects = this.state.projects;
let liItems = projects.map((project) =>
<li key={project.name}>{project.name}</li>
);
after that it's just basic HTML

You are getting property undefined, because the render function runs before the componentDidMount function. So when it first try to run, the first thing it encounters is this: let projects = this.state.projects; and you defined it initially, it's an empty array. So within your render function, remove the line above and replace with:
if (this.state.projects.length === 0){
return <div>Loading</div>
}
So when initially your component will run it will render for a split second the Loading div, and when your array is populated your component with your array will be rendered.

let liItems = projects.length && projects.map((project) =>
<li key={project.name}>{project.name}</li>
);
Change like this and in render
render(){
return(
<ul>{liItems}</ul>
)
}
If you dont have any projects it renders empty.

Related

React: Async from class to functional

I just started learning React and so far I'm liking it. But the problem is that most of the tutorials are old and still using old states instead of hooks.
Now when I'm following a tutorial, I'm trying to convert the code I see into hooks. For now I'm really stuck and could use some help for this code. I'm trying to convert the code bellow for a functionnal component. I tried using useEffect but it didn't work.
handleChangeOrder(oldIndex, newIndex){
if (oldIndex == newIndex) {return;}
const [todos] = this.state;
let newSequence =[];
todos.forEach((todos,index)=> {
newSequence.push({id: todo.id, order: index + 1 });
});
}
-------------------------------------------
static changeTodoOrderURL(){
return apiDomain + "api/todo/reorder";
}
----------------------------------------
async changeTodoOrder(order){
const url = UrlService.changeTodoOrderUrl();
try{
const response = await HttpService.post(url,{order});
return response.data;
} catch (error){console.error("Not able to change order of the todos")};
}
What I tried so far in converting it :
function handleChangeOrder(oldIndex, newIndex) {
if (oldIndex == newIndex) {
return;
}
const todos = Array.from(todolist);
let newSequence = [];
todos.forEach((todos, index) => {
newSequence.push({id: todo.id, order: index + 1 });
});
useEffect(()=>{
axios.post('http://localhost:8000/api/todo/reorder')
.then(response=>{
setList(response.data);
console.log(response)
})
.catch(err => console.log(err));
},[])
}
I'm struggling mainly with async changeTodoOrder..
You don't need to use useEffect to send API requests based on event change, useEffect used to send API requests/perform side effect based on state change or on the mounting of the component.
Also, you can't call the hooks inside a function, by using it this way you are breaking the rules of hooks.
please review the rules of Hooks here
based on your code above from the class component you can use it as it is without using useEffect

what is the equivalent of watchEffect in VueJS 2

In a VueJS 2 component, I populate a props variable when the component is loaded.
created() {
this.events = null
Service.getEvents()
.then(response => {
this.events = response.data
})
.catch(error => {
console.log(error.response)
})
}
}
I need to reload the component with "events" props when a next page is clicked. I saw a VueJS 3 tutorial, where the props variable is reloaded using the watchEffect method (https://v3.vuejs.org/guide/reactivity-computed-watchers.html#watcheffect)
What is the equivalent of this functionality in VueJS 2?
I tried to use the watch() method for this variable, but that causes an infinite recursion because the "events" object is changed inside the method.
//Error - recusrsion
watch: {
events() {
this.events = null
Service.getEvents()
.then(response => {
console.log(response.data)
this.events = response.data
})
.catch(error => {
console.log(error.response)
})
}
},
How can we reload the events in the same page when user clicks a button/link?
on your code this.events mean the variable of watch...
so it cause recursion.
if you want to add event on click next page,
then add #click= event props on component of next page props
watch in the options api receives the old and the new value:
watch: {
event: function (oldEventData, newEventData) {
console.log(newEventData) // do something with new event data
}
},

Array at index returning undefined in Angular ngAfterViewInit

Using Angular 7 and .NET Core 2.2, I'm working on a financial application and a watchlist for it. Symbols are added (and saved to a database). When the watchlist component loads, in ngOnInit, I get the data pulled from my resolver via router. Then I call a function which takes an array, loops through them, and returns a different data set from the financial data source. I then push each one of those returned items from the Observable into a local array watchListItems: IEXStockPrice[];
My code is:
ngOnInit() {
this.router.data.subscribe(data => {
this.watchListItemsFromDb = data['watchListItems'];
if (this.watchListItemsFromDb) {
this.fetchLatestPriceforWatchListItemsFromIEX(this.watchListItemsFromDb);
}
});
}
ngAfterViewInit(): void {
console.log(this.watchListItems);
console.log(this.watchListItems[0]);
}
fetchLatestPriceforWatchListItemsFromIEX(items: WatchListItemFromNet[]) {
if (!this.watchListItems) {
this.watchListItems = [];
}
items.forEach(element => {
this.iex.getStockQuoteBySymbol(element.symbol)
.subscribe(
(iexData: IEXStockPrice) => {
this.watchListItems.push(iexData);
},
(err: any) => this.alertify.error('Could not get the latest data from IEX.')
);
});
}
Now, I have a child component LineChartComponent which needs to create a line chart/graph of the active watchlist item. I was just going to first load the chart component with the first element in the watchListItems array. I can't get access to it in ngOnInit because the component hasn't finished initializing.
I've tried ngAfterViewInit.
However, as you can see in the picture, the watchListItems has content in it, but trying to access the first element returns undefined.
Since the router provides its data as an Observable, I would only call fetchLatestPriceforWatchListItemsFromIEX() if watchListItemsFromDb has been populated
ngOnInit() {
this.router.data.subscribe(data => {
this.watchListItemsFromDb = data['watchListItems'];
if (this.watchListItemsFromDb) {
this.fetchLatestPriceforWatchListItemsFromIEX(this.watchListItemsFromDb);
}
});
}
fetchLatestPriceforWatchListItemsFromIEX(items: WatchListItemFromNet[]) {
// initialize your array here vs in the viewmodel
if (!this.watchListItems) {
this.watchListItems = [];
}
items.forEach(element => {
this.iex.getStockBySymbol(element.symbol)
.subscribe(
(iexData: IEXStockPrice) => {
this.watchListItems.push(iexData);
},
(err: any) => this.alertify.error('Could not load watchlist')
);
});
}
Then in your component, you can check for the array, then pass the first item to your child component as an #Input:
<ng-container *ngIf="watchListItemsFromDb">
<child-component [data]="watchListItemsFromDb[0]"></child-component>
</ng-container>

Setting fetched data from API in state won't work

I'm trying to build a news/article website for education purposes using Django and ReactJS.
Currently, I've created an article model in Django and set up an API for ReactJS to talk to. Every article has a headline, image, content, featured and quickreads properties. featured and quickreads are boolean values. I've successfully set up my ReactJS component to fetch all the articles however, I'm having trouble filtering the articles that have article.featured as true and article.quickreads also as true. Currently my component has three states: articles, featured and quickreads. This is how it currently looks:
class Api extends React.Component{
constructor(){
super();
this.state = {
articles: null,
featured: null,
quickreads: null
}
}
componentDidMount(){
fetch("http://127.0.0.1:8000/articles/articlesapi/").then(request => request.json()).then(response => this.setState({articles: response}))
var featured = this.state.articles.filter(article => article.featured === true)
var quickreads = this.state.articles.filter(article => article.quickreads === true)
this.setState({featured: featured, quickreads: quickreads})
}
render(){
return (
<p>Hello World</p>
)
}
}
Although the component gets all the articles it fails to update featured and quickreads. I get the following error:
Uncaught TypeError: Cannot read property 'articles' of undefined at componentDidMount (eval at <anonymous>)...
Why is this happening?
fetch is asynchronous, and thus articles is not set (and is null) when you try to filter it to set state. Instead, wait until the data is fetched:
fetch("http://127.0.0.1:8000/articles/articlesapi/")
.then(request => request.json())
.then(response => {
this.setState({
articles: response
featured: response.filter(article => article.featured === true),
quickreads: response.filter(article => article.quickreads === true)
});
});
And filter and set state along with setting articles after the data is fetched. I would, though, only store articles in state, and filtering when you need to do you don't end up having to sync up all the arrays to make sure they have the same data.

Rendering wordcloud in React

I am trying to create a wordcloud inside a React container using this library. The wordcloud works when passed the array test.
I would like to call an api to get the words for the wordcloud and then pass it to the wordcloud library. Below is the code I am using to fetch from the api an array of words and then pass to the wordcloud library.
function getWords() {
return fetch('http://bio-wordcloud-dev.us-east-1.elasticbeanstalk.com/getWords/?format=json')
.then((response) => response.json())
.then((responseJson) => {
return responseJson;
})
.catch((error) => {
console.error(error);
});
}
When debugging the return value in chrome, I see that elements in the response value are undefined. Here is the component:
import WordCloud from 'wordcloud'
class WordCloudComponent extends React.Component {
componentWillMount () {
var words = getWords();
var wordList = []
words.then(function(value) {
value.map(function(object){
wordList.push ([object.word,object.weight]);
});
});
this.setState({words: wordList});
}
componentDidMount() {
var canvas = ReactDOM.findDOMNode(this.refs.canvas)
var test = [["foo", 12], ["bar", 6]];
var words = this.state.words;
console.log(words);
console.log(test)
WordCloud(canvas, { list: test, color: "random-dark", shape: "circle", color:"green", wait: 0, backgroundColor:"black"});
}
render() {
return (
<div className={styles.cloudCanvasContainer}>
<canvas ref="canvas"></canvas>
</div>
);
}
}
My console logs show that both words and tests are arrays containing arrays of words and weight. As this is my first time using React/ Promises, I am unsure as to what is going wrong. Could someone please explain?
Your call to this.setState({words: wordList}); happens instantaneously after you asynchronously invoke getWords. In other words, wordList isn't yet populated with the returned results of the API when you setState. Instead, you should update your state only after you callback from the async API.
componentWillMount () {
getWords().then(function(value) {
this.setState({
words: value.map((obj) => [obj.word, obj.weight])
});
});
}
Modifying your logic like this also eliminates stateful mutation of the wordsList variable defined outside of the scope of your promise callback.

Categories

Resources