access the data of a hook- React - json

I am requesting this API, then I save the result in the "data" hook. when I want to print "data.total_results" everything ok but when I want to print a key that has more data inside it doesn't leave me "Data.results [0] .title"
import React, { useState, useEffect } from 'react';
const Movie = () => {
// Declara una nueva variable de estado, que llamaremos "count".
const [Data, setData] = useState("");
useEffect(() => {
const Cargar = async () => {
let respuesta = await fetch("https://api.themoviedb.org/3/discover/movie?api_key=ce322f54257cc9286282b320c5e9b2a0&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=1");
let respuestaJSON = await respuesta.json();
setData(respuestaJSON);
};
Cargar();
}, [Data]);
return (
<div>
{Data.total_results}
{/* {Data.results[0].title} */}
</div>
);
}
export default Movie;

You need to check if the results property is defined.
<div className="App">
<h1>Total {Data.total_results}</h1>
{typeof Data.results !== "undefined" &&
Data.results.map((movie, index) => {
return <h3>{movie.title}</h3>;
})}
</div>
Working Demo: https://codesandbox.io/s/distracted-feather-4l55r
A few days back, I have created a repository and implemented OMDB API with React. This may help you.
https://github.com/jogeshpi03/omdb-react

Your code is almost right. There's a bug and a couple improvements you can make.
First let's take a look at the useEffect hook.
useEffect takes two arguments: a function, and a list of dependencies. The first time the component is rendered, and when any of the dependencies change, the function argument will be executed again.
So right now you have a bug with your dependencies that is causing an infinite loop. When the component is rendered, you execute the function in useEffect. That function will eventually set Data. When the value for Data is set, that will cause the useEffect function to be executed again. Which will set a new value to Data, which will execute the function again, ad infinitum.
The fix is to set the dependencies to [] if you only want that to run once. Like this:
useEffect(() => {
const Cargar = async () => {
let respuesta = await fetch("https://api.themoviedb.org/3/discover/movie?api_key=ce322f54257cc9286282b320c5e9b2a0&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=1");
let respuestaJSON = await respuesta.json();
setData(respuestaJSON);
};
Cargar();
}, []); // <-------- This changed to []
I hope that fixes your issue.
Now I will suggest some improvements:
I would set the initial state of Data to undefined
const [Data, setData] = useState();
and then use a conditional in the JSX like so
if (!Data) {
return <div>Loading...</div>
}
return (
<div>
{Data.total_results}
</div>
);

const [Data, setData] = useState([]);
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import ReactTable from 'react-table'
import 'react-table/react-table.css'
const Movie = () => {
// Declara una nueva variable de estado, que llamaremos "count".
const [Data, setData] = useState([]);
useEffect(() => {
axios.get("https://api.themoviedb.org/3/discover/movie?api_key=ce322f54257cc9286282b320c5e9b2a0&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=1")
.then(function(response) {
console.log(response.data.results)
setData(response.data.results);
}).catch(function(error) {
console.log(error);
})
}, []);
return (<div>
{Data.map(function (row, i) {
return <div key={i}>
{row.title}
</div>
})}
</div>)
}
export default Movie;

Related

Having trouble rendering items in React.js

