Reputation: 65
I'm setting an array of movie objects in useEffect and want them sorted alphabetically. If possible later I want to add the functionality of sorting them by different properties but for now I just want to get over this TypeScript hump. I have a separate types file where I export my movie interface:
export interface Movie {
comments: string,
imdb: string,
mtc: string,
rtc: string,
smn: string,
title: string
}
Which I then import in my page
import { Movie } from '../types';
I call useEffect, request the list of movies from my MongoDB and then want to sort them.
const Database = () => {
const [movies, setMovies] = React.useState<Movie[]>([]);
const [sortType, setSortType] = React.useState('title');
const history = useHistory();
React.useEffect(() => {
Requests.getMovies()
.then(setMovies)
.catch (err => console.log(err));
const types: Movie = {
title: 'title',
rtc: 'rtc',
imdb: 'imdb',
smn: 'smn',
mtc: 'mtc',
comments: 'comments'
};
const sortArray = (sortProperty: string): sortProperty is keyof typeof types => {
const sorted = [...movies].sort((a: Movie, b: Movie) => b[sortProperty] - a[sortProperty] );
return sorted;
}
sortArray(sortType);
setMovies(sortArray);
}, [sortType])
return (
// Bunch of stuff on the page and then map out the movies like so..
<tbody>
{movies.map((movie, index) => (
<tr key={index} onClick={() => history.push(`/edit/${movie.title}`)}>
<td>{movie.title}</td>
<td>{movie.imdb}</td>
<td>{movie.rtc}</td>
<td>{movie.mtc}</td>
<td>{movie.smn}</td>
<td>{movie.comments}</td>
</tr>
))}
</tbody>
//Bunch of other stuff on the page..
)
}
export default Database;
I get the list of movies to show up no problem but sorting them is hard. I found this and this and this but TypeScript keeps giving me errors. On the return sorted
...
Type 'Movie[]' is not assignable to type 'boolean'.
..and on the b[sortProperty]
and a[sortProperty]
.
Element implicitly has an 'any' type because expression of type 'string | number | symbol' can't be used to index type 'Movie'.
No index signature with a parameter of type 'string' was found on type 'Movie'.
How do I get this to work? I thought sort returned the sorted array, but it returns a boolean? Why can't it sort by a property that is defined in the object and also in the interface?
Upvotes: 0
Views: 2446
Reputation: 454
ok here we go, i'll try to explain everything as best as i could.
first of all, you need to split your useEffect
into 2 useEffect statement. useEffect
hook should only handle one thing.
so your code would look like this:
React.useEffect(() => {
Requests.getMovies()
.then(setMovies)
.catch (err => console.log(err));
), []}
React.useEffect(() => {
// your sorting code
}, [/* dependencies */])
the reasoning behind this is, you don't want unneeded action to trigger for every change in your depencies, i.e. you don't want to fetch each time a dependencies change but only when your component mounts.
as far as the issues with your code, the errors have 2 reasons:
also do a change operation through a useEffect
hook is not the most optimal solution, because you'll need the movies
state in your dependencies so you get the latest state, but if you do you'll trigger an infinite loop.
the way i do it, is sort once on component mount using the fetched array and then use an event handler function to trigger to subsequent sorts. this way you would avoid any unintended side effects.
so your code would look like this:
const [movies, setMovies] = React.useState<Movie[]>([]);
const [sortType, setSortType] = React.useState<keyof Movie>("title");
React.useEffect(() => {
Requests.getMovies()
.then(sortMovies)
.catch (err => console.log(err));
), []}
const sortMovies = (moviesParam: Movie[] = movies) => {
const sortArray = [...moviesParam].sort((a: Movie, b: Movie) => {
if (a[sortType] > b[sortType]) return 1;
else if (b[sortType] > a[sortType]) return -1;
return 0;
});
setMovies(sortArray);
}
this code does sort your array alphabetically, and does not cause infinite loop.
also the sortArray
function is not actually needed since you actually using the state anyway so it does not add any value to the code.
if you require further explanation on how and why i did those changes please add a comment and i'll update the post.
Upvotes: 1