Cant map through props - React JS/API Call - javascript

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>

Related

Can't fetch data from Firestore with React search bar

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.

React how to make a call to an API and display an array in an array of objects PokeAPI

I need help with a project i am working on, i want to be able to display the moves of a pokemon i type in, i can console.log an array of objects that have smaller arrays inside that i want to grab and display, how can i do so? ive tried the cluster of a method getPokemonNames which is supposed to get the names of the moves of the pokemon but this is where i couldnt think anymore.
import React, {useState} from 'react';
import PokemonName from './PokemonName';
import axios from 'axios';
function App() {
const [pokemon, setPokemon] = useState([])
const [loading, setLoading] = useState(false)
const [moves, setMoves] = useState([])
const getPokemon = async (name) =>{
setLoading(true);
const response = await axios.get(`https://pokeapi.co/api/v2/pokemon/${name}`)
const data = response.data;
setPokemon(data);
setMoves(data.moves);
setLoading(false);
console.log(data)
getPokemonNames(data.moves)
// const pokemonMovesAmount = pokemon.moves.map
}
const getPokemonNames = (data) =>{
console.log(data);
data.move.name.map((moves, key)=>(
<div key={key}>
<span>{moves.moves.name}</span>
</div>
))
}
return (
<>
<PokemonName getPokemon={getPokemon}/>
<div>
{!loading && pokemon ? (<div>
</div>): null}
<img src={pokemon.sprites?.front_default}/>
<div className="container">
{getPokemonNames}
</div>
</div>
</>
);
}
export default App;
this is the pokemon name component
import React, {useState} from 'react'
const onSubmit =(e) =>{
e.preventDefault();
}
export default function PokemonName(props) {
const [search, setSearch] = useState('');
return (
<div>
<div>
<h1>{search}</h1>
<form onSubmit={onSubmit}>
<input onChange={e => setSearch(e.target.value)} type ="text" placeholder="Search for Pokemon"></input>
<button onClick={(e) => props.getPokemon(search)}>Search</button>
</form >
</div>
</div>
)
}
EDIT, this shows a bit more about the data i get back after searching up the pokemon mew
First step, making sure you pass the parameter data
const [moves, setMoves] = useState([])
// since you set setMoves to be data.moves second step you can iterate over data only
return (
<>
<PokemonName getPokemon={getPokemon}/>
<div>
{!loading && pokemon ? (<div>
</div>): null}
<img src={pokemon.sprites?.front_default}/>
<div className="container">
{getPokemonNames(moves)}
</div>
</div>
</>
);
Second step, tweak your getPokemon method
const getPokemonNames = (data) => {
return data.map(moveD, key)=>(
<div key={key}>
<span>{moveD.move.name}</span>
</div>
))
}
There is an mistake in your getPokemonNames . You are trying to do data.move.name.map which means you are mapping "move" from inside "data". You need to map "moves" array. Here is how you can do it.
const getPokemonNames = (data) =>{
data.moves.map(item, key)=>(
<div key={key}>
<span>{item.move.name}</span>
</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>
);
}

React hooks can't use setState for unknown reason

