Can't fetch data from Firestore with React search bar - javascript

I need to make a simple search bar to fetch some data from the Cloud Firestore database. Can you help me? I'm new to React and it still costs me a lot. I use version 9 modular of firebase
This is my code taken from a tutorial.
import { useEffect, useState, useRef } from "react"
import { db } from "../firebase/firebase"
export default function Search(){
const [contacts, setContacts] = useState([]);
const [search, setSearch] = useState("");
const [filteredContacts, setFilteredContacts] = useState([]);
useEffect(() => {
const fetchData = async () => {
const data = await db.collection("users").orderBy("name").get();
setContacts(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
};
fetchData();
}, []);
useEffect(() => {
setFilteredContacts(
contacts.filter(
(user) =>
user.name.toLowerCase().includes(search.toLowerCase()) ||
user.city.toLowerCase().includes(search.toLowerCase())
)
);
}, [search, contacts]);
return (
<div className="container-fluid">
<div className="row justify-content-center">
<div className="col-lg-12">
<>
<div className="App">
<h1>Contact Details</h1>
<input
type="text"
placeholder="Search"
onChange={(e) => setSearch(e.target.value)}
/>
</div>
<div>
{filteredContacts.map((user) => [
<ol>
<b>Consumer Details :</b> {<br />}
{user.name},{<br />}
{user.company},{<br />}
{user.city},{<br />}
{user.contact},{<br />}
</ol>,
])}
</div>
</> </div>
</div>
</div>
)
}
I hope you can help me, I know that the code that I implement is from firebase 8, but I don't know how to change it.

Related

How to add a "show more" button to each card on React?

I have React component:
Main.jsx
import { useState, useEffect } from "react";
import { Preloader } from "../Preloader";
import { Pokemons } from "../Pokemons";
import { LoadMore } from "../LoadMore";
function Main() {
const [pokemons, setPokemons] = useState([]);
const [loading, setLoading] = useState(true);
const [pokemonsPerPage] = useState(20);
const [page, setPage] = useState(1);
function getPokemons(pokemonOffset) {
fetch(
`https://pokeapi.co/api/v2/pokemon?limit=${pokemonsPerPage}&offset=${pokemonOffset}`
)
.then((responce) => responce.json())
.then((data) => {
data.results && setPokemons((p) => [...p, ...data.results]);
setLoading(false);
});
}
useEffect(() => {
const offset = page * pokemonsPerPage - pokemonsPerPage;
getPokemons(offset);
}, [page]);
return (
<main className="container content">
{loading ? <Preloader /> : <Pokemons pokemons={pokemons} />}
<LoadMore next={() => setPage((p) => p + 1)} />
</main>
);
}
export { Main };
Pokemon.jsx
import { useState, useEffect } from "react";
function Pokemon({ name, url }) {
const [data, setData] = useState(null);
useEffect(() => {
fetch(url)
.then((r) => r.json())
.then(setData);
}, [url]);
return (
<div>
{data ? (
<div className="card animate__animated animate__fadeIn">
<div className="card-image">
<img src={data.sprites.front_default} />
<span className="card-title">{name}</span>
</div>
<div className="card-content">
{data.abilities.map((n, index) => (
<p key={index}>{n.ability.name}</p>
))}
</div>
</div>
) : (
<div>loading...</div>
)}
</div>
);
}
export { Pokemon };
I need each card (Pokemon) to have a "Details" button, which, when clicked, displays additional (unique) information from the fetch request in the "url" for the selected card
I think I need to do this in Pokemon.jsx but I just started learning React and haven't come across a similar challenge
If you just need a button for each card I would assume this
{data.map((item, index) =>
<div key={index}>
....
<button onClick={()=> { do something }}>
</div>
)}
and then create a function that fetches data and add it to your array where you keep
the data and might have to mess with the useEffect when you want to see the change.

Next-JS Beginner using two dynamic APIs, one works, the other doesn't

My friend helped me to rewrite my crappy JS web app to a next-app, but upon trying to continue, I'm running into roadblocks and much confusion.
I have built two APIs that return objects:
import fetch from "isomorphic-fetch";
import cheerio from "cheerio";
export const getData = async (player) => {
const req = await fetch('blahblahblah');
...
return teams;
};
module.exports = {
getData,
};
and another that returns teamStats
I have 2 endpoints:
[id].js - and [tid].js
import { getData } from "../../../utils/api";
export default async (req, res) => {
const details = await getData(req.query.id);
res.status(200).json(details);
};
import { getStats } from "../../../utils/squadapi";
export default async (req, res) => {
const details = await getStats(req.query.tid);
res.status(200).json(details);
};
two components (this one works fine):
import React from "react"; // react dependencies
const Teams = ({ teams }) => {
return teams.map((team, index) => (
<React.Fragment key={index}>
<br />{" "}
<div class="container">
{team.map((pokemon) => (
<React.Fragment key={pokemon.pokemon}>
<br /> <div class="bout">{pokemon.bout}</div>
<div class="child">
<img src={pokemon.sprite} />
<p>{pokemon.pokemon}</p>
</div>
</React.Fragment>
))}
</div>
<br />
</React.Fragment>
));
};
export default Teams;
this one doesn't work (teamStats is undefined)
import React from "react";
const Squads = ({ teamStats }) => {
return (
<React.Fragment>
<img src={teamStats.logo} />
</React.Fragment>
);
};
export default Squads;
why is my second component returning undefined? i did my best to replicate the steps my friend took to create the first component which works fine... second one errors 'teamStats' is undefined.
edit: directory structure is:
Pages > api > player > [id].js Pages > api > squad > [tid].js
edit: index.js:
import Head from "next/head";
import React, { useState } from "react";
import Teams from "../components/Teams";
import styles from "../../styles/Home.module.css";
import Squads from "../components/Squads";
export default function Home() {
const [teams, setTeams] = useState([]);
const [player, setPlayer] = useState("Player Name");
const [loading, setLoading] = useState(false);
const [squad, setSquad] = useState("9a7059e278");
const loadSquad = async () => {
setLoading(true);
const req = await fetch(`/api/squad/${squad}`);
const json = await req.json();
setSquad(json);
setLoading(false);
};
const loadPeople = async () => {
setLoading(true);
const req = await fetch(`/api/player/${player}`);
const json = await req.json();
setTeams(json);
setLoading(false);
};
return (
<div className={styles.main}>
<Head>
<title>Liam</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<h1>Silph Team Finder</h1>
<br />
<div>
<select
value={squad}
onChange={(e) => setSquad(e.currentTarget.value)}
>
<option value="9a7059e278">Stoked</option>
</select>
<button onClick={() => loadSquad()}>Load</button>
{loading && <div className={styles.load}>LOADING</div>}
<Squads />
<input value={player} onChange={(e) => setPlayer(e.target.value)} />
<button onClick={() => loadPeople()}>Load</button>
{loading && <div className={styles.load}>LOADING</div>}
</div>
<div className={styles.teams}>
<Teams teams={teams} />
</div>
</main>
</div>
);
}
Credit goes to #juliomalves & #calvin
my jsx/component was missing props:
<Squads teamStats={squads} />

How to fetch nested data in react

Question regarding fetching nested data in react.
APIs
https://jsonplaceholder.typicode.com/posts
https://jsonplaceholder.typicode.com/posts/${postId}/comments
Able to fetch list of posts. now want to fetch list of comments from when click on post
here is code so far
import React, { useEffect, useState } from "react";
import Post from "./Post";
const [posts, setPosts] = useState([]);
const [comments, setComments] = useState([]);
function App() {
const [posts, setPosts] = useState([]);
const [comments, setComments] = useState([]);
useEffect(() => {
const loadposts = async() => {
const resp = await fetch("https://jsonplaceholder.typicode.com/posts?userId=1");
const data = await resp.json();
setPosts(data);
}
loadposts();
}, []);
return (
<div className="App">
<ul>
{posts.map((post) =>
(
<div>
<li key={post.id}>
<Post
userId={post.id}
title={post.title}
body={post.body}
/>
</li>
</div>
))
}
</ul>
</div>
);
}
export default App;
function Post({title, body, postId}) {
return (
<div>
<h5>{postId}</h5>
<h1>{title}</h1>
<p>{body}</p>
</div>
)
}
export default Post
appreciate any help. thanks
Firstly, the "/posts" endpoint returns posts by users, so the query "/posts?userId=1" will return all the posts by user id 1. You mistakenly passed a userId prop to the Post component instead of the specific post's id, i.e.
<Post userId={post.id} title={post.title} body={post.body} />
The React key should also be placed on the outer-most element being mapped, the div in your case, but since li is already a block level element the div is basically extraneous.
<ul>
{posts.map((post) => (
<li key={post.id}> // <-- remove div and place React key on li
<Post
postId={post.id} // <-- pass the post's id
title={post.title}
body={post.body}
/>
</li>
))}
</ul>
In Post component create a fetch comments utility and click handler, and attach the click handler to the title header. Conditionally render the comments. If it wasn't already clear, you'll move the comments state into Posts so each post component maintains its own copy. The following is an example for rendering out the comments once fetched, you can use whatever conditional rendering and field subset of your choosing.
const fetchComments = async (postId) => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/posts/${postId}/comments`
);
return response.json();
};
function Post({ title, body, postId }) {
const [comments, setComments] = useState([]);
const clickHandler = () => {
fetchComments(postId).then(setComments);
};
return (
<div>
<h5>{postId}</h5>
<h1 onClick={clickHandler}>{title}</h1>
<p>{body}</p>
{comments.length && (
<>
Comments:
<ul>
{comments.map(({ id, email, name, body }) => (
<li key={id}>
<dl>
<dt>{email} - {name}</dt>
<dd>{body}</dd>
</dl>
</li>
))}
</ul>
</>
)}
</div>
);
}
Working solution if anyone looking for
function Post() {
const {id} = useParams();
const [comments, setComments] = useState([]);
useEffect(() => {
fetch(`https://jsonplaceholder.typicode.com/posts/${id}/comments`)
.then((res) => res.json())
.then(setComments)
.catch((error) => {
console.log(error)
})
console.log("setComments: ", setComments)
}, [])
return (
<div>
{comments && comments.map((comment) => (
<div key={comment.id}>
<p>{comment.body}</p>
</div>
))}
</div>
)
}
export default Post
then update rendering
<div className="App">
<Switch>
<Route exact path='/'>
{posts.map((post) => (
<article key={post.id}>
<h1>{post.title}</h1>
<Link to ={`/${post.id}`}>
<p>{post.body}</p>
</Link>
</article>
))}
</Route>
<Route path ='/:id'>
<Post/>
</Route>
</Switch>
</div>

