My data in the imageUrl array . i don't load it with for loop .
loadImage() {
console.log('before', this.state.imagesUrl);
if (this.state.imagesUrl !== undefined && this.state.imagesUrl.length !== 0) {
console.log('after', this.state.imagesUrl);
const n = this.state.imagesUrl.length;
console.log('length' , n);
for ( let i = 0 ; i < n ; i++) {
// i = i + 1
console.log('element', i, this.state.imagesUrl[i]);
return (
<Image
source={{ uri: this.state.imagesUrl[i] }}
style={{ width: 45, height: 45, marginLeft: 2, marginTop: 2, marginRight: 2, borderRadius: 10 }}
/>
)
}
}
}
Use loadImage in ES6 format:
change:
loadImage(){
// ...
}
to:
loadImage = () => {
// ...
}
Then, you have to push to a array:
let arr = [] // empty array
for ( let i = 0 ; i < n ; i++) {
// ...
arr.push(
<Image
source={{ uri: this.state.imagesUrl[i] }}
style={{ styles.Image }} // your styles
/>
)
}
this.setState(arr)
and then in your jsx:
render(){
let {arr} = this.state
return(
<View>
{arr}
</View>
)
}
Related
I am having trouble making this work and I have no Idea why this is happening.
I am using #alentoma/react-native-selectable-text inside a scrollView but when the text get focus or I click on it. The scrollview bounce up and down once.
#alentoma/react-native-selectable-text is using ReactTextView on android.
Here is the code
<ScrollView
ref={(c) => {
if (c) {
ref.current = c;
scrollTo();
}
}}
onScrollBeginDrag={() => {
clearTimeout(onScrollTimeout.current);
scrollable.current = true;
}}
onScrollEndDrag={() => {
clearTimeout(onScrollTimeout.current);
onScrollTimeout.current = setTimeout(() => {
scrollable.current = false;
}, 1500);
}}
onScroll={({ nativeEvent }) => {
if (!viewPlayer && scrollable.current) {
onScroll(nativeEvent);
} else return false
}}
contentOffset={{ y: state.content.scrollPos, x: 0 }} contentContainerStyle={!viewPlayer ? { paddingBottom: 400, paddingTop: 150, minHeight: windowHeight } : {}}
>
{showAds && !viewPlayer ? (
<Banner bannerSize="smartBannerPortrait" />
) : null}
<View style={{ minHeight: windowHeight + (!viewPlayer ? 500 : 0), flexDirection: 'row' }} incBackGround={false}>
<Text
key={state.content.key + "Text"}
style={{
width: windowWidth,
padding: 0,
margin: 0,
minHeight: windowHeight + (!viewPlayer ? 500 : -60),
paddingTop: (viewPlayer ? 60 : 0),
flex: 1,
flexWrap: 'wrap'
}} onPress={txtClick}>
<SelectableText
key={state.content.key}
highLights={detaliItemSettings.detaliItemSettingsEdit?.filter(x => x.editWith.length > 0 && x.highlightColor && x.highlightColor.length > 1).map(x => { return { text: x.editWith, color: x.highlightColor } })}
menuItems={globalContext.currentLanguage.novelReaderTextSelectionMenus.map(x => x.text)}
onSelection={(item) => {
disableClick.current = false;
handleContextMenu(item);
}}
onLongPress={() => {
disableClick.current = true
}}
onPress={txtClick}
contentWidth={windowWidth}
value={state.content} />
</Text>
</View>
</ScrollView>
I have implemented a list like Instagram explore page using this question React native grid like instagram
but it is not performant at all with the layout I have. is there any other way to implement this?
the other problem is in re-rendering different items for some rows when scrolling, I manage to reduce it by changing the conditions in which item should be rendered but there is still a problem.
I have uploaded a video of my app here: https://youtu.be/gP7kbwKDeNA
I chunck my content to 3 item arrays and then pass it to this component this is my code:
const albumIcon = require("../../assets/icons/stream.png");
const videoIcon = require("../../assets/icons/play.png");
const { width } = Dimensions.get("window");
let isLeft = false;
let url = null;
let icon = null;
function CustomGridList(props){
const onRefresh = (loadMore = false) => {
if (loadMore === true) {
loadMore();
} else {
props.refresh(props.query);
}
};
const loadMore = () => {
props.refresh(props.query, true);
};
const getItemKey = (index,item) => {
return "item".concat(index);
};
const _renderPhoto = (item, isLarge) => {
url = null;
icon = null;
if (item && item.videos.length > 0) {
url = item.videos[0].thumbnail;
icon = videoIcon;
} else if (item && item.images.length > 1) {
url = item.images[0].url;
icon = albumIcon;
} else if(item && item.images.length > 0){
url = item.images[0].url;
}else{
url = 'https://castlebba.co.uk/wp-content/uploads/2017/04/default-image-620x600.jpg'
}
return (
<TouchableOpacity style={{marginRight: 1}}>
<CachedImage
source={{uri: url}}
style={{ flex: 1}}
afterClickFunc={() => console.log('clicked')}
useTouchableOpacity
width={isLarge ? (width*(2/3)+4) : width/3}
height={isLarge ? 200: 100}
/>
<View
style={{
position: 'absolute',
top: 4,
right: 4,
}}
>
<CustomIcon boldColor={'#fff'} icon={icon} />
</View>
</TouchableOpacity>
);
};
const _renderRow = (index, item) => {
console.log(index, item );
console.log('indexable -> ', ++index%3 );
let item1 = item[0] !== null? item[0]: null;
let item2 = item[1] !== null? item[1]: null;
let item3 = item[2] !== null? item[2]: null;
if((index+1) % 3 === 0){
if (isLeft){
isLeft = false;
}else{
isLeft = true;
}
return (
<View style={{flexDirection: isLeft? 'row' : 'row-reverse', height: 200}}>
{_renderPhoto(item1, true)}
<View style={{flexDirection: 'column'}}>
<View style={{flex:1, height: 100}}>
{_renderPhoto(item2,false)}
</View>
<View style={{ flex:1, height: 100}}>
{_renderPhoto(item3,false)}
</View>
</View>
</View>
);
}else{
return (
<View style={{flexDirection: 'row', height: 100}}>
{_renderPhoto(item1, false)}
{_renderPhoto(item2, false)}
{_renderPhoto(item3, false)}
</View>
);
}
}
const { loading, content } = props;
if(loading && !content) {
return (
<View style={{ marginTop: 30, height: 100, alignItems: 'center' }}>
<ActivityIndicator
color={getThemeStyle().color_main}
size={'large'}
/>
</View>
);
}
if (content === null) {
return <View />;
}
return (
<View style={styles.flatListUserListWrapper}>
<View style={styles.albumContainer}>
<CustomFlatList
{...props}
showsVerticalScrollIndicator={false}
style={[
styles.albumContainer,
content.length > 0 ? { margin: 1 } : {},
]}
content={content}
renderItem={({ index,item }) => _renderRow(index,item)}
itemSeparator={() => <View style={{ width: '100%', height: 1 }} />}
keyExtractor={(index,item) => getItemKey(item)}
enableLoadMore={true}
loading={loading}
onRefresh={() => onRefresh()}
loadMore={()=>loadMore()}
pagePostType={"search"}
canSendPost={false}
/>
</View>
</View>
);
}
CustomGridList.propTypes = {
error: PropTypes.string,
loading: PropTypes.bool,
refresh: PropTypes.func,
navigation: PropTypes.object,
content: PropTypes.array,
query: PropTypes.string,
navigateTo: PropTypes.func, //TODO implement this one on click
};
export default CustomGridList = React.memo(CustomGridList);
I separated different rows and put them in different files then changed the way I was selecting rows with big tiles, it got a little bit better but still rerendering more than it's supposed to. I'm not doing anything dynamicly.
const albumIcon = require("../../../assets/icons/stream.png");
const videoIcon = require("../../../assets/icons/play.png");
const { width } = Dimensions.get("window");
let isLeft = false;
let url = null;
let icon = null;
function CustomGridList(props){
const onRefresh = (loadMore = false) => {
if (loadMore === true) {
loadMore();
} else {
props.refresh(props.query);
}
};
const loadMore = () => {
props.refresh(props.query, true);
};
const getItemKey = (index,item) => {
return "item".concat(index);
};
const _renderRow = (index, item) => {
if((index+1)%6 === 0){
return(<LargeTiledRow item={item} width={width} reverse={false} navigateTo={props.navigateTo}/>)
}else if((index+4)%6 === 0){
return(<LargeTiledRow item={item} width={width} reverse={true} navigateTo={props.navigateTo}/>)
}else{
return (<GridSimpleRow item={item} width={width} navigateTo={props.navigateTo}/>);
}
}
const { loading, content } = props;
if(loading && !content) {
return (
<View style={{ marginTop: 30, height: 100, alignItems: 'center' }}>
<ActivityIndicator
color={getThemeStyle().color_main}
size={'large'}
/>
</View>
);
}
if (content === null) {
return <View />;
}
return (
<View style={styles.flatListUserListWrapper}>
<View style={styles.albumContainer}>
<CustomFlatList
{...props}
showsVerticalScrollIndicator={false}
style={[
styles.albumContainer,
content.length > 0 ? { margin: 1 } : {},
]}
content={content}
renderItem={({ index,item }) => _renderRow(index,item)}
itemSeparator={() => <View style={{ width: '100%', height: 1 }} />}
keyExtractor={(index,item) => getItemKey(item)}
enableLoadMore={true}
loading={loading}
onRefresh={() => onRefresh()}
loadMore={()=>loadMore()}
pagePostType={"search"}
canSendPost={false}
/>
</View>
</View>
);
}
function areEqual(prevProps, nextProps) {
prevProps.content === nextProps.content
}
export default CustomGridList = React.memo(CustomGridList,areEqual);
Im trying to make a tic-tack-toe game as a simple react-native app. But I cant make the alert work while trying to get the game to alert that player 1 or two wins when getting "3 in row". Can somebody see what I did wrong or give me some advice.
Everything is working exept that the Alert and the this.initializeGame(); under the part where I want to get the winner.
If someone would know if there is a "better" practice to name the variables const or let im also wondering that. :)
Thank you!
Here is the code:
import * as React from 'react';
import { Text, View, StyleSheet, TouchableOpacity, Alert, } from 'react-native';
import { MaterialCommunityIcons as Icon } from 'react-native-vector-icons'
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
gameState: [
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]
],
currentPlayer: 1,
}
}
componentDidMount() {
this.initializeGame();
}
initializeGame = () => {
this.setState({gameState:
[
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]
],
currentPlayer: 1,
});
}
getWinner = () => {
const NUM_TILES = 3;
var arr = this.state.gameState;
var sum;
var i = 0;
//rows
for (i; 1 < NUM_TILES; i++) {
sum = arr[i][0] + arr[i][1] + arr[i][2];
if (sum == 3) { return 1; }
else if (sum == -3) { return -1; }
}
//colums
for (i; 1 < NUM_TILES; i++) {
sum = arr[0][i] + arr[1][i] + arr[2][i];
if (sum == 3) { return 1; }
else if (sum == -3) { return -1; }
}
//diagonals
sum = arr[0][0] + arr[1][1] + arr[2][2];
if (sum == 3) { return 1; }
else if (sum == -3) { return -1; }
sum = arr[2][0] + arr[1][1] + arr[0][2];
if (sum == 3) { return 1; }
else if (sum == -3) { return -1; }
//If no winners
return 0;
}
onTilePress = (row, col) => {
//makes sure that the tiles dont change
var value = this.state.gameState[row][col];
if (value !== 0) { return; }
//sets currant player
var currentPlayer = this.state.currentPlayer;
//sets the correct tile
var arr = this.state.gameState.slice();
arr [row][col] = currentPlayer;
this.setState({gameState: arr});
//switches player
var nextPlayer = (currentPlayer == 1) ? -1 : 1;
this.setState({currentPlayer: nextPlayer});
//check for winners
var winner = this.getWinner();
if (winner == 1) {
Alert.alert("Player 1 is the winner");
this.initializeGame();
} else if (winner == -1) {
Alert.alert("Player 2 is the winner");
this.initializeGame();
}
}
renderIcon = (row, col) => {
var value = this.state.gameState[row][col];
switch(value)
{
case 1: return <Icon name="close" style={styles.tileX} />;
case -1: return <Icon name="circle-outline" style={styles.tileO} />;
default: return <View />;
}
}
render() {
return (
<View style={styles.container}>
<View style={{flexDirection: "row"}}>
<TouchableOpacity onPress={() => this.onTilePress(0, 0)} style={[styles.tile, { borderLeftWidth: 0, borderTopWidth: 0 }]}>
{this.renderIcon(0, 0)}
</TouchableOpacity>
<TouchableOpacity onPress={() => this.onTilePress(0, 1)} style={[styles.tile, { borderTopWidth: 0, }]}>
{this.renderIcon(0, 1)}
</TouchableOpacity>
<TouchableOpacity onPress={() => this.onTilePress(0, 2)} style={[styles.tile, { borderRightWidth: 0, borderTopWidth: 0 }]}>
{this.renderIcon(0, 2)}
</TouchableOpacity>
</View>
<View style={{flexDirection: "row"}}>
<TouchableOpacity onPress={() => this.onTilePress(1, 0)} style={[styles.tile, { borderLeftWidth: 0 }]}>
{this.renderIcon(1, 0)}
</TouchableOpacity>
<TouchableOpacity onPress={() => this.onTilePress(1, 1)} style={[styles.tile, { }]}>
{this.renderIcon(1, 1)}
</TouchableOpacity>
<TouchableOpacity onPress={() => this.onTilePress(1, 2)} style={[styles.tile, { borderRightWidth: 0 }]}>
{this.renderIcon(1, 2)}
</TouchableOpacity>
</View>
<View style={{flexDirection: "row"}}>
<TouchableOpacity onPress={() => this.onTilePress(2, 0)} style={[styles.tile, { borderBottomWidth: 0, borderLeftWidth: 0, }]}>
{this.renderIcon(2, 0)}
</TouchableOpacity>
<TouchableOpacity onPress={() => this.onTilePress(2, 1)} style={[styles.tile, { borderBottomWidth: 0, }]}>
{this.renderIcon(2, 1)}
</TouchableOpacity>
<TouchableOpacity onPress={() => this.onTilePress(2, 2)} style={[styles.tile, { borderBottomWidth: 0, borderRightWidth: 0,
}]}>
{this.renderIcon(2, 2)}
</TouchableOpacity>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#fff',
alignItems: 'center',
},
tile: {
borderWidth: 10,
width: 100,
height: 100,
},
tileX: {
color: "red",
fontSize: 60,
},
tileO: {
color: "green",
fontSize: 60,
}
});
Looks like everything you did is fine, except your algorithm for the getWinner() function. There were many things wrong with this function, for example, you had the for loop ending condition as 1 < NUM_OF_TILES where NUM_OF_TILES is 3. And also you have to reinitialize the i to 0 when moving from rows to columns because i is already 2 at the end of first for loop.
I have updated this function for you as follows:
getWinner = () => {
const NUM_TILES = 3;
var arr = this.state.gameState;
var sum;
var i = 0;
//rows
for (i = 0; i < NUM_TILES; i++) {
sum = arr[i][0] + arr[i][1] + arr[i][2];
if (sum == 3) {
return 1;
} else if (sum == -3) {
return -1;
}
}
//colums
for (i = 0; i < NUM_TILES; i++) {
sum = arr[0][i] + arr[1][i] + arr[2][i];
if (sum == 3) {
return 1;
} else if (sum == -3) {
return -1;
}
}
//diagonals
sum = arr[0][0] + arr[1][1] + arr[2][2];
if (sum == 3) {
return 1;
} else if (sum == -3) {
return -1;
}
sum = arr[2][0] + arr[1][1] + arr[0][2];
if (sum == 3) {
return 1;
} else if (sum == -3) {
return -1;
}
//If no winners
return 0;
};
You can find the working code at: https://codesandbox.io/s/react-native-4f2yu I have not tested all use-cases btw, but hopefully it puts you in the right direction.
I wanna render array of "Star" to the view with absolute position. When I add them 1 by 1. It works fine, but when I add them as array, it only shown the last View in the array
render(){
const pointSize = 30
let pointViews = []
let baseStyle = {position:'absolute',width:pointSize,height:pointSize,backgroundColor:'yellow',borderRadius:pointSize/2}
console.log("render ");
let points = this.state.data
this.state.data.forEach(function(point){
console.log("for: "+JSON.stringify(point));
let finalStyle = baseStyle
finalStyle.left = point.x
finalStyle.top = point.y
let key = point.x+"_"+point.y
let pointView = <View style={finalStyle} key={key}><Text>{key}</Text></View>
pointViews.push(pointView)
})
console.log("pointViewss: "+pointViews)
return(
<Container style={{position:'absolute'}}>
{points.map((point,key) => {
let finalStyle = baseStyle
finalStyle.left = point.x
finalStyle.top = point.y
return(
<View style={finalStyle} key={key}><Text>{key}</Text></View>
)
})}
{/*<View style={[baseStyle,{left:10,top:10}]}></View>*/}
{/*<View style={[baseStyle,{left:50,top:50}]}></View>*/}
{/*<View style={[baseStyle,{left:100,top:100}]}></View>*/}
</Container>
)
}
What it shown
What I want
Here you go
constructor(props){
super(props)
this.state= {
data: [
{x: 10, y:10},
{ x: 50, y: 50 },
{ x: 100, y: 100 }
]
}
}
render() {
const pointSize = 30
let pointViews = []
let baseStyle = { position: 'absolute', width: pointSize, height: pointSize, backgroundColor: 'yellow', borderRadius: pointSize / 2 }
return (
<View style={{position: 'absolute'}}>
{
this.state.data.map((point, key) => {
return (
<View style={{ ...baseStyle, left: point.x, top: point.y }} key={key}><Text>{key}</Text></View>
)
})
}
</View>
)
}
I hope its clear enough :)
I would like to center the active tab in the scrollable navbar. Currently I have a scrollable horizontal view but am stuck on updating the view. I am looking at other examples such as here on github. I would love some feedback on creating the _updateView method, there seems to be more to solving this problem than simply adding a few flexbox rules.
Here is my component code.
export default class MenuNavBar extends Component {
static propTypes: {
goToPage: React.PropTypes.func,
activeTab: React.PropTypes.number,
tabs: React.PropTypes.array,
}
constructor(props) {
super(props)
var screenWidth = Dimensions.get('window').width;
this.state = {
paddingLeft: 5,
paddingRight: 5,
titleMenuWidth: screenWidth - 100
};
this._onScroll = this._onScroll.bind(this);
this._onClickMenu = this._onClickMenu.bind(this);
componentDidMount() {
this._listener = this.props.scrollValue.addListener(this.setAnimationValue.bind(this));
}
_onClickMenu(index) {
this.props.goToPage(index);
this._updateView(index);
}
_updateView(index) {
var navContainerWidth = Dimensions.get('window').width - 100;
console.log('navContainerWidth', navContainerWidth)
}
_onTabLayout(event, i) {
this.menuTabs[i] = (this.menuTabs[i]) ? this.menuTabs[i] : event.nativeEvent.layout.width;
}
_onScroll(event) {
let {
contentSize,
contentInset,
contentOffset,
layoutMeasurement,
} = event.nativeEvent;
}
_renderMain() {
return (
<NavigationBar
title={
<ScrollView ref='menuScrollView' onScroll={this._onScroll} onLayout={this._onLayout} style={{width: this.state._titleMenuWidth}} horizontal={true} showsHorizontalScrollIndicator={false}>
{this.props.tabs.map((tab, i) => {
if (i == 0) {
ref_name = 'tab_' + i;
component_style = {
paddingTop: 5,
paddingBottom: 5,
paddingLeft: this.state.paddingLeft,
paddingRight: 5,
};
} else if (i == this.props.tabs.length - 1) {
ref_name = 'tab_' + i;
component_style = {
paddingTop: 5,
paddingBottom: 5,
paddingLeft: 5,
paddingRight: this.state.paddingRight,
};
} else {
ref_name = "tab_" + i;
component_style = styles.navbarMenuButton;
}
return <TouchableOpacity ref={ref_name} key={tab} onLayout={(event) => this._onTabLayout(event, i)} onPress={() => this._onClickMenu(i)} style={component_style}>
<Text style={this.props.activeTab === i ? styles.navbarMenuTextActive : styles.navbarMenuText}>{tab}</Text>
</TouchableOpacity>;
})}
</ScrollView>
}
leftButton={
<View>
<Image source={require('../../assets/imgs/logo.png')} style={styles.navbarLogo} />
</View>
}
style={styles.headerStyle}
statusBar={{tintColor: '#6C0104'}} />
);
}
_renderTrend() {
return (
<NavigationBar
title={
<ScrollView ref='menuScrollView' onScroll={this._onScroll} onLayout={this._onLayout} style={{width: titleMenuWidth}} horizontal={true} showsHorizontalScrollIndicator={false}>
{this.props.tabs.map((tab, i) => {
if (i == 0) {
ref_name = 'tab_' + i;
component_style = {
paddingTop: 5,
paddingBottom: 5,
paddingLeft: this.state.paddingLeft,
paddingRight: 5,
};
} else if (i == this.props.tabs.length - 1) {
ref_name = 'tab_' + i;
component_style = {
paddingTop: 5,
paddingBottom: 5,
paddingLeft: 5,
paddingRight: this.state.paddingRight,
};
} else {
ref_name = "tab_" + i;
component_style = styles.navbarMenuButton;
}
return <TouchableOpacity ref={ref_name} key={tab} onLayout={(event) => this._onTabLayout(event, i)} onPress={() => this._onClickMenu(i)} style={component_style}>
<Text style={this.props.activeTab === i ? styles.navbarMenuTextActive : styles.navbarMenuText}>{tab}</Text>
</TouchableOpacity>;
})}
</ScrollView>
}
leftButton={
<View>
<Image source={require('../../assets/imgs/logo.png')} style={styles.navbarLogo} />
</View>
}
rightButton={
<View>
<TouchableOpacity onPress={() => Actions.country()}>
<Image source={require('../../assets/imgs/ic_world.png')} style={styles.worldLogo} />
</TouchableOpacity>
</View>
}
style={styles.headerStyle}
statusBar={{tintColor: '#6C0104'}} />
);
}
render() {
var screenWidth = Dimensions.get('window').width;
var titleMenuWidth = screenWidth - 100;
return (this.props.mode == 'main') ? this._renderMain() : this._renderTrend();
}
}
P.S. I will update this question to be more clear as I fully understand it better.