Bootstrap-like col-row-grid functionality on React Native? - layout

It will be better if I demonstrate with images.
This is what I am trying to achieve. Assume landscape mode tablet size. Let's say I have X amount of elements in array. I want to map it across the row until it has 3 items in a row, then goes down. With bootstrap, I can do something like col-md-4 three times.
Currently I am using native-base. It has an interesting grid systems, but not exactly what I wanted.
This is what I have right now:
<Grid>
<Row>
{ recipeStore.categories.map((category, index) => {
return (
<Col key={index} style={{height: 300}}>
<Card>
<CardItem>
<Body>
<Text>{ category.name }</Text>
</Body>
</CardItem>
</Card>
</Col>
)
})}
</Row>
</Grid>
How can I get the array iteration to fill out 3 columns then goes to the next row?

You can use flexWrap: 'wrap' on the parent contain and then use flexBasis on the children.
import React, { Component } from 'react';
import { View, StyleSheet } from 'react-native';
const ABox = () => <View style={styles.box} />;
export default class App extends Component {
render() {
return (
<View style={styles.container}>
<ABox />
<ABox />
<ABox />
<ABox />
<ABox />
<ABox />
<ABox />
<ABox />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexWrap: 'wrap',
flexDirection: 'row',
paddingTop: 20,
},
box: {
flexBasis: 75,
borderWidth: 1,
borderColor: 'black',
height: 40,
margin: 10,
}
});
Snack: https://snack.expo.io/HkUFUp7ub

The best way to do this is using the FlatList it is good in performance and easy to use. And it is recommended for making a list (see this). Additionally you don't need to add any extra package to your project.
You can easily use the FlatList as below:
_keyExtractor = (item, index) => index;
_renderItem = ({item: category}) => (
<Card>
<CardItem>
<Body>
<Text>{ category.name }</Text>
</Body>
</CardItem>
</Card>
);
render() {
return (
<FlatList
numColumns={3}
data={recipeStore.categories}
keyExtractor={this._keyExtractor}
renderItem={this._renderItem}
/>
);
}
Note: you should use the numColumns peroperty to define the number of items in each row.
You can see the documentation of FlatList in here.

Related

Resizing book cover according to display size (express + react)

