I'm trying to make a filter of all my "localtypes" but when I check the console on my browser it shows me an empty array. I want to get to the localtypes propertys of the API I'm using
I tried to use this filter
const type = (category) => {
const clonArray = [...restaurants]
const filter = clonArray.filter((restaurant) => {
return restaurant.localtype == category;
})
setRestaurants(filter);
}
sending the props to another component "Filter" as
categories={() => type()}
but when i get to these props in the Filter component i get an empty array
onClick={() => {categories("farmacia")}}>
I want to know how to access to the props of "categories"
You can do it in below order. First thing to note is that .filter does not mutate the original array, so you can use it directly.
You need to pass the value with function to the Filter component.
onClick={() => categories('farmacia'}
categories={(cat) => type(cat)}
const type = (category) => {
const filter = clonArray.filter((restaurant) => {
return restaurant.localtype === category;
})
setRestaurants(filter);
}
Related
I'm trying to learn to react online and I understood everything except this line code
const removeItem = (id) => {
let newPeople = people.filter((person) => person.id !== id);
setPeople(newPeople);
};
especially how person.id !== idremoves the item from list and add to new list
here is the full code
import React from 'react';
import { data } from '../../../data';
const UseStateArray = () => {
const [people, setPeople] = React.useState(data);
const removeItem = (id) => {
let newPeople = people.filter((person) => person.id !== id);
setPeople(newPeople);
};
return (
<>
{people.map((person) => {
const { id, name } = person;
return (
<div key={id} className='item'>
<h4>{name}</h4>
<button onClick={() => removeItem(id)}>remove</button>
</div>
);
})}
<button className='btn' onClick={() => setPeople([])}>
clear items
</button>
</>
);
};
export default UseStateArray;
first you shold khow how filter works,
The filter() method creates a new array filled with elements that pass a test provided by a function.
in your case test is person.id !== id,
if test passed for an element that element will be in new array.
otherwise element will not be in new array. is it clear?
The filter method creates a shallow copy of an array but not the whole array but only those elements that fulfills the predicate.
So newPeople will contain a copy of all the elements within people that it's people[element].id is different than id.
Visit https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter for additional details of filter method.
I want to return a tag from a method so that once I call the method the tags are returned and these tags are used to call a component.
const Populate =() =>
{
let tags = [];
analysisinfo.forEach((_item,index) =>
{
if(analysisinfo[index].id!== defaultid)
tags.push(<AU props={analysisinfo[index]} instance ={index}/>);
console.log({tags});
}
return tags;
let me explain in-detail the analysisinfo is an useState array which contains the below values. These values are being passed into this method.
0:
cpu: true
hdd: true
id: "00000000000000000000000000000000"
ram: true
And pushes these values into tags[]. I want them pushed into array in the form of a <>, so that when I return it calls the <AU..../> tag component which takes the above values as props.
Use a combination of filter and map:
const Populate = () => (
<>
{
analysisinfo
.filter(i => i.id !== defaultid)
.map((i, idx) => (
<AU props={i} instance={idx} />
))
}
</>
);
I have two api requests that return JSON objects. They return an array of objects.
One API request that I make is fine and allows me to update the state with the response, but the other one (below) doesn't and I don't understand why.
API request to fetch genres list:
async getGenreList() {
const genresResults = await getGenres();
return genresResults;
}
The request:
export const getGenres = async () => {
try {
const response = await axios.get(
"https://api.themoviedb.org/3/genre/movie/list?api_key=<APIKEY>&language=en-US"
);
const { genres } = response.data;
return genres;
} catch (error) {
console.error(error);
}
};
The response is an array of 19 genre objects but this is just an example:
[
{id: 28, name: "Action"},
{id: 12, name: "Adventure"}
]
I then want to update the state like this and pass the response to genreOptions. But it tells me Error: Objects are not valid as a React child (found: object with keys {id, name}). If you meant to render a collection of children, use an array instead.
componentDidMount() {
this.getGenreList().then((response) => {
console.log(response)
this.setState({ genreOptions: response});
});
}
The below works when i update the state and map over it but I don't want to do that, i want to pass the whole response down so i can map over the data in my component as I need it there to do some data matching.
this.setState({ genreOptions: response.map((genreOption) => {
return genreOption.name
})});
This is the state:
this.state = {
results: [],
movieDetails: null,
genreOptions: [],
};
I want to pass the genreOptions here to genres then map over it in the MovieResults component.
<MovieResults>
{totalCount > 0 && <TotalCounter>{totalCount} results</TotalCounter>}
<MovieList movies={results || []} genres={genreOptions || []} />
</MovieResults>
Why can't I? Any ideas? I have done it for another similar request :S
UPDATE TO SHOW MOVIELIST COMPONENT
export default class MovieList extends React.Component {
render() {
const { movies, genres } = this.props;
const testFunction = (movieGenreIds) => {
const matchMovieGenresAndGenreIds = genres.map((genreId) => {
const matchedGenres = movieGenreIds.find((movieGenre) => {
return movieGenre.id === genreId
})
return matchedGenres // this returns the matching objects
})
const result = matchMovieGenresAndGenreIds.filter(Boolean).map((el)=> {
return el.name
})
return result
}
return (
<MoviesWrapper>
{movies.map((movie) => {
const {
title,
vote_average,
overview,
release_date,
poster_path,
genre_ids
} = movie;
return (
<MovieItem
title={title}
rating={vote_average}
overview={overview}
release={release_date}
poster={poster_path}
movieGenres={testFunction(genre_ids)}
/>
);
})}
</MoviesWrapper>
);
}
}
**** MOVIE ITEM COMPONENT***
export default class MovieItem extends React.Component {
render() {
const { title, overview, rating, release, poster, movieGenres } = this.props;
return (
// The MovieItemWrapper must be linked to the movie details popup
<MovieItemWrapper>
<LeftCont>
<img
className="movie-img"
src={`https://image.tmdb.org/t/p/w500${poster}`}
/>
</LeftCont>
<RightCont>
<div className="movie-title-container">
<h2 className="movie-title">{title}</h2>
<Rating>{rating}</Rating>
</div>
<div>{movieGenres}</div>
<p>{overview}</p>
<p>{release}</p>
</RightCont>
</MovieItemWrapper>
);
}
}
Please follow this steps to fix your code. I'll try yo explain what's happening along the way:
In your main component. Set the state to the value that you really want to pass to your child component. Remember that response will be an array of objects.
componentDidMount() {
this.getGenreList().then((response) => {
this.setState({genreOptions: response});
});
}
In your MovieList component. Please check your testFunction to respect data types. The following code will return you an array of strings containing the names of the genres that are included in the movies genres array.
const testFunction = (movieGenreIds) => {
return genres
.filter((genre) => {
return movieGenreIds.includes(genre.id);
})
.map((genre) => genre.name);
};
In your MovieItem component. (This is were the real problem was)
Instead of:
<div>{movieGenres}</div>
You may want to do something like this:
<div>{movieGenres.join(' ')}</div>
This converts your array into a string that can be rendered. Your error was due to the fact that you were passing there an array of objects that React couldn't render.
If you have any doubt, please let me know.
NOTE: I suggest you to use a type checker to avoid this kind of problems. And to be consistent with your variables naming conventions.
Update based on new information from chat:
In your ExpandableFilters component, you must fix the following piece of code to get the genre name (string). As explained in chat, you can't have objects as a result for a JSX expression ({}), but only primitives that can be coerced to strings, JSX elements or an array of JSX elements.
<GenreFilterCont marginTop>
{filtersShown && (
<ExpandableFiltersUl>
{this.props.movieGenres.map((genre, index) => {
return (
<ExpandableFiltersLi key={index}>
<Checkbox />
{genre.name}
</ExpandableFiltersLi>
);
})}
</ExpandableFiltersUl>
)}
</GenreFilterCont>
Please also note that I've added a key property. You should do it whenever you have a list of elements to render. For more about this I will refer you to the React Docs.
I have JSON data. I need only specific key-value pairs to be passed as data to a component. I used delete and deleted existing key-value pair except what I needed so that after deleting, only key-value pairs that I needed would exist. I got my code working perfectly but, I'm not satisfied by using delete to make my code work. I need the JSON data filtered with the key-value pairs that I specify.
JSON data will be like,
commentProps = {
comments:"example",
author:"authorName",
ownerId:"1029989",
objectId:"152039900"
};
Code that I'm using right now,
renderItem={props => {
const commentProps = Object.assign({}, props);
delete commentProps.ownerId;
delete commentProps.objectId;
return <Comment {...commentProps}/>
}}
This returns JSON data as,
commentProps = {comments:"example",author:"authorName"};
I need this to achieve without using delete. Any help would be appreciated. Thanks in advance!
// Using rest operator ignore delete props:
renderItem = props => {
const {ownerId, objectId, ...rest} = props
return <Comment {...rest} />;
};
// Best and reccomended, you should not let go all props:
renderItem = props => {
const {comments, author} = props
return <Comment comments={comments} author={author} />;
};
With your solution
renderItem={props => {
const commentProps = Object.assign({}, {comments: props.comments, author: props.author});
return <Comment {...commentProps}/>
}}
I'm attempting to map over data I received from an API call. Getting shallow endpoints works fine, but anything nested gives me an error.
The goal is to get all of the opening themes and display them in a 'ul'.
The exact error "TypeError: anime.opening_themes is undefined"
Repo to the project
Heres the endpoints.
Heres my component.
const AnimeDetails = (props) => {
const API = 'https://api.jikan.moe/v3/anime'
const initialState = {
anime: []
}
const [anime, setAnime] = useState(initialState)
useEffect(() => {
const getAnime = async () => {
const response = await fetch(`${API}/${props.match.params.animeId}`)
const data = await response.json()
console.log(data);
setAnime(data) // set initial state to hold data from our API call
}
getAnime()
}, []) // [] prevents useEffect from running in an infinite loop
return (
<AnimeDetailsWrapper>
<Title>{anime.title}</Title>
<Details>
{anime.opening_themes
.map((song, index) => (
<li key={index}>{song}</li>
))}
</Details>
</AnimeDetailsWrapper>
)
}
Your initial state is an empty array, not an empty object:
const initialState = {
anime: []
}
When your component mounts, there is no data yet, so you're attempting to render [].opening_themes.map, and obviously there is no opening_themes property on an empty array.
Set your initial state to an empty object instead:
const initialState = {}
And you will probably want to test that you have data before attempting to render it:
return anime.mal_id && (
<AnimeDetailsWrapper>
<Title>{anime.title}</Title>
<Details>
{anime.opening_themes
.map((song, index) => (
<li key={index}>{song}</li>
))}
</Details>
</AnimeDetailsWrapper>
)
This will prevent your component from rendering anything until your anime state contains a mal_id property.
The first time you render your component, the state anime is equal to {anime: []}, which has no property called opening_themes.
you should try like this
first, remove initialState code and use direct code like following
if the response is in the form of an array
const [anime, setAnime] = useState([])
if the response is in the form of an object
const [anime, setAnime] = useState({})
otherwise, null will work with any response
const [anime, setAnime] = useState(null)
return code like this
return (
<> {anime && <AnimeDetailsWrapper>
<Title>{anime.title}</Title>
<Details>
{anime.opening_themes
.map((song, index) => (
<li key={index}>{song}</li>
))}
</Details>
</AnimeDetailsWrapper>}</>
)