At this line. I added an onChange Attribute to change the textbox state and update it. But it is not working, I have no idea why because I try the same/ similar thing at other project and it is working correctly. Is there a limit on how many hooks I can use? Is it better to setState as an object with multiple parameters instead of multiple hooks?
<input value={textbox} onChange={e => setTextBox(e.target.value)}></input>
Below is the full code
import React, { useState } from 'react'
export const StartPage = () => {
const [players, SetPlayers] = useState(["jaw", "weihan"])
const [view, setView] = useState(selection)
//const [roles, SetRoles] = useState(null)
const [textbox, setTextBox] = useState("")
const selection = (
<div>
<button onClick={() => setView(addPlayer)}> Create Game </button>
<button> Join Game </button>
</div>
)
const addPlayer = (
<div className="add-player">
<form>
<input value={textbox} onChange={e => setTextBox(e.target.value)}></input>
<button type="submit" > Add more</button>
</form>
<ul>
{players.map(item => <li>{item}</li>)}
</ul>
</div>
)
return (
<section id="start-page">
{view}
</section>
)
}
onChange={e =>{
e.preventDefault()
setTextBox(e.target.value)
}}
Is this you want to achieve?
const [players, SetPlayers] = useState(['jaw', 'weihan']);
const [textbox, setTextBox] = useState('');
const [showPlayersForm, setShowPlayersForm] = useState(false);
const handleSubmit = e => {
e.preventDefault();
SetPlayers(prevState => [...prevState, textbox]);
setTextBox('');
};
const addPlayer = (
<div className='add-player'>
<form onSubmit={handleSubmit}>
<input value={textbox} onChange={e => setTextBox(e.target.value)} />
<button type='submit'> Add more</button>
</form>
<ul>
// use key when you loop trough items
{players.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
return (
<div>
<button onClick={() => setShowPlayersForm(true)}>Create game</button>
<button> Join Game </button>
{showPlayersForm && addPlayer}
</div>
);

React wait for data to fetch from api and then send to components

I fetch the data from an API with covid-19 cases and store that in a react hook then i try to send the data to a child component but i got error because data is undefined the problem is how code execute he dont have time to take the data from the api and directly send to the child but idk how to fix if someone can help i will appreciate that. Here is th code:
//importing components
import Main from './components/Main/Main'
import Second from './components/Second/Second'
//import style
import './App.scss'
// api for testing => https://jsonplaceholder.typicode.com/todos/1
const App = () => {
const [data, setData] = useState() //here we get the data from the API
const [drop, setDrop] = useState(null)
const getValue = (e) => {
setDrop(e.target.value)
}
useEffect(() => {
let URL;
if (drop === null) {
URL = 'https://disease.sh/v3/covid-19/all'
} else {
URL = `https://disease.sh/v3/covid-19/countries/${drop}?strict=true`
}
//getting data from the api
fetch(URL).then(res => res.json()).then(data => setData(data))
})
return (
<div className="wrapper">
<div className="first">
<Main info={data} getValue={getValue} />
<button onClick={() => { console.log(drop) }}>testing</button>
<button onClick={() => { console.log(data) }}>testing API</button>
<button onClick={() => { console.log(data.deaths) }}>testing deaths</button>
</div>
<div className="bla">
<Second />
</div>
</div>
)
}
export default App```
Edit: data !== undefined &&
return data !== undefined && (
<div className="wrapper">
<div className="first">
<Main info={data} getValue={getValue} />
<button onClick={() => { console.log(drop) }}>testing</button>
<button onClick={() => { console.log(data) }}>testing API</button>
<button onClick={() => { console.log(data.deaths) }}>testing deaths</button>
</div>
<div className="bla">
<Second />
</div>
</div>
)
edit:
return data !== undefined ? (
<div className="wrapper">
<div className="first">
<Main info={data} getValue={getValue} />
<button onClick={() => { console.log(drop) }}>testing</button>
<button onClick={() => { console.log(data) }}>testing API</button>
<button onClick={() => { console.log(data.deaths) }}>testing deaths</button>
</div>
<div className="bla">
<Second />
</div>
</div>
): (
<div className="wrapper">
Loading...
</div>
)
return (
<div className="wrapper">
<div className="first">
{data!==undefined &&<Main info={data} getValue={getValue} />}
<button onClick={() => { console.log(drop) }}>testing</button>
<button onClick={() => { console.log(data) }}>testing API</button>
<button onClick={() => { console.log(data.deaths) }}>testing deaths</button>
</div>
<div className="bla">
<Second />
</div>
</div>
)
There's going to be some delay until you get a response from the API. They're asynchronous, therefore the response will not be immediate.
Since you didn't defined an initial state for data, its going to be undefined by default. That is why you're getting the error. Basically <Main /> is trying to render data that is undefined.
The typical approach to circumvent this is to use short-circuit evaluation:
{ data && JSON.stringify(data) }
This checks if data is a thing, if it is, then it executes the code to right side (in this case a simply stringification of object).
You can add this safeguard right before you pass down data as props:
{data && <Main data={data} getValue{getValue} />
or directly on the child component. Like I did below:
const App = () => {
const [data, setData] = React.useState(null);
const [drop, setDrop] = React.useState(null);
const getValue = (e) => {
setDrop(e.target.value);
};
React.useEffect(() => {
let URL;
if (!drop) {
URL = "https://disease.sh/v3/covid-19/all";
} else {
URL = `https://disease.sh/v3/covid-19/countries/${drop}?strict=true`;
}
fetch(URL)
.then((res) => res.json())
.then((data) => setData(data));
});
return (
<div>
{ /* Pass down data as props */ }
<Main data={data} getValue={getValue}/>
</div>
);
};
const Main = ({ data, getValue }) => {
return (
<ul>
{ /* If data is a "thing" then map over it */ }
{data &&
Object.keys(data).map((k) => (
<li key={k}>
{k}: {data[k]}
</li>
))}
</ul>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>

Categories