TypeScript is really great, just for the moment I feel like I work for TypeScript more than TypeScript works for me.
I have a FlatList that renders restaurant results in a Carousel.
const renderRestaurantRows = ({ item }) => (
<Carousel
id={item.id}
name={item.name}
/>
);
const renderBottomSheetRestaurantList = () => (
<View style={[styles.listView, { height: topSnapPoint }]}>
<FlatList
data={restaurants}
keyExtractor={(item) => `row-${item.id}`}
renderItem={renderRestaurantRows}
/>
</View>
);
TypeScript complains about item
with Binding element 'item' implicitly has an 'any' type
- makes sense. So I try to tell it what to expect:
interface RestaurantItem {
item: {
id: string;
name: string;
};
}
const renderRestaurantRows = ({ item }: RestaurantItem) => (
<Carousel
id={item.id}
name={item.name}
/>
);
But then TS complains about data={restaurants]
with Type 'Restaurant[]' is not assignable to type 'readonly { id: string; name: string; imageUrl: string; rating: number; reviews: number; }
and a ton of other information.
Any chance someone can share a solution AND explain how to find such a solution in the future for other similar cases?
restaurants
is fetched from a custom hook. It is defined as an array of Restaurant objects:
interface Restaurant {
id: string;
name: string;
}
export default function useRestaurantSearch() {
const [restaurants, setRestaurants] = useState<Restaurant[] | null>(null);
...
Guys thanks for the comments, following the two suggestions, I rewrote the code:
import useRestaurantSearch from '../hooks/useRestaurantSearch';
export default function RestaurantPage() {
const renderRestaurantRows = ({ item }: Restaurant) => (
<Carousel
id={item.id}
name={item.name}
/>
);
const renderBottomSheetRestaurantList = () => (
<View style={[styles.listView, { height: topSnapPoint }]}>
<FlatList
data={restaurants}
keyExtractor={(item) => `row-${item.id}`}
renderItem={renderRestaurantRows}
/>
</View>
);
This time I am getting an error for { item }: Restaurant
with Cannot find name 'Restaurant'
. And it's not surprising because Restaurant is defined in an external hooks file ../hooks/useRestaurantSearch
. Do I need to import it somehow?
After hours of playing around with this, I got to this point:
const renderRestaurantRows = (result: { item: Restaurant }) => {
return <Carousel {...result.item} />;
};
const renderBottomSheetRestaurantList = () => (
<View style={[styles.listView, { height: topSnapPoint }]}>
<FlatList
data={restaurants}
keyExtractor={(item) => `row-${item.id}`}
renderItem={renderRestaurantRows}
/>
</View>
);
renderItem
generates an object which contains each restaurant data under an item
key. So for example, one of the objects could be:
{
"index": 18,
"item": Object {
"coordinates": Object {
"latitude": 123,
"longitude": -123,
},
"id": "dfg987fshjsdfh",
"name": "Yummy Food",
},
"separators": Object {
"highlight": [Function highlight],
"unhighlight": [Function unhighlight],
"updateProps": [Function updateProps],
},
}
When I pass a result
object, I can let TS know about item
inside the object and casting it to the Restaurant type. I do it with (result: { item: Restaurant }). However, when I try to directly destructure the
resultobject with
({ item }: Restaurant)it gives me the error
Property 'item' does not exist on type 'Restaurant'`. Any idea why?
SectionList s are like FlatList s, but they can have section headers to separate groups of rows. SectionList s render each item in their input sections using the renderSectionHeader and renderItem prop. Each item in sections should be an object with a unique id (the key), and an array data of row data.
FlatList is implemented from the VirtualizedList component that takes care of displaying a limited number of items that will fit in the current view port of your mobile screen. The rest of the data is rendered as the user scrolls. The basic props like data and renderItem can be used to create a FlatList.
By passing extraData={selectedId} to FlatList we make sure FlatList itself will re-render when the state changes. Without setting this prop, FlatList would not know it needs to re-render any items because it is a PureComponent and the prop comparison will not show any changes.
Try importing ListRenderItem and type the const receiving the function:
import { ListRenderItem } from 'react-native';
...
const renderRestaurantRows: ListRenderItem<Restaurant> = ({ item }) => (
<Carousel
id={item.id}
name={item.name}
/>
);
What you're really getting as arguments from the renderItems is:
Where ItemT
is your type, Restaurant
.
Suppose that the item to render is a Movie
:
_renderItem(info: ListRenderItemInfo<Movie>): ReactElement<Movie> {
return <MovieTile movie={info.item} />;
}
render() {
let movies = this.props.movies;
return <FlatList
data={movies}
horizontal={true}
renderItem={this._renderItem}
keyExtractor={(item, i) => item.id.toString()}
/>;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With