Create list elements from an object in react - reactjs

I am trying to do a recipe app in react. I want to create a new li element in the favorite component when clicking like button.
Here my App.js
import "./App.css";
import React, { useEffect, useState } from "react";
import axios from "axios";
import FavMeals from "./favMeals/FavMeals";
import Header from "./header/Header";
import { RandomMeal } from "./randomMeals/RandomMeal";
const API = "https://www.themealdb.com/api/json/v1/1/random.php";
function App() {
const [meal, setMeal] = useState({});
const [favoriteMeal, setFavoriteMeal] = useState({});
useEffect(() => {
axios
.get(API)
.then((res) => {
setMeal(res.data.meals[0]);
})
.catch((error) => {
console.log(error);
});
}, []);
const sendData = (e) => {
setFavoriteMeal({
favoriteMeal: e,
});
};
return (
<div className="App">
<Header className="header" />
<FavMeals favouriteMealData={favoriteMeal} />
<RandomMeal meals={meal} functionCall={sendData} />
</div>
);
}
export default App;
and here my fav meal component and I send an object that I got from API.

Related

It doesn't take the id correctly and doesn't display data. I am trying to get the recipe info by clicking on the button, but it doesn't show anything

I am trying to create a favourite functionality, for now I am trying to get the recipe by it's id and then save the data in const variables and then create a new recipe with the same data and add it to the favourites data collection. It adds a recipe to the favourites page, but it doesn't show the data, it shows only the html model of the recipe. I hope that's clear enough.
import "./RecipeModel.css";
import { Link, NavLink, useNavigate, useParams } from 'react-router-dom';
import { BrowserRouter,Route,Routes, Switch, Redirect } from 'react-router-dom';
import React, { useLayoutEffect, useState,useEffect, useContext } from 'react';
import * as userService from '../../../services/userService';
import { AuthContext } from "../../../contexts/AuthContext";
const RecipeModel = ({
recipe
}) => {
const history = useNavigate();
const {user} = useContext(AuthContext);
const {recipeId} = useParams();
const [recipec,setRecipe] = useState();
useEffect(() => {
userService.getOne(recipeId)
.then(result => {
setRecipe(result);
})
},[recipeId]);
const HandleFavourite = (e) => {
e.preventDefault();
// const recipesd = async () => {
// let reciperes = await userService.getOne(recipeId);
// console.log(reciperes);
// setRecipe(reciperes);
// };
// useEffect(() => { recipesd(recipec); },[]);
console.log(`Recipe ${recipec._id}`);
// let recipeData = Object.fromEntries(new FormData(e.currentTarget))
// userService.addFavourite(recipe._id, recipeData);
// history('/favourites');
// const formData = new FormData(e.currentTarget);
const name = recipec.name;
const time = recipec.time;
const imageUrl = recipec.imageUrl;
const ingredients = recipec.ingredients;
const instructions = recipec.instructions;
userService.addFavourite({
name,
time,
imageUrl,
ingredients,
instructions
},user.accessToken)
.then(result => {
console.log(result);
history('/favourites');
})
}
return (
<article className="articles">
<img className="img2" src={recipe.imageUrl}/>
<h1>{recipe.name}</h1>
<p className="cut-text">{recipe.instructions}</p>
<div className="btns1">
<Link smooth= "true" className="btnd" to={`/recipe-details/recipe-number:${recipe._id}`}>Details</Link>
<button className="favour" onClick={HandleFavourite} ><i className="fas fa-solid fa-heart-circle-plus"></i></button>
</div>
</article>
);
};
export default RecipeModel;
The method to getOne works fine for the other data collections I fetched.
That's the Favourites component
import "./Favourites.css";
import { Link, NavLink } from 'react-router-dom';
import { BrowserRouter,Route,Routes, Switch, Redirect } from 'react-router-dom';
import RecipeList from "../RecipeList/RecipeList";
import CreateRecipe from "../Recipe/CreateRecipe/CreateRecipe";
import React, { useLayoutEffect, useState ,useEffect} from 'react';
import RecipeFavourite from "../Recipe/RecipeFavourite/RecipeFavourite";
import * as recipeService from "../../services/recipeService";
import * as userService from '../../services/userService';
function Favourites() {
// const [recipes,setRecipes] = useState([]);
// useEffect(() => {
// recipeService.getAll()
// .then(result => {
// setRecipes(result);
// })
// },[]);
const [favourites,setFavourites] = useState([]);
useEffect(() => {
userService.getAllFavourites()
.then(result => {
setFavourites(result);
})
},[]);
useLayoutEffect(() => {
window.scrollTo(0, 0)
});
const scrollToTop = () => {
window.scrollTo(0, 0)
}
return (
<div>
<h1 className = "h1m">Your Favourite Recipes</h1>
<section className="secfav">
{favourites.map(x => <RecipeFavourite key ={x._id} recipe={x} />)}
{/* {recipes.map(x => <RecipeFavourite key ={x._id} recipe={x}/>)} */}
</section>
</div>
);
}
export default Favourites;
Thats's the RecipeFavourite component
import "./RecipeFavourite.css";
import { Link, NavLink } from 'react-router-dom';
import { BrowserRouter,Route,Routes, Switch, Redirect } from 'react-router-dom';
import React, { useLayoutEffect, useState } from 'react';
const RecipeFavourite = ({recipe,recipes}) => {
return (
<article className="articles">
<img className="img4" src={recipe.imageUrl}/>
<h1>{recipe.name}</h1>
<p className="cut-text">{recipe.instructions}</p>
<div className="btns1">
<Link smooth= "true" className="btnd" to={`/recipe-details/recipe-number:${recipe._id}`}>Details</Link>
<button className="favnon"><i className="fas fa-solid fa-heart-circle-minus"></i></button>
</div>
</article>
);
};
export default RecipeFavourite;
Thats the AuthContext logic
import { createContext, useContext } from 'react';
export const AuthContext = createContext();
The userService logic
export const addFavourite = async (recipeData,token) => {
let response = await fetch(`${baseUrl}/favourites`, {
method: 'POST',
headers: {
'content-type': 'application/json',
'X-Authorization': token,
},
body: JSON.stringify(recipeData)
});
let result = await response.json();
return result;
};
export const getAllFavourites = async () => {
let response = await fetch(`${baseUrl}/favourites`)
let recipes = await response.json();
let result = Object.values(recipes);
return result;
}