I'm pretty new to React, being very used to OOP this is kind of twisting my brain a bit!
I'm playing around with the PokeAPI, to show different stats of pokemon, I've gotten all my data into the array fine, I've just hit a brick wall when it comes to rendering it, nothing apears.
index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import PokemonRender from './PokemonRender';
ReactDOM.render(
<React.StrictMode>
<PokemonRender />
</React.StrictMode>,
document.getElementById('root')
);
PokemonRender:
import React, { useEffect } from 'react';
import axios from 'axios';
const PokemonRender = () => {
const pokemonList = [];
const getPokemonData = async (id) => {
try{
const dataArray = [];
const url = 'https://pokeapi.co/api/v2/pokemon/' + id;
const res = await axios.get(url);
dataArray.push(res.data);
pokemonList.push(dataArray[0]);
dataArray.length = 0;
} catch(e) {
console.log(e);
}
}
const Pokemon = ({ id, name }) => (
<div>
<p>{id}</p>
<p>{name}</p>
</div>
)
useEffect((i) => {
for(i = 1; i < 152; i++) {
getPokemonData(i);
} }, []);
return (
pokemonList.map((pokemon) => (
<Pokemon id={pokemon.id} name={pokemon.name} />
))
)
}
export default PokemonRender;
edit:
I've boiled it down to this, the arrays all contain the right data (which they didn't before) but the return statement isn't returning what it should? Any ideas?
import React, { useEffect, useState } from 'react';
const Pokemon = ({ id, name }) => (
<div>
<p>{id}</p>
<p>{name}</p>
</div>
);
const PokemonRender = () => {
const [pokemonList, setPokemonList] = useState([]);
const tempPokemonArray = []
const getPokemonData = async (id) => {
try {
fetch("https://pokeapi.co/api/v2/pokemon/" + id).then(res => res.json()).then((pokemon) => tempPokemonArray.push(pokemon))
} catch (e) {
console.log(e);
}
};
useEffect((i) => {
for (i = 1; i < 5; i++) {
getPokemonData(i)
}
setPokemonList(tempPokemonArray);
tempPokemonArray.length = 0;
}, []);
return pokemonList.map((pokemon) => <Pokemon id={pokemon.id} name={pokemon.name}/>);
};
export default PokemonRender;
You need to use the state to handle data inside the component. You can do it by following this link.
By the way, you should not getPokemonData in the loop like that. It will re-render your component 151 times.
You need to call useState to make a re-rendering. Without it, React won't understand when to re-render your UI. In your case, you need to update pokemon list with useState
Did a small refactoring for your code which are removing dataArray, fixing useEffect, and moving your Pokemon component outside of PokemonRender to avoid calling it many times
import React, { useEffect, useState } from "react";
import axios from "axios";
const Pokemon = ({ id, name }) => (
<div>
<p>{id}</p>
<p>{name}</p>
</div>
);
const PokemonRender = () => {
const [pokemonList, setPokemonList] = useState([]);
const getPokemonData = async (id) => {
try {
const url = "https://pokeapi.co/api/v2/pokemon/" + id;
const res = await axios.get(url);
//pokemonList.push(res.data);
setPokemonList([...pokemonList, res.data]); //add current data to the pokemon list
} catch (e) {
console.log(e);
}
};
useEffect(() => {
const blankArray = new Array(152);
for (const i = 1; blankArray.length < 152; i++) {
getPokemonData(i);
}
}, []);
return pokemonList.map((pokemon) => (
<Pokemon id={pokemon.id} name={pokemon.name} />
));
};
export default PokemonRender;
useState is also asynchronous, so I think you keep updating data and UI at the same time which will make your data unsafe (maybe lost during re-rendering). So you should return data in a new array and then render all of them together. For example
const getPokemonData = async (id) => {
try{
const url = 'https://pokeapi.co/api/v2/pokemon/' + id;
const res = await axios.get(url);
dataArray.push(res.data);
return res.data
} catch(e) {
console.log(e);
}
}
Update useEffect with a list of pokemon
useEffect(() => {
const blankArray = new Array(152)
for(const i = 1; blankArray.length < 152; i++) {
pokemonListData.push(getPokemonData(i));
}
setPokemonList(pokemonListData) //only run once
}, []);
Since you're new to these stuff, I'd suggest you follow up this document

How can I make a search box in react-hooks?