Cant map through props - React JS/API Call

Trying to populate a list from an API call/search on a react app. Getting the error that property "map" is undefined in Main.js where the movieList is being called. Application runs until you try and type into the search box. Any clues as to what I'm doing wrong here??
App JS:
function App() {
const [movieList, SetMovieList] = useState([]);
const [search, SetSearch] = useState("");
const HandleSearch = e => {
e.preventDefault();
FetchMovies(search);
}
const FetchMovies = async (query) => {
const temp = await fetch(`http://www.omdbapi.com/=${query}`)
.then(res => res.json());
SetMovieList(temp.results);
}
<div className="content-wrap">
<Sidebar nominations={nominations}/>
<Main
HandleSearch={HandleSearch}
search={search}
SetSearch={SetSearch}
movieList={movieList}
/>
</div>
Main JS:
<main>
<div className="main-head">
<form className="search-box"
onChange={props.HandleSearch}>
<input
type="search"
placeholder="Search the OMDB database..."
required
value={props.search}
onSubmit={e => props.SetSearch(e.target.value)}/>
</form>
</div>
<div className="movie-list">
{props.movieList.map(movie => (
<MovieCard
movie={movie}
key={movie.mal_id} />
))}
</div>
</main>
It's because the movieList is undefined or empty.
Try to check whether it has data or not with conditional operator:
<div className="movie-list">
{props.movieList ? props.movieList.map(movie => (
<MovieCard
movie={movie}
key={movie.mal_id} />
)) : {}
}
</div>

how to create a filter to dynamically render fetched items

How do I create a filter to dynamically render fetched items from an API in to get more than one filter criteria. I'm new to React.
Below is My App.js file where I'm fetching from the API, setting it in the 'records' state. I'm importing the FilterRecords component here
const App = () => {
const [records, setRecords] = useState([])
const [loading, setLoading] = useState(false)
const [currentPage, setCurrentPage] = useState(1)
const [recordsPerPage] = useState(20)
useEffect(() => {
const fetchRecords = async () => {
setLoading(true)
const res = await fetch('http://api.enye.tech/v1/challenge/records')
const data = await res.json();
setRecords(data.records.profiles)
setLoading(false)
}
fetchRecords()
// eslint-disable-next-line
}, [])
// Get current records
const indexOfLastRecord = currentPage * recordsPerPage // should give the index of the last records
const indexOfFirstRecord = indexOfLastRecord - recordsPerPage // should give the index of the first records
// Get current records
const currentRecords = records.slice(indexOfFirstRecord, indexOfLastRecord) // Slices out the number of records per page
// change page
const paginate = (pageNumber) => setCurrentPage(pageNumber)
return (
<Fragment>
<SearchBar />
<div className='container'>
<h2>Patients Record Details</h2>
<RecordsFilter />
<Pagination recordsPerPage={recordsPerPage} totalRecords={records.length} paginate={paginate} />
{!loading ? <Records loading={loading} records={currentRecords} /> : <div></div>}
</div>
</Fragment>
);
}
And Below is my filterRecords component. Here, I've destructured the records, filtered through them to only search for Gender, and mapped it to display the typed outcome. I'm not sure if the onChange, onSubmit is properly implemented. When I type into the filter input, I get cannot read property filter of undefined.
I need help with how to make it work properly
import React, { useState } from 'react'
const RecordsFilter = ({ records }) => {
const [search, setSearch] = useState('')
const [loading, setLoading] = useState('')
const onChange = (e) => {
setSearch({ [e.target.name]: e.target.value })
}
const filteredRecord = records.filter((record) => {
return record.Gender.includes('Male', 'Female', 'Prefer to skip')
})
const renderFilter = (
<div className='card card-body'>
<ul>
{filteredRecord.map(record => {
return <li className="collection-item">
{record}
</li>
})}
</ul>
</div>
)
return (
<div>
<form onSubmit={filteredRecord}>
<input type="text" name='text' value={search} placeholder='Filter from...' onChange={onChange} />
</form>
{!loading ? renderFilter : <div></div>}
</div>
)
The fetching works fine and renders. I want to filter and map properly below is a screenshot
You have not passed records as props in RecordsFilter.
return (
<Fragment>
<SearchBar />
<div className='container'>
<h2>Patients Record Details</h2>
<RecordsFilter records={currentRecords} />
<Pagination recordsPerPage={recordsPerPage} totalRecords={records.length} paginate={paginate} />
{!loading ? <Records loading={loading} records={currentRecords} /> : <div></div>}
</div>
</Fragment>
);
}

Categories