How do I store firebase Auth data in firestore?

So I have a basic app running. There's just one button which is used to login with google and I want to store that user's info in firestore and then I want to map through every user and display every single user's which are signed in, in my app. Firebase auth is complete but I don't know how to store that user's auth info.Also I am using useContext to pass authorized users info.Here's the code:
Main Entry Level App.js
import React, { useEffect, useMemo, useState } from "react";
import Login from "./components/Login";
import { User } from "./components/User";
import db, { auth } from "./firebase";
import { UserContext } from "./Contexts/UserContext";
const App = () => {
const [user, setUser] = useState([]);
const value = useMemo(() => ({ user, setUser }), [user, setUser]);
useEffect(() => {
auth.onAuthStateChanged((user) => {
// console.log(user);
setUser(user);
});
}, []);
return (
<UserContext.Provider value={value}>
{user ? <User /> : <Login />}
</UserContext.Provider>
);
};
export default App;
User.js Component
import React, { useContext } from "react";
import { UserContext } from "../Contexts/UserContext";
import db, { auth } from "../firebase";
export const User = () => {
const { user } = useContext(UserContext);
return (
<>
<img src={user.photoURL} alt={user.displayName} />
<div>{user.displayName}</div>
<div>{user.email}</div>
<button onClick={() => auth.signOut()}>Log Out</button>
</>
);
};
Login.js
import React, { useContext, useEffect } from "react";
import { UserContext } from "../Contexts/UserContext";
import { auth, signInWithGoogle } from "../firebase";
const Login = () => {
const { setUser } = useContext(UserContext);
useEffect(() => {
auth.onAuthStateChanged((user) => {
console.log(user);
setUser(user);
});
});
return (
<>
<div style={{ textAlign: "center" }}>
<button onClick={signInWithGoogle}>
<img
src="https://img.icons8.com/ios-filled/20/000000/google-logo.png"
alt="google icon"
/>
<span> Continue with Google</span>
</button>
</div>
</>
);
};
export default Login;
signInWithGoogle
export const signInWithGoogle = () => {
auth.signInWithPopup(provider).catch((err) => alert(err.message));
};
You should use a then() block in your signInWithGoogle() function, as follows:
export const signInWithGoogle = () => {
auth.signInWithPopup(provider)
.then((result) => {
const userId = result.user.uid;
// Create a doc in a users collection
// It's up to you to build theJavaScript objec to pass to the set() methood
firestore.collection("users").doc(userId).set( {foo: bar, bar: foo} );
})
.catch((err) => alert(err.message));
};
More details in the doc.