I'm making a cocktail recipe web. If I search for the name of the cocktail, I want the cocktail to appear. The error message shown to me is as follows.
"TypeError: Cannot read property 'filter' of undefined"
Please tell me how to solve this problem. I'm a beginner. Is there a problem with my code?
This is Search.jsx
import React, { useState, useEffect } from "react";
import useFetch from "../Components/useFetch";
const Searchs = () => {
const url =
"https://www.thecocktaildb.com/api/json/v1/1/search.php?s=margarita";
const [data] = useFetch(url);
const [searchTerm, setSearchTerm] = useState("");
const [searchResults, setSearchResults] = useState([]);
const handleChange = (event) => {
setSearchTerm(event.target.value);
};
useEffect(() => {
const results = data.drinks.filter(({ strDrink }) =>
data.strDrink.toLowerCase().includes(searchTerm)
);
setSearchResults(results);
}, [searchTerm]);
return (
<Wrapper>
<Search
type="text"
placeholder="재료 또는 이름을 검색하세요"
value={searchTerm}
onChange={handleChange}
/>
<ul>
{searchResults.map((item) => (
<li>{item}</li>
))}
</ul>
</Wrapper>
);
};
export default Searchs;
This is useFetch.jsx
import { useState, useEffect } from "react";
function useFetch(url) {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
async function fetchUrl() {
const response = await fetch(url);
const json = await response.json();
setData(json);
setLoading(false);
}
useEffect(() => {
fetchUrl();
}, []);
return [data, loading];
}
export default useFetch;
This is JSON
{
"drinks": [
{
"idDrink": "12784",
"strDrink": "Thai Iced Coffee",
"strCategory": "Coffee / Tea",
"strIBA": null,
"strAlcoholic": "Non alcoholic",
"strGlass": "Highball glass",
"strDrinkThumb": "https://www.thecocktaildb.com/images/media/drink/rqpypv1441245650.jpg",
"strIngredient1": "Coffee",
"strIngredient2": "Sugar",
"strIngredient3": "Cream",
"strIngredient4": "Cardamom",
"strMeasure1": "black",
"strMeasure3": " pods\n",
"strImageAttribution": null,
"strCreativeCommonsConfirmed": "No",
"dateModified": "2015-09-03 03:00:50"
}
]
}
Do null check before filter(), Your API might return null/undefined you should handle such cases.
Bonus: onChange={handleChange} don't directly call API on change, add some denounce check, to improve performance.
useEffect(() => {
const results = data?.drinks?.filter(({ strDrink }) =>
data.strDrink.toLowerCase().includes(searchTerm)
) ?? [];
setSearchResults(results);
}, [searchTerm]);
you did many mistakes in this code look below how I did it
here you can find sandbox URL where you can see live working code
https://codesandbox.io/s/boring-tesla-hoc11?file=/src/App.js:75-1141
I have changed your wrapper to input element for testing you can revert it back
const Searchs = () => {
const url =
"https://www.thecocktaildb.com/api/json/v1/1/search.php?s=margarita";
const [searchTerm, setSearchTerm] = useState("");
const [searchResults, setSearchResults] = useState([]);
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const handleChange = (event) => {
setSearchTerm(event.target.value);
};
useEffect(() => {
async function fetchUrl() {
const response = await fetch(url);
const json = await response.json();
setData(json);
setLoading(false);
const results = data.drinks.filter(({ strDrink }) =>
strDrink.toLowerCase().includes(searchTerm)
);
setSearchResults(results);
}
fetchUrl();
}, [searchTerm]);
return (
<>
<input
type="text"
placeholder="재료 또는 이름을 검색하세요"
value={searchTerm}
onChange={handleChange}
/>
<ul>
{searchResults.map((item,index) => (
<li key={index}>{item.strDrink}</li>
))}
</ul>
</>
);
};
It seems like your API is returning nothing. You should add a check to see if anything is returned from API:
ALSO: you have to include data which you get from useFetch to the useEffect dependencies, otherwise it's value won't be changed in each useEffect call:
useEffect(() => {
const results = data?.drinks?.filter(({ strDrink }) =>
data.strDrink.toLowerCase().includes(searchTerm)
) ?? [];
setSearchResults(results);
}, [searchTerm, data]);

React not being able to acess a query value

Hey i am trying to access whats inside of a fetch trought item.name and so on..
I would like to know why i am not being able to access the item properties
However i am not getting anythign back in the return statement, this is the json i need to access.
{"message":{"_id":"5ea9c860ea9fb600178ae676","name":"frutology","description":"frutas"}}
import React, {useState,useEffect} from 'react';
function StoreDetails ({ match }) {
useEffect(()=>{
fetchItem();
},[]);
const[item,setItem]=useState({name: "", description : ""});
const fetchItem = async() => {
const fetchItem = await fetch(
`/storedisplay/${match.params.id}`
);
const item = await fetchItem.json();
setItem(item);
console.log(item);
}
return (
<div>
<h1>{item.name}</h1>
<h2> {item.description}</h2>
</div>
);
}
export default StoreDetails;
Keep in mind that useState is asynchronous.
Use useEffect.
useEffect(() => {
console.log(item);
}, [item])

How can I turn a function used in many components into its own component which I can reuse across the app?