I'm working on a website where users can read book. In this, homepage showcase multiple books but I want this page to change the size of book cover depending upon display size and not crop them.
I'm also using mui library for creating ui elements.
Home.js
import { ImageList, ImageListItem, ImageListItemBar,
Typography, Link, Grid, Divider } from '#mui/material'
import { useContext } from 'react'
import { useNavigate } from 'react-router-dom'
import { PageLayout, Context } from '../Components/PageLayout'
function Feed({ catg, catgTitle, routeName = "" }) {
const navg = useNavigate();
const bookCatg = useContext(Context)[0][catg];
return (
<>
<Grid
container
direction="row"
justifyContent="space-between"
alignItems="center"
>
<Grid item>
<Typography variant="h6">{catgTitle}</Typography>
</Grid>
<Grid item>
<Link
href={`/book/${routeName}`}
variant="body1"
underline="none"
component="button"
>
More..
</Link>
</Grid>
</Grid>
<Divider />
<ImageList
sx={{ pt: '10px' }}
cols={4}
spacing={1}
rowHeight={250}
>
{bookCatg.map((book) => (
<ImageListItem key={book._id}>
<img
onClick={() => navg(`/book/${book._id}`)}
src={book.img}
sx={{ height: "50px" }}
alt='Not Found'
loading="lazy"
/>
<ImageListItemBar
onClick={() => navg(`/book/${book._id}`)}
sx={{ width: '10vw' }}
title={book.title}
position="below"
/>
</ImageListItem>
))}
</ImageList>
</>
)
}
export default function Home(){
var element = <>
<Feed catgTitle={'New Arrival'}
catg={'newArrival'} routeName={'newest'} />
<Feed catgTitle={'Popular Books'}
catg={'popRating'} routeName={'popular'} />
<Feed catgTitle={'Hot Rated'}
catg={'hotRating'} routeName={'hot'} />
<Feed catgTitle={'Rank'}
catg={'ranking'} routeName={'ranking'} />
</>;
return (
<PageLayout
url="/book"
gridElem={element}
failureMsg="Error"
/>
)
}
PageLayout.jsx
import { Grid, Typography } from '#mui/material'
import { useState, useEffect, createContext } from 'react'
import axios from 'axios'
import Navbar from './Navbar'
import AppFormNav from './AppFormNav'
import Loading from './Loading'
export const Context = createContext({});
export function PageLayout({ url, nav='normal', elem, gridElem, failureMsg }){
const NavType = {
'normal': <Navbar />,
'form': <AppFormNav />
};
const [info, setInfo] = useState({});
const fetchData = async() => {
axios.get(url)
.then((resp) => {
if(resp.status === 200)
setInfo({status: 200, data: resp.data.data});
else if(resp.status === 204)
setInfo({ status: 204, data: resp.message });
})
.catch((err) => {
setInfo({ status: 400, data: err.response.data.data })
// console.log("Bad Request", err.resp.status);
})
};
useEffect(() => {
fetchData();
}, []);
var gridElement = <Loading />, element, message;
if(info.status === 200){
gridElement = gridElem;
element = elem;
}
else if (info.status === 204){
gridElement=<></>;
message = failureMsg ? failureMsg : info.data;
}
return (
<Context.Provider value={info?.data}>
<Grid container sx={{ justifyContent: 'center' }}>
<Grid item> { NavType[nav] } </Grid>
<Grid item xs={12}> { element } </Grid>
<Grid item xs={10}>
<Typography sx={{ justifyContent: "center", alignItems: "center" }}> {message} </Typography>
</Grid>
<Grid item xs={ 10 }> { gridElement } </Grid>
</Grid>
</Context.Provider>
)
}
current look
As you can see the bookcover of different size take up space of other book cover.
Home.jsx
import { ImageList, ImageListItem, ImageListItemBar,
Typography, Link, Grid, Divider } from '#mui/material'
import { useContext } from 'react'
import { useNavigate } from 'react-router-dom'
import { PageLayout, Context } from '../Components/PageLayout'
function Feed({ catg, catgTitle, routeName = "" }) {
const navg = useNavigate();
const bookCatg = useContext(Context)[0][catg];
return (
<>
<Grid
container
direction="row"
justifyContent="space-between"
alignItems="center"
>
<Grid item>
<Typography variant="h6">{catgTitle}</Typography>
</Grid>
<Grid item>
<Link
href={`/book/${routeName}`}
variant="body1"
underline="none"
component="button"
>
More..
</Link>
</Grid>
</Grid>
<Divider />
<ImageList
sx={{ pt: '10px' }}
cols={4}
spacing={1}
// rowHeight={250}
>
{bookCatg.map((book) => (
<ImageListItem key={book._id}>
<img
onClick={() => navg(`/book/${book._id}`)}
src={book.img}
//changed start from here
style={{ aspectRatio: "0.8", resize: "auto", objectFit: "scale-down" }}
//change end here
alt='Not Found'
loading="lazy"
/>
<ImageListItemBar
onClick={() => navg(`/book/${book._id}`)}
sx={{ width: '10vw' }}
title={book.title}
position="below"
/>
</ImageListItem>
))}
</ImageList>
</>
)
}
export default function Home(){
var element = <>
<Feed catgTitle={'New Arrival'}
catg={'newArrival'} routeName={'newest'} />
<Feed catgTitle={'Popular Books'}
catg={'popRating'} routeName={'popular'} />
<Feed catgTitle={'Hot Rated'}
catg={'hotRating'} routeName={'hot'} />
<Feed catgTitle={'Rank'}
catg={'ranking'} routeName={'ranking'} />
</>;
return (
<PageLayout
url="/book"
gridElem={element}
failureMsg="Error"
/>
)
}
just added this style to img tag
style={{ aspectRatio: "0.8", resize: "auto", objectFit: "scale-down"
}}

How to make Material-UI GridListTitleBar and Image a Link in ReactJS

