I'm very new to JS and ReactJS and I try to fetch an endpoint which gives me a JSON Object like this :
{"IDPRODUCT":4317892,"DESCRIPTION":"Some product of the store"}
I get this JSON Object by this endpoint :
http://localhost:3000/product/4317892
But I dont how to use it in my react application, I want to use those datas to display them on the page
My current code looks like this but it's not working and I'm sure not good too :
import React, {Component} from 'react';
class Products extends Component {
constructor(props){
super(props);
this.state = {
posts: {}
};
};
componentWillMount() {
fetch('http://localhost:3000/product/4317892')
.then(res => res.json())
.then(res => {
this.setState({
res
})
})
.catch((error => {
console.error(error);
}));
}
render() {
console.log(this.state)
const { postItems } = this.state;
return (
<div>
{postItems}
</div>
);
}
}
export default Products;
In the console.log(this.state) there is the data, but I'm so confused right now, dont know what to do
Since I'm here, I have one more question, I want to have an input in my App.js where the user will be able to type the product's id and get one, how can I manage to do that ? Passing the data from App.js to Products.js which is going to get the data and display them
Thank you all in advance
Your state doesn't have a postItems property which is considered undefined and react therefore would not render. In your situation there is no need to define a new const and use the state directly.
Also, when you setState(), you need to tell it which state property it should set the value to.
componentWillMount() {
fetch('http://localhost:3000/product/4317892')
.then(res => res.json())
.then(res => {
this.setState({
...this.state, // Not required but just a heads up on using mutation
posts: res
})
})
.catch((error => {
console.error(error);
}));
}
render() {
console.log(this.state)
return (
<div>
<p><strong>Id: {this.state.posts.IDPRODUCT}</strong></p>
<p>Description: {this.state.posts.DESCRIPTION}</p>
</div>
);
}
I have got 3 names for the same thing in your js: posts, postItems and res.
React can not determine for you that posts = postItems = res.
So make changes like this:
-
this.state = {
postItems: {}
};
-
this.setState({
postItems: res
});
-
return (
<div>
{JSON.stringify(postItems)}
<div>
<span>{postItems.IDPRODUCT}</span>
<span>{postItems.DESCRIPTION}</span>
</div>
</div>
);
{postItems["IDPRODUCT"]}
Will display the first value. You can do the same for the other value. Alternatively, you can put
{JSON.stringify(postItems)}
With respect to taking input in the App to use in this component, you can pass that input down through the props and access it in this component via this.props.myInput. In your app it'll look like this:
<Products myInput={someInput} />
Related
Goal:
Get the second data by pressing the button and the list should display in the html page. It should replace the old data with a new data.
Problem:
I cannot make the button to be working in order to display the result on the webpage. Show new data and replace the old data.
What part am I missing?
Info:
*I'm newbie in Reactjs
Stackblitz:
https://stackblitz.com/edit/react-fetch-data-from-api-f632fh?
Thank you!
import React, { Component } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import './style.css';
class App extends Component {
constructor() {
super();
this.state = {
name: 'React',
data: null
};
}
componentDidMount = () => {
fetch('https://jsonplaceholder.typicode.com/users')
.then(res => res.json())
.then(data => {
console.log(data);
this.setState({ data });
});
};
getSecondData() {
fetch('https://fakerestapi.azurewebsites.net/api/v1/Activities')
.then(res => res.json())
.then(data => {
console.log(data);
this.setState({ data });
});
}
showAlert() {
alert("I'm an alert");
}
render() {
return (
<div>
<button onClick={this.getSecondData}>ASDF</button>
<p>Start editing to see some magic happen :)</p>
<ul>
{this.state.data &&
this.state.data.map(user => <li id={user.id}>{user.id}</li>)}
</ul>
</div>
);
}
}
render(<App />, document.getElementById('root'));
You were missing a couple of things.
First of all, you should never setState like that. Instead, always use prevState. Like this:
this.setState(prevState => ({
...prevState,
data
}));
Second, you either need to bind the function you are creating in the constructor like this: this.getSecondData = this.getSecondData.bind(this)
You read more about that here: https://reactjs.org/docs/handling-events.html
Or better solution is to just use arrow functions.
The correct working solution can be found here:
https://stackblitz.com/edit/react-fetch-data-from-api-znjdf6
I'm trying to get some data from the github api, but I'm getting duplicate <Card /> components in my output. Here's the code of my App.js. My card component seems to be working fine.
import logo from './logo.svg';
import './App.css';
import React from 'react';
import Card from './Card';
import axios from 'axios';
class App extends React.Component {
state = {
users: [],
// Enter some github usernames for "followers"
followers: ["abc", "def", "ghi", "jkl", "mnop"]
}
componentDidMount() {
// Use initial github username for mainUser
axios.get(`https://api.github.com/users/mainUser`)
.then((resp)=> {
console.log(resp);
this.setState({
users: [...this.state.users, resp]
});
})
.catch(err=> console.log(err));
this.state.followers.map((user) => {
return(axios.get(`https://api.github.com/users/${user}`)
.then((resp)=> {
console.log(resp);
console.log(this.state.users);
this.setState({
users: [...this.state.users, resp]
});
}));
})
}
render() {
return(
<div className="container">
{
this.state.users.map(user => (
<Card key={Date.now()} user={user} />
))
}
</div>)
}
}
export default App;
I assume I'm failing to understand something about the lifecycle.
I think the main reason why it's failing for you is that you are using Date.now() as your key when rendering <Card /> components.
Keys used within arrays must be unique among their siblings (in this case, <Card /> components). This way React knows which items have been added/removed or changed. Plus, it prevents the unexpected behaviour that you are seeing.
Since the users that are returned from GitHub API include id, you could simply use that for your key. This way, React would render your <Card /> components without duplicates.
Also, in your axios request, instead of trying to add the whole response object to your user's state, try and destructure the data from the response and assign that to your users state. This way you will get only the user data that you need.
Plus, it's a good practice to use the previous state from setState() when assigning new state rather than getting the current state. This way you can be sure that the users state will have all the previous users and the new user that you are concatenating.
Taken all of this into account, the state assignment could look something like this:
.then(({ data }) => {
this.setState((prevState) => ({
users: [...prevState.users, data]
}));
})
As mentioned above, Date.now() makes for a poor component key.
You could also tidy up your Axios requests, including using the response data instead of the entire response object. Something like this
componentDidMount() {
const allUsers = ["mainUser", ...this.state.followers]
Promise.all(allUsers.map(user =>
axios.get(`https://api.github.com/users/${encodeURIComponent(user)}`)
.then(({ data }) => data)
)).then(users => {
this.setState({ users })
}).catch(err => console.error(err))
}
render() {
return(
<div className="container">
{
this.state.users.map(user => (
<Card key={user.id} user={user} />
))
}
</div>
)
}
I am using fetch to get API data that I'm using to create a drop down that I will add routes too. I've done this a couple of times before but I used axios previously but I just wanted to get familiar with fetch as well. Can anyone see the problem of why map would be undefined?
import React, { Component } from 'react'
class Fetchheroes extends Component {
constructor() {
super();
this.state = {
heroes: [],
}
}
componentDidMount(){
fetch('https://api.opendota.com/api/heroStats')
.then(results => {
return results.json();
}).then(data =>{
let heroes = data.results.map((hero) =>{
return(
<div key={hero.results}>
<select>
<option>{hero.heroes.localized_name}</option>
</select>
</div>
)
})
this.setState({heroes: heroes});
console.log("state", this.state.heroes);
})
}
render(){
return(
<div>
<div>
{this.state.heroes}
</div>
</div>
)
}
}
export default Fetchheroes
You have a bad mapping about data. You need to use data instead of data.result and you have a bad key value because results are not unique key in that case. You also don't need your hero.heroes.localized_name just hero.localized_name. I made an example in codesandbox.
https://codesandbox.io/s/clever-hodgkin-7qo6p
Edit
I made another example when I put all records to one select, not for multiple selects, maybe is that what you need or someone else :).
https://codesandbox.io/s/bold-grass-gv0wc
In my react app I have a main App component where I fetched data from api in componentDidMount method and saved it in its state. Then I passed that state to another component in App. However, when I consume that data from prop it is showing undefined.
Another strange this I didn't is when I console.log state in App components render method them first I get an empty array then after a second another array with the data in it. Please help me here
The code goes like this-
class App extends React.Component {
constructor() {
super();
this.state = {
data: []
};
}
componentDidMount() {
this.fetchData();
}
fetchData() {
fetch(
`https://api.themoviedb.org/3/movie/popular?api_key=${
process.env.REACT_APP_API_KEY
}&language=en-US&page=1`
)
.then(res => res.json())
.then(data => {
this.setState({ data });
})
.catch(err => console.log(err));
}
render() {
console.log(this.state.data);
return (
<div>
<Movie title="Popular" results={this.state.data} />
</div>
);
}
}
export default App;
this.state.data is undefined in Movie component which is like this
function Movie(props) {
return (
<p>{props.data.results[0].title}</p>
)
}
You are passing results and title as the props to the <Movie> component yet trying to fetch data prop.
component:
<Movie title="Popular" results={this.state.data} />
So you need to fetch the results prop, not the data one.
fixed:
function Movie(props) {
return (
<p>{props.results[0].title}</p>
)
}
additionaly:
If you're already passing the title prop, why not just use that prop for the title?
function Movie(props) {
return (
<p>{props.title}</p>
)
}
Your prop is results so you need to reference that in your component:
function Movie(props) {
return (
<p>{props.results[0].title}</p>
)
}
In your App component you might also want to add a check in render to show a loading message or spinner if the data load hasn't been resolved when the component initially renders:
if (!this.state.data.length) return <Spinner />;
I'm pretty new in React and need some help.
I wanted display data from a movie database based on the search term. I'm using fetch inside my getMovies methode to get the data. The data is stored in data.Search but I don't know how to access it and store it in a variable.
class DataService
{
getMovies (searchTerm) {
//let dataStorage; //store somehow data.Search inside
fetch("http://www.omdbapi.com/?s=" + searchTerm, {
method: 'get'
})
.then((resp) => resp.json())
.then(function(data) {
return data.Search;
}).catch(function(error) {
console.log(error);// Error :(
});
}
//return dataStorage; //return data.Search
}
The below code is the correct react's way for your case, as simple as this:
import React from 'react';
export default class DataService extends React.Component {
constructor(props) {
super(props);
this.state = {
search_data: [], //for storing videos
};
this.getMovies = this.getMovies.bind(this); //bind this function to the current React Component
}
getMovies (searchTerm) {
fetch("http://www.omdbapi.com/?s=" + searchTerm, {
method: 'get'
})
.then((resp) => resp.json())
.then((data) => { //so that this callback function is bound to this React Component by itself
// Set state to bind search results into "search_data"
// or you can set "dataStorage = data.Search" here
// however, fetch is asynchronous, so using state is the correct way, it will update your view automatically if you render like I do below (in the render method)
this.setState({
search_data: data.Search,
});
});
}
componentDidMount() {
this.getMovies(); //start the fetch function here after elements appeared on page already
}
render() {
return (
{this.state.search_data.map((video, i) =>{
console.log(video);
// please use the correct key for your video below, I just assume it has a title
return(
<div>{video.title}</div>
)
})}
);
}
}
Feel free to post here any errors you may have, thanks
There are several ways of doing asynchronous tasks with React. The most basic one is to use setState and launch a Promise in the event handler. This might be viable for basic tasks but later on, you will encounter race conditions and other nasty stuff.
In order to do so, your service should return a Promise of results. On the React side when the query changes, the service is called to fetch new results. While doing so, there are few state transitions: setting loading flag in order to notify the user that the task is pending and when the promise resolves or rejects the data or an error is stored in the component. All you need is setState method.
More advanced techniques are based on Redux with redux-thunk or redux-saga middlewares. You may also consider RxJS - it is created especially for that kind of stuff providing debouncing, cancellation and other features out of the box.
Please see the following example of simple search view using yours DataService.
class DataService
{
//returns a Promise of search results, let the consumer handle errors
getMovies (searchTerm) {
return fetch("http://www.omdbapi.com/?s=" + searchTerm, {
method: 'get'
})
.then((resp) => resp.json())
.then(function(data) {
return data.Search;
})
}
}
const SearchResults = ({data, loading, error}) =>
<div>
{
data && data.map(({Title, Year, imdbID, Type, Poster}) =>
<div key={imdbID}>
{Title} - {Year} - {Type}
<img src={Poster} />
</div>
)
}
{loading && <div>Loading...</div>}
{error && <div>Error {error.message}</div>}
</div>
class SearchExample extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this)
this.state = {
data: [],
loading: false
};
}
handleChange(event) {
const service = new DataService();
//search query is target's value
const promise = service.getMovies(event.target.value);
//show that search is being performed
this.setState({
loading: true
})
//after the promise is resolved display the data or the error
promise.then(results => {
this.setState({
loading: false,
data: results
})
})
.catch(error => {
this.setState({
loading: false,
error: error
})
})
}
render() {
return (
<div>
<input placeholder="Search..." onChange={this.handleChange} type="search" />
<SearchResults data={this.state.data} loading={this.state.loading} error={this.state.error} />
</div>
)
}
}
ReactDOM.render(<SearchExample />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>