I have a fetch request used on multiple pages, and would like to turn it into a component to simply call in whenever it's needed. This is proving to be harder than I thought, and it's bring up a number of issues.
I have tried using the wrappedComponent function but not sure if that's the solution as it's still not working. It's now saying that the fetchPosts class constructor cannot be invoked without new.
const that = this;
fetch ('/testrouter')
.then (response => {
return response.json();
}).then(jsonData => {
that.setState({posts:jsonData})
}).catch(err => {
console.log('Error fetch posts data '+err)
});
}
This is what I want to turn into a component, so that I can just call it by it's name from another one inside componentDidMount. I have tried doing this:
function fetchPosts(WrappedComponent) {
class FetchPosts extends Component {
constructor(props) {
super(props)
this.state = {
posts: []
}
}
fetchAllPosts() {
const that = this;
fetch ('/testrouter')
.then (response => {
return response.json();
}).then(jsonData => {
that.setState({posts:jsonData})
}).catch(err => {
console.log('Error fetch posts data '+err)
});
}
render() {
return (<WrappedComponent
fetchAllPosts = {this.fetchAllPosts})
/>);
}
}
return FetchPosts;
}
export default fetchPosts
Then importing it and calling it with fetchPosts but it's not working.
I was hoping I would be able to create a component, add the code then import the component, but this is not working.
You might want to create a custom hook to do this:
useFetch.jsx
import React, { useState, useEffect } from 'react'
const useFetch = (url) =>
const [state, setState] = useState({ loading: true, data: null, error: null })
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(data => setState(state => ({ ...state, loading: false, data }))
.catch(error => setState(state => ({ ...state, loading: false, error }))
},[])
return state
}
export default useFetch
MyComponent.jsx
import React from 'react'
import useFetch from './useFetch.jsx'
const MyComponent = () => {
const data = useFetch('/testrouter')
return (<>
{ data.loading && "Loading..." }
{ data.error && `There was an error during the fetch: {error.message}` }
{ data.data && <Posts posts={data.data}/> }
</>)
}

Making an API call in React

I am trying to make an API call in React to return JSON data but I am a bit confused on how to go about this. My API code, in a file API.js, looks like this:
import mockRequests from './requests.json'
export const getRequestsSync = () => mockRequests
export const getRequests = () =>
new Promise((resolve, reject) => {
setTimeout(() => resolve(mockRequests), 500)
})
It is retrieving JSON data formatted like this:
{
"id": 1,
"title": "Request from Nancy",
"updated_at": "2015-08-15 12:27:01 -0600",
"created_at": "2015-08-12 08:27:01 -0600",
"status": "Denied"
}
Currently my code to make the API call looks like this:
import React from 'react'
const API = './Api.js'
const Requests = () => ''
export default Requests
I've looked at several examples and am still a bit confused by how to go about this. If anyone could point me in the right direction, it would be greatly appreciated.
EDIT: In most examples I've seen, fetch looks like the best way to go about it, though I'm struggling with the syntax
Here is a simple example using a live API (https://randomuser.me/)... It returns an array of objects like in your example:
import React from 'react';
class App extends React.Component {
state = { people: [], isLoading: true, error: null };
async componentDidMount() {
try {
const response = await fetch('https://randomuser.me/api/');
const data = await response.json();
this.setState({ people: data.results, isLoading: false });
} catch (error) {
this.setState({ error: error.message, isLoading: false });
}
}
renderPerson = () => {
const { people, isLoading, error } = this.state;
if (error) {
return <div>{error}</div>;
}
if (isLoading) {
return <div>Loading...</div>;
}
return people.map(person => (
<div key={person.id.value}>
<img src={person.picture.medium} alt="avatar" />
<p>First Name: {person.name.first}</p>
<p> Last Name: {person.name.last}</p>
</div>
));
};
render() {
return <div>{this.renderPerson()}</div>;
}
}
export default App;
Does it make sense? Should be pretty straight forward...
Live Demo Here: https://jsfiddle.net/o2gwap6b/
You will want to do something like this:
var url = 'https://myAPI.example.com/myData';
fetch(url).then((response) => response.json())
.then(function(data) { /* do stuff with your JSON data */})
.catch((error) => console.log(error));
Mozilla has extremely good documentation on using fetch here that I highly recommend you read.
The data parameter in the second .then will be an object parsed from the JSON response you got and you can access properties on it by just using the property label as was in the JSON. For example data.title would be "Request from Nancy".
If you are struggling with fetch, Axios has a much simpler API to work with.
Try this in your API.js file (of course install axios first with npm i --save axios):
import axios from 'axios'
import mockRequests from './requests.json'
export const getRequests = (url) => {
if (url) {
return axios.get(url).then(res => res.data)
}
return new Promise((resolve, reject) => { // you need to return the promise
setTimeout(() => resolve(mockRequests), 500)
})
})
In your component, you can access the getRequests function like so
import React, { Component } from 'react'
import { getRequests } from './API.js'
class App extends Component {
state = {
data: null
}
componentWillMount() {
getRequests('http://somedomain.com/coolstuff.json').then(data => {
console.log(data)
this.setState({ data })
})
}
render() {
if (!this.state.data) return null
return (
<div className='App'>
{this.state.data.title}
</div>
)
}
}
export default App