Is there a way I can pass a property between components, without using the props object?

I have a component that displays a list of cafes. Within this component ( CafeList.jsx), an axios request is made which returns a list of cafes, which is the mapped over and rendered to the browser.
I'd like users to be able to click on a cafe, then be directed to a page with specific information about that particular cafe (at this stage it's CafeReview.jsx).
I need to pass the cafe ID (_id) from CafeList to CafeReviews, so that I can use it in an axios request that brings back specific data about the cafe that was clicked on. Any suggestions? Also, do I have the right general approach?
Components
CafeList.jsx
import React, {useState, useEffect} from 'react'
import axios from 'axios'
import {Link} from 'react-router-dom'
const CafeList = () => {
const [cafes, setCafe] = useState([])
useEffect(() => {
axios.get('/api/all-cafes')
.then(cafe => {
setCafe(cafe.data)
})
.catch(err => {
console.log(err)
})
},[])
return(
<div className = 'cafe-container-container'>
<h2>Cafes</h2>
{
cafes.map(cafe =>{
const {cafeName,photoURL,_id} = cafe
return (
<Link to = {`/cafe-reviews/${_id}`} style={{ textDecoration: 'none' }} >
<div className = 'cafe-container'>
<h2>{cafeName}</h2>
<img src = {photoURL}></img>
</div>
</Link>
)
})
}
</div>
)
}
export default CafeList
CafeReviews.jsx
import React,{useState, useEffect} from 'react'
import axios from 'axios'
const CafeReviews = () => {
const [cafe,setCafe] = useState([])
useEffect(() => {
axios.get('/api/cafe/:id')
.then(result => {
setCafe(result.data)
})
},[])
return(
<div>
{
cafe.map(item => {
return (
<h2>{item.cafeName}</h2>
)
})
}
</div>
)
}
export default CafeReviews
Routes and data models
GET cafe by id:
app.get('/api/cafe/:id', (req,res) => {
const id =req.params.id
Cafe.findById(id)
.then(result => {
res.send(result)
})
.catch(err => {
console.log(err)
})
})
Cafe Model:
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const cafeSchema = new Schema({
cafeName:String,
photoURL:String,
}, {timestamps:true})
const Cafe = mongoose.model('cafes', cafeSchema)
module.exports = Cafe
Router:
import React from 'react'
import AddReview from './components/AddReview'
import Main from './components/Main'
import AllReviews from './components/AllReviews'
import CafeReviews from './components/CafeReviews'
import './styles.css'
import {BrowserRouter as Router, Switch, Route} from 'react-router-dom'
const App = () => {
return(
<Router>
<div>
<Switch>
<Route path ='/' exact component = {Main}/>
<Route path ='/add-review' component = {AddReview}/>
<Route path ='/all-reviews' component = {AllReviews}/>
<Route path ='/cafe-reviews/:id' component = {CafeReviews}/>
</Switch>
</div>
</Router>
)
}
export default App;
Since the CafeReviews component is directly rendered by a <Route/>, by default is has all the react router props passed to it. From there you can access the params, which will contain the :id of that specific cafe in the URL. So try something like this:
const CafeReviews = ({ match }) => {
const [cafe,setCafe] = useState([])
useEffect(() => {
axios.get(`/api/cafe/${match.params.id}`)
.then(result => {
setCafe(result.data)
})
},[])
Haven't tested, might need to check the docs react-router-dom to see if that's correct shape of object and such, but in general that's how to access the params inside the component

TypeError: patients.map is not a function React Js

I am requesting some basic info from the back end using axios but for some reason unable to render the data on screen. Below is my basic App component using hooks and a map function to return a surname
import React, { useState, useEffect } from 'react';
import FullCalendar from '#fullcalendar/react'
import dayGridPlugin from '#fullcalendar/daygrid'
import interactionPlugin from '#fullcalendar/interaction';
import { Router } from '#reach/router'
import 'bootstrap/dist/css/bootstrap.css'
import axios from 'axios'
import './custom.css'
const App = () => {
const [patients, setPatient] = useState([])
useEffect(() => {
axios.get('http://localhost:5000/api/patient').then(response => {
console.log(response.data)
setPatient(response.data)
})
}, [])
return (
<>
<div>
<ul>
{patients.map(p => (
<li>{p.surname}</li>
))}
</ul>
</div>
</>
)
}
export default App
When I check the dev tools I am bringing back all of data
I cannot see how my map function 'is not a function' can anyone help me out here please I get the below error message which is annoying
Try to use Async and Await for API call.
useEffect(function() {
async function fetchPatients() {
const response = await
fetch('http://localhost:5000/api/patient');
const json = await response.json();
setPatient(json.data);
}
fetchPatients();
}, []);
try this fixes:-
import React, { useState, useEffect } from 'react';
import FullCalendar from '#fullcalendar/react'
import dayGridPlugin from '#fullcalendar/daygrid'
import interactionPlugin from '#fullcalendar/interaction';
import { Router } from '#reach/router'
import 'bootstrap/dist/css/bootstrap.css'
import axios from 'axios'
import './custom.css'
const App = () => {
const [patients, setPatient] = useState([])
useEffect(() => {
(async () => {
try {
// fetching all patirnts
let res = await axios.get("http://localhost:5000/api/patient");
setPatient(res.data);
} catch (err) {
console.log(err);
}
})();
}, []);
return (
<>
<div>
<ul>
{patients?.map(p => (
<li>{p.surname}</li>
))}
</ul>
</div>
</>
)
}
export default App
A working code, for anyone who stupidly wastes too much time on this problem in the future. The data was nested meaning I had to setPatient to response.data.data. I was then able to pull all required info using Axios
useEffect(() => {
axios.get('http://localhost:5000/api/patient').then(response => {
setPatient(response.data.data)
})
}, [])

How to display a component in react js according to api response.?

Iam trying to display a component whenever response of an api is true. but if i try to do it in the axios where iam sending api request it does not work and if i remove the below return it gives me an error that there is nothing to render.
My code
import React from "react";
import { useState, useEffect } from "react";
import { SignUpComponent } from "../index";
import axios from "axios";
import { Redirect, useParams } from "react-router-dom";
import { getToken } from "../../common/constants/variables";
function Reference() {
const [openSignUp, setOpenSignup] = useState(true);
const [refer, setRef] = useState({});
let { ref } = useParams();
function toggleToSignUp() {
setTimeout(() => {
setOpenSignup(true);
}, 350);
}
axios
.post("https://theappsouk.com/api/v1/check-referral", {
ref: ref,
})
.then((response) => {
if (response.data.status == true) {
return (
<SignUpComponent open={openSignUp} toggleModal={toggleToSignUp} />
);
} else{
<Redirect to= '/'/>
console.log("NOTHING")
}
console.log("REFFEERR", JSON.stringify(response.data.status));
});
console.log("REFF", JSON.stringify(ref));
return ( //what ever the api response is it seems to render only this return statement
<div>
<SignUpComponent open={openSignUp} toggleModal={toggleToSignUp} />
</div>
);
}
export default Reference;
you should request axios in the useEffect, and display all the UI inside the return
import React from "react";
import { useState, useEffect } from "react";
import { SignUpComponent } from "../index";
import axios from "axios";
import { Redirect, useParams } from "react-router-dom";
import { getToken } from "../../common/constants/variables";
function Reference() {
const [openSignUp, setOpenSignup] = useState(true);
const [status, setStatus] = useState(true);
const [refer, setRef] = useState({});
let { ref } = useParams();
function toggleToSignUp() {
setTimeout(() => {
setOpenSignup(true);
}, 350);
}
useEffect(() => {
axios
.post("https://theappsouk.com/api/v1/check-referral", {
ref: ref,
})
.then((response) => {
setStatus(response.data.status)
});
}, []);
return ( //what ever the api response is it seems to render only this return statement
<div>
{
status? <SignUpComponent open={openSignUp} toggleModal={toggleToSignUp} /> :
<Redirect to= '/'/>
}
</div>
);
}
export default Reference;

Resources