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>
)
}
Related
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
I am using nextjs. I need to render custom landing pages according to their specific url. I am able to render all the details from the database of that particular URL except for the object which contains the details for the page. The pages has been built with the help of grapesjs.
Following is the data in db:
Following is code for rendering the list of the pages:
index.js
import Link from "next/link"
export const getStaticProps = async () => {
const res = await fetch("http://localhost:5000/api/webpage/");
const data = await res.json();
return {
props: {
data,
}
};
};
const blog = ({ data }) => {
return (
<div>
{data?.map((currentElement) => {
return (
<div key={currentElement.id} className="ssr-styles">
<h3>
{/* {currentElement._id} */}
<Link href={`/blog/${currentElement.url}`}>
{currentElement.name}
</Link>
</h3>
</div>
);
})}
</div>
);
};
export default blog;
Following is the code where the page is actually rendering:
[pageno].js=>
export const getStaticPaths = async () => {
const res = await fetch("http://localhost:5000/api/webpage/");
const data = await res.json();
const paths = data.map((currentElement) => {
return {
params: { pageno: currentElement.url.toString() }
};
});
return {
paths,
fallback: false
};
};
export const getStaticProps = async (context) => {
const url = context.params.pageno;
const res = await fetch(`http://localhost:5000/api/webpage/url/${url}`);
const data = await res.json();
return {
props: {
data
}
};
};
export const Details = ({ data }) => {
return (
<>
<div key={data.url} className="ssr-styles">
{data._id}
<h3>{data.name}</h3>
</div>
</>
);
};
export default Details;
how do I render the html inside the object content so as to get a proper webpage?
you can try using html-react-parser library. it converts an HTML string to React elements.
Why am I asking this question (main return) is finsied, but board.writer is not drawing. how can i drawing this object
if my question is wrong way plz tell me . i will fix it
i tried this way
first use map function
make like this function renderUser = ({board_SEQ, content, writer,subject}) => { parameter}
const Home = () => {
const initboard = {
board_SEQ: '',
writer: '',
content: '',
subject: ''
};
const [boards, setBoard] = useState([]);
const load = () => {
axios.get('http://www.heon.shop:8080/api/boards')
.then((res) => {
const data = res.data;
setBoard(boards => [...boards, {
board_SEQ: (data[0].board_SEQ),
writer: data[0].writer,
content: data[0].content,
subject: data[0].subject
}]);
})
.catch((error) => {
console.log("error" + error);
}
);
}
useEffect(() => {
load();
console.log("useEffect");
}, []);
const renderUser = ({board_SEQ, content, writer, subject}) => {
return (
<div>
<li>he</li>
<li>{board_SEQ}</li>
<li>{content}</li>
<li>{writer}</li>
<li>{subject}</li>
</div>
);
}
return (
<div>
{boards.map(board => {
<h1> {board.writer}</h1>
})}
</div>
);
}
export default Home;
Maybe you can try like this,
<div>
{boards.map(board => renderUser(board)}
</div>
if you want to render item directly using (add return to the map):
<div>
{boards.map(board => {
return <h1> {board.writer}</h1>;
})}
</div>
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]);
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;