I am using react app. I fetched one data from open api. in that api the ingredient in one single string but divided by \n1. When I fetched the data it came like this one single string and the \n1 shows like (, divided). I wanted to put the ingredients in the ul li elements or and comma-a after each ingredient. I tried lots of ways to split the data and also tried to add a comma after each word but it did not work. I shared my code in codesandbox.
This is my code
import React, { useEffect } from "react";
import "./styles.css";
export default function App() {
const [state, setState] = React.useState([]);
useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
const response = await fetch("https://sampleapis.com/recipes/api/recipes");
const data = await response.json();
setState(data);
};
return (
<div className="App">
{state.map((recipe) => {
return (
<>
<div key={recipe.id}>
<h1>{recipe.title}</h1>
<p>{recipe.ingredients}</p>
</div>
</>
);
})}
</div>
);
}
try to split and mapping them in separate html elements
<div className="App">
{state.map((recipe) => {
return (
<div key={recipe.id}>
<h1>{recipe.title}</h1>
<div>{recipe.ingredients?.split("\n").map(ingre=><p>{ingre}</p>)}</div>
</div>
);
})}
</div>
I found solution and did it like this:
const ingre = newRecipe.ingredients // this is the data
var result = ingre.split(',')
.map(word => `${word.trim()}`)
.join(', ');
Related
I want to use useEffect(on mount) to fetch from API and store it in useState. Fetch API is used to get the data. The problem is when initial page loading and also when I reload the page, it outputs an error called test.map is not a function. Why this happening and how to avoid this ?
import { useEffect, useState } from 'react';
function App() {
const[test, setTest] = useState({})
useEffect(() => {
testfunc()
}, [])
async function testfunc(){
let api = await fetch('https://jsonplaceholder.typicode.com/users')
let apijson = await api.json()
setTest(apijson)
}
return (
<div className="App">
{
test.map((item) => {
return(
<div>
{item.name}
</div>
)
})
}
</div>
);
}
export default App;
You can't map on an object {}, so you should need to define an array [] for the base state :
const[test, setTest] = useState([])
You have to change {} to array first to be able to map over it. You can easily place ? after test like this. or make in the default value of the state a default value for item name. because this error results as you map over an empty object.
import { useEffect, useState } from 'react';
function App() {
const[test, setTest] = useState([{name:"default"}])
useEffect(() => {
testfunc()
}, [])
async function testfunc(){
let api = await fetch('https://jsonplaceholder.typicode.com/users')
let apijson = await api.json()
setTest(apijson)
}
return (
<div className="App">
{
test?.map((item) => {
return(
<div>
{item.name}
</div>
)
})
}
</div>
);
}
export default App;
As already mentioned, you can't use the .map for objects.
Instead of this, you can make something like that
Object.keys(test).map(key => {
const currentSmth = test[key]
return(
<div>
{currentSmth.name}
</div>
)
})
})
I think it helps you to solve your problem.
Be careful using the correct data structures and methods.
This is the structure of the json being fetched. I am trying to render some of the nested threads data to a web page with react.
import react, {useState, useEffect} from "react";
import axios from 'axios'
import ReactJson from 'react-json-view'
const FeaturedBoards = () => {
const [boards, setBoards] = useState([{page: '', threads: {}}]);
useEffect(() => {
fetchBoards();
}, []);
const fetchBoards = () => {
axios.get('https://a.4cdn.org/po/catalog.json')
.then((res) => {
console.log(res.data);
setBoards(res.data);
})
.catch((err) => {
console.log(err);
});
};
if(boards === 0) {
return <div>Loading...</div>;
}
else{
return (
<div>
<h1>Featured Boards</h1>
<div className='item-container'>
{boards.map((board) => (
<div className='board' key={board.id}>
<p>{board['threads']}</p>
</div>
))}
</div>
</div>
);
}
};
export default FeaturedBoards;
I have tried everything to display some of the nested threads data but nothing comes up. I've tried doing a second call to map on board but no luck, storing it in a variable and calling from that still nothing. Am I doing something totally wrong?
I believe this is more fully answered by How can I access and process nested objects, arrays or JSON?. but to explain for this particular data structure, keep reading.
Look at your actual data... boards is an array. Each element in it is an object with page (int) and threads (array) properties. Each threads array element is an object with other properties. You can use map to iterate arrays and return a JSX representation of the objects within.
For example
const [boards, setBoards] = useState([]); // start with an empty array
const [loading, setLoading] = useState(true)
useEffect(() => {
fetchBoards().then(() => setLoading(false))
}, []);
const fetchBoards = async () => {
const { data } = await axios.get('https://a.4cdn.org/po/catalog.json')
setBoards(data)
}
return loading ? <div>Loading...</div> : (
<div>
<h1>Featured Boards</h1>
<div className="item-container">
{boards.map(board => (
<div className="board" key={board.page}> <!-- 👈 note "page", not "id" -->
{board.threads.map(thread => (
<p>{thread.name}</p>
<p>{thread.sub}</p>
<p>{thread.com}</p>
<!-- etc -->
))}
</div>
))}
</div>
</div>
)
I connected my react app to firebase and I think the problem is that the page loads before the data from my database is acquired, what can I do to delay the function until after it finishes acquiring the data?
function getPosts(){
db.collection("Posts").get().then(snapshot =>{
snapshot.docs.forEach(docs =>{
createPost(
docs.data().postName,
docs.data().createdAt,
docs.data().postContent,
)
})
})
}
getPosts();
function Blog(){
return (
<div>
<Navbar/>
<div className="container">
<div className="row" id="posts-collection">
</div>
</div>
</div>
)
}
export default Blog;
As MehmetDemiray already shows you can load data as an effect within a function component, but that answer assumes you only wish to track loading status.
If you want to use the data loaded to display the post data then you will also need to store the returned data.
const Blog: React.FunctionComponent = () => {
// state to store data returned by async call. (originally set to null).
const [posts, setPosts] = React.useState(null);
// use an effect to load async data.
// the effect only runs on component load and every time the data in
// the dependency array changes "[setPosts]" (reference comparison).
React.useEffect(() => {
// Create funtion to run async logic to load posts.
const getPosts = () => {
// load posts
db.collection("Posts").get().then(snapshot => {
// map loaded posts to an array of easy to manage objects.
const loadedPosts = snapshot.docs.map(docs => {
return {
name: docs.data().postName,
createdAt: docs.data().createdAt,
content: docs.data().postContent,
}
});
// store loaded posts in state.
setPosts(loadedPosts ?? []);
});
};
// run async function created above.
getPosts();
}, [setPosts])
// posts will remain null until the async function has loaded data.
// you can manually track loading in a separate state if required.
if (posts === null) {
// Show loading view while loading.
return (
<div>
Loading Posts...
</div>
);
}
// map out posts view after posts have been loaded.
return (
<div>
{posts.map(post => (
<div>
<div>{post.postName}</div>
<div>{post.createdAt}</div>
<div>{post.content}</div>
</div>
))}
</div>
);
};
You need to loading control before rendering jsx. Looks like this;
import {useEffect, useState} from 'react';
function Blog(){
const [loading, setLoading] = useState(true);
useEffect(() => {
setLoading(true);
function getPosts(){
db.collection("Posts").get().then(snapshot =>{
snapshot.docs.forEach(docs =>{
createPost(
docs.data().postName,
docs.data().createdAt,
docs.data().postContent,
)
})
setLoading(false);
})
}
getPosts();
}, [])
return (
loading ?
<div>
< Navbar/>
<div className="container">
<div className="row" id="posts-collection">
</div>
</div>
</div> : null
)
}
export default Blog;
I've retrieved a list of categories using an API. Now I want to fetch images from an URL based on the categories. I tried using each category to fetch images from another API, but I'm not sure how to do it.
import React, { useEffect, useState } from 'react';
import './css/Category.css';
function Category() {
useEffect(() => {
fetchData();
getImage();
}, []);
const [categories, setCategories] = useState([]);
const [image, setImage] = useState('');
const fetchData = async () => {
const data = await fetch('https://opentdb.com/api_category.php')
const categories = await data.json();
console.log(categories.trivia_categories)
setCategories(categories.trivia_categories)
}
const getImage = async (name) => {
console.log(name)
const q = name.split(' ').join('+')
const img = await fetch(`https://pixabay.com/api/?key=apikey&q=${q}&image_type=photo`)
const image = await img.json();
console.log(image)
setImage(image.previewURL)
}
return (
<div className="categories">
Yesss
<div className="category-grid">
{categories.map(category => (
<div className="category">
{category.name}
<img src={getImage(category.name)} /> //do not know what to do here to fetch image of the respective category
</div>
))}
</div>
</div>
)
}
export default Category;
After changes suggested by Noah, I was able to show only one image.
const getImage = async (name) => {
const query = stringMan(name.name)
console.log(query)
const img = await fetch(`https://pixabay.com/api/?key=17160673-fd37d255ded620179ba954ce0&q=${query}&image_type=photo`)
const image = await img.json();
console.log(image)
setImage({ [name.name]: image.hits[0].largeImageURL })
}
return (
<div className="categories">
Yesss
<div className="category-grid">
{categories.map(category => (
<div className="category" key={category.id}>
{category.name}
<img key={category.id} src={image[category.name]} />
</div>
))}
</div>
</div>
)
There are a couple of changes that you can make here.
One issue that I see is that you have a single image variable, that's being re-used for every single category. So when you map over a list of categories (for example let's say we have categories: [history, science, and math]). The current code will call getImage three times, with history, science, and math as parameters.
However, there is only one state variable that is being written to. Which means the last execution of setImage is the only one that will be preserved.
So, you might want to change image from being the URL of a category image, to an object that has the shape:
{
history: [url],
science: [url],
math: [url]
}
The other change to make is that you are calling the getImage() function directly in the rendered output <img src={getImage(category.name)} />. Instead, this should simply use the value that was assigned to the image state: <img src={image} />.
To actually fetch the image, you can use the useEffect hook (https://reactjs.org/docs/hooks-effect.html) to react to changes to the categories variable. That might look something like:
useEffect(() => {
categories.forEach((c) => getImage(c));
}, [categories]);
The useEffect hook will invoke the function it is given, whenever the dependencies change. This will allow you to trigger the getImage function in response to changes to the categories.
There're lot of improvement that could be done as stated by #noah-callaway above/below but coming straight to the point you need to simply fix the URI creation logic to use encodeURIComponent like below:
import React, { useEffect, useState } from 'react';
function Category() {
useEffect(() => {
fetchData();
getImage();
}, []);
const [categories, setCategories] = useState([]);
const [image, setImage] = useState('');
const fetchData = async () => {
const data = await fetch('https://opentdb.com/api_category.php')
const categories = await data.json();
console.log(categories.trivia_categories)
setCategories(categories.trivia_categories)
}
const getImage = async (name) => {
return encodeURI(`https://pixabay.com/api/?key=apikey&q=${encodeURIComponent(name)}&image_type=photo`)
}
return (
<div className="categories">
Yesss
<div className="category-grid">
{categories.map(category => (
<div className="category">
{category.name}
<img src={getImage(category.name)} />
</div>
))}
</div>
</div>
)
}
don't have the api key so can't test but it'll give you something like
https://pixabay.com/api/?key=apikey&q=Entertainment%3A%20Comics&image_type=photo
good luck, hope it works.
i use axios to retrieve data from back.
I do get a result from the back. I can even console.log it. Yet, even I assign it to the recipe variable. It doesn't works. I get a empty array.
Anyone would known why ? I really don't understand.
FRONT
import React, { useEffect,useState } from 'react'
import Header from '../../components/Header'
import axios from 'axios'
export default function OneRecipePage(props) {
const [recipe, setrecipe] = useState([])
useEffect(() => {
const id = props.match.params.id
const getRecipe = async () => {
const url = `http://localhost:8000/user/recipe/${id}`
const result = await axios.get(url)
setrecipe(result.data)
console.log('recipe',recipe)
console.log('from back', result.data);
}
getRecipe()
},[])
return (
<div>
<Header/>
<main class="main">
<div class="heading">
<aside class="recipes-info__category_name">{recipe.name}
</aside>
<aside class="recipes-info__date">{recipe.created_at}
</aside>
<h2 class="heading-secondary heading-secondary--big">{recipe.title}</h2>
<p>by author</p>
</div>
<div class="image-box">
<img class="image" src={recipe.photo}/>
</div>
<div class="recipes-details"></div>
</main>
</div>
)
}
BACK
router.get('/recipe/:id', (req,res) => {
const id = req.params.id
connection.query('SELECT * from ETB.recipes WHERE id = ?', id, (err, results) => {
if (err) {
res.status(500).send('Error retrieving the recipe')
}else {
res.status(200).json(results)
}
})
})
In react, set State is an asynchronous aciton. By the time it executes next line, it is no guaranteed that it has set state.
instread of this
`setrecipe(result.data)
console.log('recipe',recipe)`
you can use useEffect() to detect the change in state.
useEffect(()=>console.log('recipe',recipe),[recipe])