I am using Material UI GridList to display a list of Events in React. Everything is working fine except that I am not able to make Title or Image a Link. Does anyone know how to make Title and Image a Link?
Here is my Component.
<div className={classes.root}>
<GridListTile key="Subheader" style={{ height: "auto" }}>
<ListSubheader component="div">This is List of Events</ListSubheader>
</GridListTile>
<GridList
cellHeight={330}
cols={matches ? 1 : 3}
className={classes.gridList}
spacing={12}
>
{tileData.length > 0 &&
tileData.map((tile, index) => {
return (
<GridListTile
key={Math.floor(Math.random() * new Date().getTime())}
>
<img src={tile.eventImage} alt={tile.title} />
<GridListTileBar
title={tile.title}
subtitle={<span>by: {tile.description}</span>}
actionIcon={<IconButton title={tile.title} />}
/>
</GridListTile>
);
})}
</GridList>
</div>
);
Update
This is an update to the answer that was given below. The first image is now smaller than the rest of the images after update the code with the solution given below.
Here is the new code that I am trying:
<div className={classes.root}>
<GridListTile key="Subheader" style={{ height: "auto" }}>
<ListSubheader component="div">This is List of Events</ListSubheader>
</GridListTile>
<GridList
cellHeight={330}
cols={matches ? 1 : 3}
className={classes.gridList}
spacing={12}
>
{tileData.length > 0 &&
tileData.map((tile, index) => {
return (
<GridListTile
key={Math.floor(Math.random() * new Date().getTime())}
>
<a href={"events/" + tile._id + "/eventcomments"}>
<img
src={tile.eventImage}
alt={tile.title}
className="MuiGridListTile-imgFullHeight"
/>
<GridListTileBar title={tile.title} />
</a>
</GridListTile>
);
})}
</GridList>
</div>
Try wrapping the image with an anchor tag like this:
// ...
<GridListTile key={Math.floor(Math.random() * new Date().getTime())}>
<a href="https://www.google.com/">
<img src={tile.eventImage} alt={tile.title} className="MuiGridListTile-imgFullHeight" />
</a>
<GridListTileBar
title={tile.title}
subtitle={<span>by: {tile.description}</span>}
actionIcon={<IconButton title={tile.title} />}
/>
</GridListTile>
// ...
It is important, after wrapping the image inside an anchor tag to add the class MuiGridListTile-imgFullHeight to the image to keep the same styling of the grid. Normally this class is added automatically, but if you wrap it inside an anchor tag it isn't. So you need to add it manually.
Update
The image shows expected behavior, because your first image is not wide enough to cover the whole tile and you only added the class to scale the img to full height. There is also a class to scale to full width: MuiGridListTile-imgFullWidth, but you can't use both of these classes together for your use case as the styles conflict (see the style definition here: https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/GridListTile/GridListTile.js).
You can try to set the width to 100% in the image style prop:
<img
// ...
style={{ width: "100%" }}
className="MuiGridListTile-imgFullHeight"
/>
I eventually managed to get it to work without breaking or stretching my images, and here is what I did.
<div className={classes.root}>
<GridListTile key="Subheader" style={{ height: "auto" }}>
<ListSubheader component="div">This is List of Events</ListSubheader>
</GridListTile>
<GridList
cellHeight={330}
cols={matches ? 1 : 3}
className={classes.gridList}
spacing={12}
>
{tileData.length > 0 &&
tileData.map((tile, index) => {
return (
<GridListTile
component={Link}
to={"/events/" + tile._id + "/eventcomments"}
key={Math.floor(Math.random() * new Date().getTime())}
>
<img src={tile.eventImage} alt={tile.title} />
<GridListTileBar title={tile.title} />
</GridListTile>
);
})}
</GridList>
</div>
const Gallery = () => {
const classes = useStyles();
return (
<Fragment>
<div style={{background: "black"}}>
<Toolbar/>
<Grid container spacing={0}>
<Grid item xs={12}>
<Paper className={classes.paper}>GALERIA</Paper>
<div className={classes.root}>
<GridList cellHeight={280} className={classes.gridList} cols={1}>
{tileData.map((tile) => (
<GridListTile style={{ width: "100%" }} key={tile.img} cols={tile.cols || 1}>
<img src={tile.img} alt={tile.title} />
</GridListTile>
))}
</GridList>
</div>
</Grid>
</Grid>
</div>
</Fragment>
)
}
export default Gallery;
You can change the type of underlying component rendered by the GridListTime Material-UI component. Just add the component="a" to have it render as an anchor tag. Then you may specify an href prop to pass in your route. See below:
<GridList cellHeight={160} className={classes.gridList} cols={3}>
{tileData.map((tile) => (
<GridListTile key={tile.img} cols={tile.cols || 1} component="a" href="/">
<img src={tile.img} alt={tile.title} />
</GridListTile>
))}
</GridList>

