State array does not render properly in React.js - javascript

I am working on a hacker news clone I am trying to get the ids of the top stories from their api using axios in componentDidMount and then making another axios call to get the stories and push them in a state array but when I try to map over and render that array nothing shows up
class App extends Component {
constructor(props) {
super(props);
this.state = {
posts: []
}
}
componentDidMount() {
axios.get('https://hacker-news.firebaseio.com/v0/topstories.json')
.then( result => {
result.data.slice(0, 10).forEach(element => {
axios.get('https://hacker-news.firebaseio.com/v0/item/' + element + '.json')
.then( value => {
this.state.posts.push(value)
})
.catch(err =>{
console.log(err)
})
})
})
.catch(err => {
console.log(err);
})
}
render() {
return (
<div>
<Header title="Hacker News" />
{this.state.posts.map( (element, index) => <Post key={element.data.id} serialNum={index} postTitle={element.data.title} postVotes={element.data.score} postAuthor={element.data.by} />) }
</div>
)
}
}

Try setting the state like this:
axios.get('https://hacker-news.firebaseio.com/v0/item/' + element + '.json')
.then( value => {
this.setState({
posts: [value, ...this.state.posts]
})
})
.catch(err =>{
console.log(err)
})
})
This way you're using setState and appending every new value to the existing state.

As stated in the comments, don't use push for set state. In your code when you make the second request you must change the setState method to spread out the new value.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
posts: []
}
}
componentDidMount() {
axios.get('https://hacker-news.firebaseio.com/v0/topstories.json')
.then( result => {
result.data.slice(0, 10).forEach(element => {
axios.get('https://hacker-news.firebaseio.com/v0/item/' + element + '.json')
.then( value => {
this.setState(prevState => ({posts: [ value.data, ...prevState.posts]}))
})
.catch(err =>{
console.log("err");
console.log(err);
})
})
})
.catch(err => {
console.log(err);
})
}
render() {
return (
<div>
{this.state.posts && this.state.posts.map( (element, index) =>
<div key={element.id}>
{element.title}
</div>
)}
</div>
);
}
}

componentDidMount() is called after Render() only once. React doesn't know about the state changes unless you use setState().
componentDidMount() {
axios.get('https://hacker-news.firebaseio.com/v0/topstories.json')
.then( result => {
result.data.slice(0, 10).forEach(element => {
axios.get('https://hacker-news.firebaseio.com/v0/item/' + element + '.json')
.then( value => {
this.setState({posts: [value, ...this.state.posts]})
})
})
})
}
Use this.setState({posts : [value, ...this.state.posts]}) instead of this.state.posts.push(value). using ... (spread operator) appends the value to the original posts array.

Related

How to render new data (or GET new) after making a POST request with axios?

