I am using MUI aocomplete component.
I have called an API call and feeding 'options' and 'value' with API response.
Following is the code.
// App.js
import { useEffect, useCallback, useState } from "react";
import TextField from "#mui/material/TextField";
import Autocomplete from "#mui/material/Autocomplete";
function App() {
const [getUsers, setUsers] = useState([]);
const fetchUsers = useCallback(async () => {
try {
const response = await fetch(
"https://jsonplaceholder.typicode.com/users"
);
const userData = await response.json();
setUsers(userData);
} catch (error) {
console.log("Error in catch!")
}
}, []);
useEffect(() => {
fetchUsers();
}, [fetchUsers]);
return (
<Autocomplete
disablePortal
id="combo-box-demo"
options={getUsers.name} // should display dropdown options
renderInput={(params) => {
return <TextField {...params} />
}}
value={getUsers.id} // value for each dropdown item
/>
)
}
export default App;
I am getting the following error.
Warning: Failed prop type: The prop `options` is marked as required in `ForwardRef(Autocomplete)`, but its value is `undefined`.
Here is codesandbox link
You need to loop the options because you are retrieving an array, you can do that by using map
options={getUsers.map(el => el.name)}
import { useEffect, useCallback, useState } from "react";
import TextField from "#mui/material/TextField";
import Autocomplete from "#mui/material/Autocomplete";
function App() {
const [getUsers, setUsers] = useState([]);
const fetchUsers = useCallback(async () => {
try {
const response = await fetch(
"https://jsonplaceholder.typicode.com/users"
);
const userData = await response.json();
// console.log("userData: ", userData);
setUsers(userData);
} catch (error) {
console.log("Error in catch!");
}
}, []);
useEffect(() => {
fetchUsers();
}, [fetchUsers]);
return (
<Autocomplete
disablePortal
id="combo-box-demo"
options={getUsers.map(el => el.name)}
renderInput={(params) => {
return <TextField {...params} />;
}}
value={getUsers.id}
/>
);
}
Related
I have a simple react app in which I am fetching the data using jsonplaceholder API and displaying the data with a delete button. I have a function deleteUser inside the User.js component. Now I want to make this deleteUser function a general function so I created a separate hook for it.
Now I want to pass arguments to that custom hook but I get the error
React Hooks must be called in a React function component or a custom
React Hook function react-hooks/rules-of-hooks
userDelete.js
import { useFetch } from "./useFetch";
export const useDelete = (userName) => {
const { data, setData} = useFetch();
const newData = data.filter((dataItem) => dataItem.name !== userName);
console.log(newData);
setData(newData)
};
useFetch.js
import { useState, useEffect } from "react";
export const useFetch = () => {
const [loading, setLoading] = useState(false);
const [data, setData] = useState([]);
const url = `https://jsonplaceholder.typicode.com/users`;
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch(url);
const data = await response.json();
console.log(data);
setData(data);
setLoading(false);
return;
} catch (error) {
console.log("Error", error);
}
};
useEffect(() => {
fetchData();
}, []);
return { loading, data, setData };
};
User.js
import { useFetch } from "../Hooks/useFetch";
import { useDelete } from "../Hooks/useDelete";
const Users = () => {
const { loading, data, setData } = useFetch();
const deleteCallback = useDelete(data);
const deleteUser = (userName) => {
const newData = data.filter((dataItem) => dataItem.name !== userName);
console.log("newData", newData);
setData(newData);
};
return (
<>
<h1>Custom Hook Example</h1>
{loading && <h4>Fetching Data...</h4>}
{data.map((data, index) => (
<div
key={index}
style={{
border: "2px solid red",
width: "fit-content",
margin: "0 auto"
}}
>
<p>Name: {data.name}</p>
<article>Email: {data.email}</article>
{/* <button onClick={(e) => deleteUser(data.name, e)}>Delete</button> */}
<button onClick={() => useDelete(data.name)}>Delete</button>
</div>
))}
{data.length === 0 && (
<>
<p>No Items to Show</p>
</>
)}
</>
);
};
export default Users;
What am I doing wrong ?
I'm trying to render multiple cards by pulling data from the API. But the return is an array, I don't understand why the map is not working.
const CharacterCard = () => {
const [showModal, setShowModal] = useState(false)
const openModal = () => {
setShowModal(prev => !prev)
}
const characters = useRequestData([], `${BASE_URL}/characters`)
const renderCard = characters.map((character) => {
return (
<CardContainer key={character._id} imageUrl={character.imageUrl}/>
)
})
return (
<Container>
{renderCard}
<ModalScreen showModal={showModal} setShowModal={setShowModal} />
</Container>
)
}
export default CharacterCard
The hook is this
import { useEffect, useState } from "react"
import axios from "axios"
const useRequestData = (initialState, url) => {
const [data, setData] = useState(initialState)
useEffect(() => {
axios.get(url)
.then((res) => {
setData(res.data)
})
.catch((err) => {
console.log(err.data)
})
}, [url])
return (data)
}
export default useRequestData
console error image
requisition return image
API: https://disneyapi.dev/docs
Looks like the default value of the characters is undefined.
So something like (characters || []).map.. will help I think.
For deeper look at this you can debug useRequestData hook, as I can't see the source of that hook from you example
I have the following problem: I use a fatch API to get all the products I have registered in my database (mongodb), then I store the result in a slice called products-slice which has an array as its initial state empty. Until then everything is in order. As I need information the time the homepage is loaded, I use the useEffect hook to fetch the products I have registered. Then I pass this array as props to a component, and make a map. The problem is that when the component loads, the information is not local.
código do backend
module.exports.fetchProduct = async (req, res) => {
try {
const products = await Product.find({});
if (products) {
res.json(products);
}
} catch (error) {
console.log(error);
}
};
productsActions.js
export const fetchProducts = () => {
return async (dispatch) => {
try {
const response = await fetch("http://localhost:xxxx/xxxxxx");
const data = await response.json();
let loadedProducts = [];
for (const key in data) {
loadedProducts.push({
id: data[key]._id,
productName: data[key].productName,
price: data[key].price,
imageUrl: data[key].imageUrl,
});
}
dispatch(setProducts(loadedProducts));
} catch (error) {
console.log(error);
}
};
};
home.jsx
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Container } from "react-bootstrap";
import {fetchProducts} from '../../store/actions/productsActions';
import Hero from "../hero/Hero";
import Footer from "../footer/Footer";
import DisplayProductsList from "../displayProduct/DisplayProductsList";
export default function Home() {
const productsInfo = useSelector((state) => state.products.products);
console.log(productsInfo);
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchProducts());
}, [dispatch]);
return (
<>
<Hero />
<DisplayProductsList products={productsInfo} />
<Container fluid>
<Footer></Footer>
</Container>
</>
);
}
product-slice.js
const initialState = {
products: [],
};
const productSlice = createSlice({
name: "product",
initialState,
reducers: {
setProducts(state, action) {
state.products.push(action.payload);
},
},
});
export const { setProducts } = productSlice.actions;
export default productSlice.reducer;
component where I'm mapping
export default function DisplayProductsList(props) {
console.log(props);
return (
props.products.map((product) => (
<DisplayProducts
key={product.id}
imageUrl={product.imageUrl}
name={product.productName}
price={product.price}
/>
))
);
}
console.log output in the above component
enter image description here
I am learning React as I am fetching data from Pokéapi to make a list component, card component, detail component and filter component. I am trying to make a filter so you can filter by pokémon type. Only the cards that also contain that type string should then render (Not there yet). So I am not sure if a) I should make a different call from API inside PokemonList depending on selected value or b) if I should compare the values and just change how the PokemonCard element is rendered inside PokemonList.js depending on the comparison. I managed to pass data from filter to the list component. I have then been trying to pass the type data from PokemonCard.js to the list component so that I can compare these two values but I find it hard to use callbacks to pass the type data from the card component, since I dont pass it through an event or something like that.
Which method should I use here to simplify the filtering? Make different API call or render PokemonCard element conditionally?
Is it a good idea to compare filter option to pokemon card's type in PokemonList.js? Then how can I pass that data from the card component since I don't pass it through click event?
Thankful for any ideas! I paste the code from list component that contains the cards, card component and filter component.
PokemonList component:
import { useState } from 'react';
import useSWR from 'swr';
import PokemonCard from './PokemonCard';
import PokemonFilter from './PokemonFilter';
import './PokemonList.css';
const PokemonList = () => {
const [index, setIndex] = useState(0);
const [type, setType] = useState('');
function selectedType(type) { // value from filter dropdown
setType(type)
console.log("handled")
console.log(type)
}
const url = `https://pokeapi.co/api/v2/pokemon?limit=9&offset=${index}`;
const fetcher = (...args) => fetch(...args).then((res) => res.json())
const { data: result, error } = useSWR(url, fetcher);
if (error) return <div>failed to load</div>
if (!result) return <div>loading...</div>
result.results.sort((a, b) => a.name < b.name ? -1 : 1);
return (
<section>
<PokemonFilter onSelectedType={selectedType} selectedPokemonType={type} />
<div className="pokemon-list">
<div className="pokemons">
{result.results.map((pokemon) => (
<PokemonCard key={pokemon.name} pokemon={pokemon} /> // callback needed??
))}
</div>
<div className="pagination">
<button
onClick={() => setIndex(index - 9)}
disabled={result.previous === null}
>
Previous
</button>
<button
onClick={() => setIndex(index + 9)}
disabled={result.next === null}
>
Next
</button>
</div>
</div>
</section>
)
}
export default PokemonList;
PokemonCard component:
import { Link } from "react-router-dom";
import useSWR from 'swr';
import './PokemonCard.css';
const PokemonCard = ({ pokemon }) => {
const { name } = pokemon;
const url = `https://pokeapi.co/api/v2/pokemon/${name}`;
const { data, error } = useSWR(url);
if (error) return <div>failed to load</div>
if (!data) return <div>loading...</div>
const { types, abilities } = data;
// types[0].type.name <---- value I want to pass to PokemonList.js
return (
<div className='pokemon-card'>
<div className='pokemon-card__content'>
<img
className='pokemon-card__image'
src={data.sprites.front_default}
alt={name}
/>
<div className='pokemon-card__info'>
<p className='pokemon-card__name'>Name: {name}</p>
<p className='pokemon-card__abilities'>Abilities: {abilities[0].ability.name}</p>
<p className='pokemon-card__categories'>Category: {types[0].type.name}</p>
</div>
</div>
<Link className='pokemon-card__link' to={{
pathname: `/${name}`,
state: data
}}>
View Details
</Link>
</div>
)
}
export default PokemonCard;
PokemonFilter component:
import './PokemonFilter.css';
import useSWR from 'swr';
const PokemonFilter = ({onSelectedType, selectedPokemonType}) => {
const url = `https://pokeapi.co/api/v2/type/`;
const fetcher = (...args) => fetch(...args).then((res) => res.json())
const { data: result, error } = useSWR(url, fetcher);
if (error) return <div>failed to load</div>
if (!result) return <div>loading...</div>
function filteredTypeHandler(e) {
console.log(e.target.value);
onSelectedType(e.target.value);
}
console.log(selectedPokemonType)
return(
<div className="pokemon-types__sidebar">
<h2>Filter Pokémon by type</h2>
<select
name="pokemon-type"
className="pokemon-types__filter"
onChange={filteredTypeHandler}
>
<option value="All">Filter By Type</option>
{result.results.map((type) => {
return (
<option key={type.name} value={type.name}> {type.name}</option>
)
})}
</select>
</div>
)
}
export default PokemonFilter;
Here is an example to improve, modify, ... I didn't test, it's just a visual example.
I don't know about useSWR sorry, I use axios in my example...
If you want to centralize all your API requests, you can create a useApi hook, on the internet you will find tutorials.
PokemonList.js
import React, { useState, useEffect } from 'react';
import axios from 'axios'; // or swr
import PokemonFilter from './PokemonFilter';
import PokemonCard from './PokemonCard';
export default function PokemonList() {
const [data, setData] = useState([]);
const [filter, setFilter] = useState('');
// Executed every first render
useEffect(() => {
getData();
}, []);
// Executed only when filter changes
useEffect(() => {
getDataByTypes(filter);
}, [filter]);
// Get data
const getData = async () => {
const uri = 'https://xxx';
try {
const response = await axios.get(uri);
setData(response.data...);
} catch (error) {
console.log(error);
}
};
// Get data by types
const getDataByTypes = async (filter) => {
const uri = `https://xxx/type/${filter}...`;
if (filter) {
try {
const response = await axios.get(uri);
setData(response.data...);
} catch (error) {
console.log(error);
}
}
};
return (
<div className="main">
<PokemonFilter filter={filter} setFilter={setFilter} />
<div className="container">
<div className="cards-container">
{data.map((d) => (
<PokemonCard key={d.name} data={d} />
))}
</div>
</div>
</div>
);
}
PokemonCard.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';
export default function PokemonCard({ data }) {
const [pokemons, setPokemons] = useState();
useEffect(() => {
getPokemons(data);
}, [data]);
// Get Pokemons
const getPokemons = async (data) => {
const uri = `https://xxx/pokemon/${data.name}/`;
try {
const response = await axios.get(uri);
setPokemons(response.data...);
} catch (error) {
console.log(error);
}
};
return (
<div>
{pokemons && (
<div className="card">
<img src={pokemons.sprites.front_default} alt={pokemons.name} />
<p>{pokemons.name}</p>
<p>{pokemons.abilities[0].ability.name}</p>
<p>{pokemons.types[0].type.name}</p>
</div>
)}
</div>
);
}
PokemonFilter.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';
export default function PokemonFilter({ filter, setFilter }) {
const [types, setTypes] = useState([]);
useEffect(() => {
getType();
}, []);
// Get Type
const getType = async () => {
const uri = 'https://xxx/type/';
try {
const response = await axios.get(uri);
setTypes(response.data.results....);
} catch (error) {
console.log(error);
}
};
const handleFilter = (e) => {
setFilter(e.target.value);
};
return (
<select onChange={handleFilter} value={filter}>
<option>Filter by type</option>
{types.map((type) => {
return (
<option key={type.name} value={type.name}>
{type.name}
</option>
);
})}
</select>
);
}
I want to render a list from a JSON URL. However, I have the following error: Objects are not valid as a React child (found: TypeError: Failed to fetch). If you meant to render a collection of children, use an array instead. What am I going wrong? Thanks for your answer
//hooks.tsx
import { useEffect, useState } from 'react'
export const useFetch = () => {
const [data, setData] = useState([])
const [loading, setLoading] = useState(false)
const [error, setError] = useState(null)
useEffect(() => {
setLoading(true)
setError(null)
fetch('https://jsonkeeper.com/b/Z51B')
.then(res => res.json())
.then(json => {
setLoading(false)
if (json.data) {
setData(json.data)
} else {
setData([])
}
})
.catch(err => {
setError(err)
setLoading(false)
})
}, [])
return { data, loading, error }
}
//index.tsx
import React from 'react';
import { useFetch } from "./hooks.js";
import {CardItem} from './card';
export const List = () => {
const { data, loading, error } = useFetch()
if (loading) return <div>Loading...</div>
if (error) return <div>{error}</div>
return (
<>
<ul>
{data.map((item: any, index: any) => (
<li key={index}>
{item.names.map((name: any) => {
return <CardItem
family={item.family}
name={name}
/>
})
}
</li>
))}
</ul>
</>
);
};
I guess the problem is with your setError(err) in catch block of your custom hook. It should be setError(err.message).