How to set up a location of menubar with Material UI

I recently started learning about the Material UI. To develop a basic website structure while checking the basic template code.
However, certain items in the menu bar are not on the right side as in the example. I have taken most of the code as is and merged it, and I wonder why it doesn't move to the right.
CodeSandBox URL
Here's my full code but it seems quite long so I also add codesandbox URL
Full Code
import React from "react";
import clsx from "clsx";
import { makeStyles, useTheme } from "#material-ui/core/styles";
import Drawer from "#material-ui/core/Drawer";
import CssBaseline from "#material-ui/core/CssBaseline";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import List from "#material-ui/core/List";
import Typography from "#material-ui/core/Typography";
import Divider from "#material-ui/core/Divider";
import IconButton from "#material-ui/core/IconButton";
import MenuIcon from "#material-ui/icons/Menu";
import ChevronLeftIcon from "#material-ui/icons/ChevronLeft";
import ChevronRightIcon from "#material-ui/icons/ChevronRight";
import ListItem from "#material-ui/core/ListItem";
import ListItemIcon from "#material-ui/core/ListItemIcon";
import ListItemText from "#material-ui/core/ListItemText";
import InboxIcon from "#material-ui/icons/MoveToInbox";
import MailIcon from "#material-ui/icons/Mail";
import Badge from "#material-ui/core/Badge";
import Menu from "#material-ui/core/Menu";
import MenuItem from "#material-ui/core/MenuItem";
import NotificationsIcon from "#material-ui/icons/Notifications";
import AccountCircle from "#material-ui/icons/AccountCircle";
import MoreIcon from "#material-ui/icons/MoreVert";
const drawerWidth = 240;
const useStyles = makeStyles(theme => ({
root: {
display: "flex"
},
appBar: {
transition: theme.transitions.create(["margin", "width"], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
})
},
appBarShift: {
width: `calc(100% - ${drawerWidth}px)`,
marginLeft: drawerWidth,
transition: theme.transitions.create(["margin", "width"], {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen
})
},
menuButton: {
marginRight: theme.spacing(2)
},
hide: {
display: "none"
},
drawer: {
width: drawerWidth,
flexShrink: 0
},
drawerPaper: {
width: drawerWidth
},
drawerHeader: {
display: "flex",
alignItems: "center",
padding: theme.spacing(0, 1),
...theme.mixins.toolbar,
justifyContent: "flex-end"
},
content: {
flexGrow: 1,
padding: theme.spacing(3),
transition: theme.transitions.create("margin", {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
}),
marginLeft: -drawerWidth
},
contentShift: {
transition: theme.transitions.create("margin", {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen
}),
marginLeft: 0
}
}));
export default function User() {
const classes = useStyles();
const theme = useTheme();
const [open, setOpen] = React.useState(false);
const handleDrawerOpen = () => {
setOpen(true);
};
const handleDrawerClose = () => {
setOpen(false);
};
// TEST
const [anchorEl, setAnchorEl] = React.useState(null);
const [mobileMoreAnchorEl, setMobileMoreAnchorEl] = React.useState(null);
const isMenuOpen = Boolean(anchorEl);
const isMobileMenuOpen = Boolean(mobileMoreAnchorEl);
const handleProfileMenuOpen = event => {
setAnchorEl(event.currentTarget);
};
const handleMobileMenuClose = () => {
setMobileMoreAnchorEl(null);
};
const handleMenuClose = () => {
setAnchorEl(null);
handleMobileMenuClose();
};
const handleMobileMenuOpen = event => {
setMobileMoreAnchorEl(event.currentTarget);
};
const menuId = "primary-search-account-menu";
const renderMenu = (
<Menu
anchorEl={anchorEl}
anchorOrigin={{ vertical: "top", horizontal: "right" }}
id={menuId}
keepMounted
transformOrigin={{ vertical: "top", horizontal: "right" }}
open={isMenuOpen}
onClose={handleMenuClose}
>
<MenuItem onClick={handleMenuClose}>Profile</MenuItem>
<MenuItem onClick={handleMenuClose}>My account</MenuItem>
</Menu>
);
const mobileMenuId = "primary-search-account-menu-mobile";
const renderMobileMenu = (
<Menu
anchorEl={mobileMoreAnchorEl}
anchorOrigin={{ vertical: "top", horizontal: "right" }}
id={mobileMenuId}
keepMounted
transformOrigin={{ vertical: "top", horizontal: "right" }}
open={isMobileMenuOpen}
onClose={handleMobileMenuClose}
>
<MenuItem>
<IconButton aria-label="show 4 new mails" color="inherit">
<Badge badgeContent={4} color="secondary">
<MailIcon />
</Badge>
</IconButton>
<p>Messages</p>
</MenuItem>
<MenuItem>
<IconButton aria-label="show 11 new notifications" color="inherit">
<Badge badgeContent={11} color="secondary">
<NotificationsIcon />
</Badge>
</IconButton>
<p>Notifications</p>
</MenuItem>
<MenuItem onClick={handleProfileMenuOpen}>
<IconButton
aria-label="account of current user"
aria-controls="primary-search-account-menu"
aria-haspopup="true"
color="inherit"
>
<AccountCircle />
</IconButton>
<p>Profile</p>
</MenuItem>
</Menu>
);
return (
<div className={classes.root}>
<CssBaseline />
<AppBar
position="fixed"
className={clsx(classes.appBar, {
[classes.appBarShift]: open
})}
>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={handleDrawerOpen}
edge="start"
className={clsx(classes.menuButton, open && classes.hide)}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap>
LiveToday
</Typography>
<div className={classes.grow} />
<div className={classes.sectionDesktop}>
<IconButton aria-label="show 4 new mails" color="inherit">
<Badge badgeContent={4} color="secondary">
<MailIcon />
</Badge>
</IconButton>
<IconButton aria-label="show 17 new notifications" color="inherit">
<Badge badgeContent={17} color="secondary">
<NotificationsIcon />
</Badge>
</IconButton>
<IconButton
edge="end"
aria-label="account of current user"
aria-controls={menuId}
aria-haspopup="true"
onClick={handleProfileMenuOpen}
color="inherit"
>
<AccountCircle />
</IconButton>
</div>
<div className={classes.sectionMobile}>
<IconButton
aria-label="show more"
aria-controls={mobileMenuId}
aria-haspopup="true"
onClick={handleMobileMenuOpen}
color="inherit"
>
<MoreIcon />
</IconButton>
</div>
</Toolbar>
</AppBar>
<Drawer
className={classes.drawer}
variant="persistent"
anchor="left"
open={open}
classes={{
paper: classes.drawerPaper
}}
>
<div className={classes.drawerHeader}>
<IconButton onClick={handleDrawerClose}>
{theme.direction === "ltr" ? (
<ChevronLeftIcon />
) : (
<ChevronRightIcon />
)}
</IconButton>
</div>
<Divider />
<List>
{["Inbox", "Starred", "Send email", "Drafts"].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
<Divider />
<List>
{["All mail", "Trash", "Spam"].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
</Drawer>
<main
className={clsx(classes.content, {
[classes.contentShift]: open
})}
>
<div className={classes.drawerHeader} />
</main>
{renderMobileMenu}
{renderMenu}
</div>
);
}
The div which is responsible for pushing icons to the right is using className={classes.grow}, but you haven't defined grow anywhere.
Add the following code to your makeStyles function:
grow: {
flexGrow: 1
}

navigation.navigate does not change scene

I've been trying to work with this since yesterday, tried different methods and none actually worked. I'm currently using a drawer navigator, tab navigator and I want to open a different scene when clicking a icon.
Here is the current design:
The icon I want to open a new scene is the "filter" up on the top-right corner.
My current header code is:
<Header>
<Left>
<Icon name='ios-menu' style={{ color: 'white' }} onPress={() =>
this.props.navigation.dispatch(DrawerActions.openDrawer())} />
</Left>
<View style={{ width: Dimensions.get('window').width * 0.7 }}>
<Text style={styles.headerTextTop}>Estabelecimentos</Text>
</View>
<Right>
<Icon name='ios-search' style={{ paddingRight: 20, color: 'white' }} />
<Icon name='ios-options' style={{ color: 'white' }} onPress={this.openFilter} />
</Right>
</Header>
and here is the function openFilter:
openFilter = () =>
{
this.props.navigation.navigate('Filter');
}
I'm importing the filter component from its .js file, I can't get it to switch scenes.
Here is the Filter.js (which i'm importing as import Filter from './Filter'):
import React, { Component } from "react";
import {
View,
Text,
Dimensions
} from "react-native";
import { Icon, Header, Left, Right } from 'native-base'
class Filter extends Component {
render() {
return (
<View>
<Header>
<Left>
<Icon name='ios-arrow-back' style={{color: 'white'}} onPress={() =>
this.props.navigation.goBack()} />
</Left>
<View style={{ width: Dimensions.get('window').width * 0.7 }}>
<Text style={sty.headerTextTop}>Filtro</Text>
</View>
<Right>
</Right>
</Header>
</View>
);
}
}
export default Filter;
--EDIT:--
As #Pritish Vaidya suggested, I should implement a Stack Navigator, so I made one:
import { StackNavigator} from 'react-navigation'
import Filter from './Filter';
import App from './App';
import Home from './Home';
const stackNav = StackNavigator({
App: {
screen: App,
navigationOptions:({navigation}) => ({
title: "Main",
headerStyle: { paddingRight: 10, paddingLeft: 10 }
})
},
Filter: {
screen: Filter,
navigationOptions: (props) => ({
title: "Filtro",
})
},
Home: {
screen: Home,
navigationOptions: (props) => ({
title: "Home",
})
}
})
export default stackNav;
But in Home.js when I call navigate using:
<Icon name='ios-options' style={{ color: 'white' }} onPress={() => this.props.navigator.navigate("Filter")} />
It gets me this error:

How to use ScrollView with third party Components in React Native?

Very simple, how can I use ScrollView when using an third party (react-native-elements) List/List-Items? The given docs does not inform this: link
The list is fully working, but can't be scrolled:
Here is the code for rendering the list:
<View style={{ flex: 1 }}>
<List>
{
list.map((l, i) => (
<ListItem
roundAvatar
key={i}
title={l.name}
subtitle={
<View>
<View style={styles.subtitleView}>
<Text style={styles.ratingText}>{l.subtitle}</Text>
</View>
</View>
}
avatar={<Avatar
medium
rounded
source={l.avatar_url && { uri: l.avatar_url }}
title={l.name[0]}
/>}
/>
))
}
</List>
</View>
If I simply change the View to ScrollView, that should be the obvious move, I get this error:
I'm importing ScrollView from native-base and the List from react-native-elements.
Try to make the ScrollView under your View, also add additional View (not required) under the ScrollView.
<View style={{ flex: 1 }}>
<ScrollView>
<View>
<List>
{
list.map((l, i) => (
<ListItem
roundAvatar
key={i}
title={l.name}
subtitle={
<View>
<View style={styles.subtitleView}>
<Text style={styles.ratingText}>{l.subtitle}</Text>
</View>
</View>
}
avatar={<Avatar
medium
rounded
source={l.avatar_url && { uri: l.avatar_url }}
title={l.name[0]}
/>}
/>
))
}
</List>
</View>
</ScrollView>
</View>
Also consider using FlatList rather than ScrollView for better performance.
Reference:
https://facebook.github.io/react-native/docs/scrollview
https://facebook.github.io/react-native/docs/flatlist

Resources