I'm using mockapi.io to practice with Axios API call
After I make a POST request, which create a new data, I want to render FlatList with the updated data. I'm thinking of making a new GET request to do that, but I'm not succeeded with it.
I need help
Here is where I call GET request, which already have mock data, and use FlatList to view it
ListScreen.js
class ListScreen extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
}
}
componentDidMount() {
axios.get('insert url')
.then(res => {
this.setState({
data: res && res.data ? res.data : []
})
})
.catch(err => {
console.log('Run into problem')
})
}
render() {
const { data } = this.state;
return (
<View>
<FlatList
data={data}
renderItem={({ item }) => {
return (
<Item
name={item.lastName}
phone={item.phoneNumber}
/>
);
}}
keyExtractor={(item) => item.id}
/>
</View>
And here is where I call POST request
class Create extends Component {
handleSubmitData = (value) => {
console.log('check value: ', value)
axios.post('insert url', {
lastName: `${value.Name}`,
phoneNumber: `${value.Phone}`,
})
.then((response) => {
console.log('here is what you upload: ', response.data)
})
.catch((err) => {
console.log('Run into problem')
})
}
render() {
return (
<CreateForm
handleSubmitData={this.handleSubmitData}
/>
)
}
}
The CreateForm component looks something like this
class CreateForm extends Component {
render() {
const { handleSubmit } = this.props;
return (
<View>
<View>
<Field
name="Name"
component={}
/>
<Field
name="Phone"
component={}
/>
</View>
<View>
<Button
title='Save'
onPress={handleSubmit(this.props.handleSubmitData)}
/>
</View>
</View>
)
This can be done by lifting the state up, from ListScreen to ListScreen's parent.
In ListScreen, take out the state and move into its parent:
this.state = {
data: [],
}
Pass the state down to ListScreen as a prop, also pass the fetch function (see below):
<ListScreen data={this.state.data} fetchListData={this.fetchListData} />
Change ListScreen's render function to access the data from props:
const { data } = this.props;
Move the axios.get() out of ListScreen and into a method on the parent component.
class App extends Component {
fetchListData() {
axios.get('insert url')
.then(res => {
this.setState({
data: res && res.data ? res.data : []
})
})
.catch(err => {
console.log('Run into problem')
})
}
}
In ListScreen, call the function in componentDidMount:
componentDidMount() {
this.props.fetchListData();
}
Alternatively, you could call it in the parent component's componentDidMount(). This might be preferred if you render multiple ListScreen's for example.
Finally, in the Create component, pass the fetch function:
<Create fetchListData={this.fetchListData} />
Call it on successful creation:
.then((response) => {
this.props.fetchListData();
console.log('here is what you upload: ', response.data)
})
As I see in your code, when you are making a POST request it wont display an updated data to your screen because there is no GET request callback upon the success result.
Suggesting you are not using redux , in ListScreen.js you could wrap the GET request into a function and call it in componentDidMount(). It should be look like:
const getData () => {
axios.get('insert url')
.then(res => {
this.setState({
data: res && res.data ? res.data : []
})
})
.catch(err => {
console.log('Run into problem')
})
}
componentDidMount() {
this.getData()
}
Therefore, you need to pass or drill the GET request function into your child component as a props and use it in a POST request callback. The final POST request method should be look like:
handleSubmitData = (value) => {
console.log('check value: ', value)
axios.post('insert url', {
lastName: `${value.Name}`,
phoneNumber: `${value.Phone}`,
})
.then((response) => {
console.log('here is what you upload: ', response.data)
getData()
})
.catch((err) => {
console.log('Run into problem')
})
}
As you can see, after your POST request is finished, it will trigger a GET request to update your parent state, resulting a screen with an updated data. However, you have to make sure to pass your parameters correctly based on how your component structure is.

Correct way to fetch through array

In the below compoenent, the function is neverending. Can someone tell me what to fix so that in the end the beers array in the state has 5 names?
export default class GetBeers extends React.Component {
constructor() {
super();
this.state = {
beers: [],
didError: false
};
this.getBeerInfo = this.getBeerInfo.bind(this);
}
render() {
return (
...
}
getBeerInfo() {
let beerArr = [1,2,3,4,5];
this.props.beerArr.map(id => {
fetch(`https://api.punkapi.com/v2/beers/${id}`)
.then(res => res.json())
.then(json => {
this.setState(state => {
const beers = state.beers.concat(json[0].name);
return {
beers
};
});
})
.catch(err => {
this.setState({
didError : true
});
});
})
}
}
Well your code should be somethings like this ..
import React from 'react';
export default class GetBeers extends React.Component {
constructor() {
super();
this.state = {
beers: [],
didError: false
};
this.getBeerInfo = this.getBeerInfo.bind(this);
}
render() {
return (
<div>{this.state.beers}</div>
)
}
componentDidMount() {
this.getBeerInfo()
}
getBeerInfo() {
let beerArr = [1,2,3,4,5];
beerArr.map(id => {
fetch(`https://api.punkapi.com/v2/beers/${id}`)
.then(res => res.json())
.then(json => {
this.setState({
//const beers = state.beers.concat(json[0].name);
//return {
//beers
//};
beers: this.state.beers.concat(json[0].name)
});
console.log('well at least this works')
})
.catch(err => {
this.setState({
didError : true
});
});
})
}
}
It is advised that you use the componentDidMount() lifecycle method for the fetch api and add what #atahnksy said.
When you are using setState, you can try this:
this.setState({ beers: [...this.state.beers, json[0].name])
This might fix your problem.
You can improve the render method using a combination of ternary operator(to display appropriate message when it cannot reach the server), format with map and ordered list to get something like this :
render() {
return (
<div><ol>{this.state.beers.length!==0 ? this.state.beers.map((beer)=><li>{beer}</li>) :"Could not retrieve any bears. Try again/ensure you can access the server/networtk"}</ol></div>
)
}

Fetch the first element from the array with fake api 'myjson.com'

In the 'myjson.com' website, I created a url 'https://api.myjson.com/bins/17qwmf' which returns an array to me. How to get an element with 'id: 1', the first element from the array. I'm trying to do it this way: 'https://api.myjson.com/bins/17qwmf/1' but I'm getting an error.
From the documentation it looks like it can be done: http://myjson.com/api
Code here: stackblitz demo
class Items extends Component {
constructor (props) {
super(props);
this.state = {
items: []
}
}
componentDidMount() {
const id = 1;
axios.get
axios({
url: `https://api.myjson.com/bins/17qwmf/${id}`,
method: "GET"
})
.then(response => {
console.log(response.data);
this.setState({
items: response.data
});
})
.catch(error => {
console.log(error);
})
}
render () {
return (
<div >
</div>
)
}
}
if there is no a router for getting an element by it id, you have to filter got array
class Items extends Component {
constructor (props) {
super(props);
this.state = {
items: []
}
}
componentDidMount() {
const id = 1;
axios.get
axios({
url: `https://api.myjson.com/bins/17qwmf`,
method: "GET"
})
.then(response => {
console.log(response.data);
this.setState({
items: response.data.filter(item => item.id === id)[0] // you will get a first element of got array
});
})
.catch(error => {
console.log(error);
})
}
render () {
return (
<div >
</div>
)
}
}
Please check this `https://api.myjson.com/bins/17qwmf?id=${id} if you want to fetch only element with id that ur passed

Accessing axios body parameter outside the URL

I have this axios request:
axios.all([
axios.post(baseURL+'sentiments', {
sentiment_type: 'positive',
filter_tab: type
}),
axios.post(baseURL+'sentiments', {
sentiment_type: 'negative',
filter_tab: type
})
])
.then(axios.spread(result1, result2) => {
console.log(result1),
console.log(result2)
})
.catch(err => {
console.log(err)
})
Then in my render method,
What I am trying to achieve is to access 'sentiment_type' value 'positive' into some variable or something, to further use it for conditional rendering.
if(sentiment_type === positive){
then do this
}
else{
do something else
}
Is there any way to do this? Please guide.
I assume this part of code, is part of a component.
You could store these variables in a local store.
Here is a link for more information: State and Lifecycle
and an example, I hope this will help you.
class parentComponent extends React.Component {
state = {
sentiment_types: [];
}
componentDidMount() {
axios.post(baseURL+'sentiments', {
sentiment_type: 'positive',
filter_tab: type
})
.then((firstResponse) => {
axios.post(baseURL+'sentiments', {
sentiment_type: 'negative',
filter_tab: type
})
.then((secondResponse) => {
// Here you can store you results into the state
this.setState({
sentiment_types: ['positive', 'negative'];
})
})
.catch(err => {
console.log(err);
})
console.log(result)
})
.catch(err => {
console.log(err)
})
}
render() {
<childComponent sentiment_types={this.state.sentiment_types} />
}
}
const childComponent = (props) => {
if(props.state.sentiment_types.includes('positive')){
return <h1>oke</h1>
}
}

how to get data in the reactJS using axios?

I have reactJS class when I get a data in JSON and try to trasfrom it for html:
class ProjectList extends React.Component {
constructor(props) {
super(props);
this.state = {projects: []};
}
componentDidMount() {
axios
.get('http://localhost:8080/project')
.then(res => this.setState({ projects: res.data.name }))
.catch(err => console.log(err))
}
render() {
return (
this.state.projects.map((project) => {
return (
<div>
<p> {project.name} </p>
</div>
)
})
)
}
}
ReactDOM.render(
<ProjectList />,
document.getElementById('root')
);
I can undestand what is problem because have some problem with reactJS
Looks like you are storing res.data.name instead of res.data. Meaning this.state.projects is getting set to a single name string. You should be setting this.state.projects to res.data to get the full array of projects:
componentDidMount() {
axios
.get('http://localhost:8080/project')
.then(res => this.setState({ projects: res.data }))
.catch(err => console.log(err))
}

Categories