Having trouble rendering items in React.js - html

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

Related

Trying to retrive JSON Array through fetch in react native but JSON parse error:Unreconised token "<"

In the code,i am fetching the token from expo-secure-store,later fetching the API data from fetchdata function.But unfortunately error "unrecognized token" is displayed.
After the error is displayed,the API call returns JSON Array data.Unable to do data map in react native to TradeCard Component.
import { StatusBar } from 'expo-status-bar';
import {React,useState,useEffect} from 'react';
import TradeCard from './TradeCard';
import { StyleSheet, Text, View,TextInput,TouchableOpacity,ScrollView,ActivityIndicator } from 'react-native';
import * as SecureStore from 'expo-secure-store';
export default function Trades()
{
const [ data,setData] = useState([]);
const [ isLoading,setLoading] = useState(true);
const [user,setUser] = useState('');
const [token,setToken] = useState('');
const fetchTokens = async () => {
try {
const user = await SecureStore.getItemAsync('user');
const token = await SecureStore.getItemAsync('token');
setUser(user);
setToken(token);
if (user && token)
{
fetchData();
}
else
{
}
} catch (e) {
alert('Error',e);
}
}
useEffect(()=>{ fetchTokens()},[]);
const fetchData = async () => {
setLoading(true);
fetch('https://tradecoaster.herokuapp.com/api/v1/listTrades/'+user+'/',
{
method:'GET',
headers:{
'Accept':'application/json',
'Content-Type':'application/json',
'Authorization':'token '+token
}
})
.then(res => res.json())
.then((res)=>{
console.log('Data',res);
setData(res);
setLoading(false);
})
.catch((error)=>{
setLoading(false);
alert(error);
console.error("Error",error);
});
}
return(
<ScrollView>
<View>
{isLoading && data.length==0 ? <ActivityIndicator size="large" color="#0000ff" /> :
<Text>No Trades</Text>
}
</View>
</ScrollView>
);
}```

Change number of servings on click (React Hooks - API)

I'm working on a recipe site using API from a third party and want to change the number of servings (which is output from the API data) when clicking the + & - button. I tried assigning the output serving amount <Servings>{recipe.servings}</Servings> in a variable and useState to update it but it kept showing errors. I would appreciate any help (preferably using react Hooks). Thanks :)
Here is my code:
const id = 716429;
const apiURL = `https://api.spoonacular.com/recipes/${id}/information`;
const apiKey = "34ac49879bd04719b7a984caaa4006b4";
const imgURL = `https://spoonacular.com/cdn/ingredients_100x100/`;
const {
data: recipe,
error,
isLoading,
} = useFetch(apiURL + "?apiKey=" + apiKey);
const [isChecked, setIsChecked] = useState(true);
const handleChange = () => {
setIsChecked(!isChecked);
};
return (
<Section>
<h2>Ingredients</h2>
<ServingsandUnits>
{recipe && (
<ServingsIncrementer>
<p>Servings: </p>
<Minus />
<Servings>{recipe.servings}</Servings>
<Plus />
</ServingsIncrementer>
)}
<ButtonGroup>
<input
type="checkbox"
id="metric"
name="unit"
checked={isChecked}
onChange={handleChange}
/>
<label htmlFor="male">Metric</label>
</ButtonGroup>
</ServingsandUnits>
</Section>
};
My custom hook is called useFetch:
const useFetch = (url) => {
const [data, setData] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const abortCont = new AbortController();
fetch(url, { signal: abortCont.signal })
.then((res) => {
if (!res.ok) {
// error coming back from server
throw Error("Could not fetch the data for that resource");
}
return res.json();
})
.then((data) => {
setIsLoading(false);
setData(data);
setError(null);
})
.catch((err) => {
if (err.name === "AbortError") {
console.log("Fetch aborted");
} else {
// auto catches network / connection error
setIsLoading(false);
setError(err.message);
}
});
return () => {
abortCont.abort();
};
}, [url]);
return { data, isLoading, error };
};
export default useFetch;

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]);

access the data of a hook- React

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;

Component nesting in react with ES6 syntax

When working on a name card generator app, trying to extract information from address object (https://jsonplaceholder.typicode.com/users). Was told the best way to present the text is to put them in separate components, and . Now I am having troubles piecing the two components together. The code is down below.
(Complete set of new code will be appreciated!)
import React, { useState, useEffect } from 'react';
const Namecard = ({ name, email, address }) => {
return (
<div>
<p>{name}</p>
<p>{email}</p>
</div>
);
};
const Address = ({ street }) => {
return <h1>{street}</h1>;
};
function App() {
const [identis, setIdenti] = useState([]);
useEffect(() => {
getIdenti();
}, []);
const getIdenti = async () => {
const acquired = await fetch(`https://jsonplaceholder.typicode.com/users`);
const data = await acquired.json();
setIdenti(data);
};
return (
<div>
{identis.map(identi => (
<Namecard name={identi.name} email={identi.email}>
<Address street={identi.address.city} />
</Namecard>
))}
</div>
);
}
export default App;
So with the current code, the output only includes name and email. Nothing is shown from the address object.
Namecard needs to explicitly render its children, otherwise they are not rendered:
const Namecard = ({name,email,children}) => {
return (
<div>
<p>{name}</p>
<p>{email}</p>
{children}
</div>
)
}
You use Address component as a children of Namecard component, so you should tell Namecard component that use this children component and render in his body.
For example you could rewrite Namecard component like that:
const Namecard = ({name, email, address, children}) => {
return (
<div>
<p>{name}</p>
<p>{email}</p>
{children}
</div>
)
};
and as a children our component render <Address street={identi.address.city}/>
Or you could rewrite like that: render Address component inside Namecard:
const Namecard = ({name, email, address}) => {
return (
<div>
<p>{name}</p>
<p>{email}</p>
<Address street={address.city}/>
</div>
)
};
const Address = ({street}) => <h1>{street}</h1>;
function App() {
const [identis, setIdenti] = useState([]);
useEffect(() => {
getIdenti();
}, []
);
const getIdenti = async () => {
const acquired = await fetch(`https://jsonplaceholder.typicode.com/users`);
const data = await acquired.json();
setIdenti(data);
}
return (
<div>
{identis.map(identi => (
<Namecard
name={identi.name}
email={identi.email}
address={identi.address}
/>
))}
</div>
)
}