How can I update state once onPress via TouchableOpacity in React Native? - react-native

So I am rendering a ListView with two columns of items. The following function renders the rows, renderRow(rowData):
For each item, if clicked, I want it to be changed to opacity of 0.5 and if clicked again, want it to be back to clear up the opacity, so what I was thinking is setting it to opacity of 1.0.
I tried the following, but for some reason the opacity is not being updated:
constructor(props){
super(props);
this.state = {
opacity: 1.0,
selected: false,
}
}
renderRow(rowData){
return (
<View style={styles.item}>
<TouchableHighlight onPress={()=>this._selected() underlayColor='transparent'}>
<View style={{opacity:this.state.opacity}}>
<Text>{rowData.firstName}</Text>
<Text>{rowData.lastName}</Text>
</View>
</TouchableHighlight>
</View>
)
}
_selected(){
if(this.state.selected){
console.log('ITEM HAS BEEN UNSELECTED')
this.setState({
opacity: 1.0,
selected: false
})
}else{
console.log('ITEM HAS BEEN SELECTED')
this.setState({
opacity: 0.5,
selected: true
})
}
}
Why isn't the opacity being updated once clicked and rerendering the view inside the TouchableHighlight? Also how can I do so with individual item, with each's 'opacity' and 'selected' states?
**FULL CODE
'use strict';
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
ListView,
Image,
TouchableHighlight,
TouchableOpacity
} from 'react-native';
class Interest extends Component {
constructor(props){
super(props);
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
selected: false,
dataSource: ds.cloneWithRows([
{firstName: 'Johnny', lastName: 'Boy'},
{firstName: 'Shawn', lastName: 'Ke'},
{firstName: 'An', lastName: 'Twon'}
};
}
renderRow(rowData){
return (
<View style={this.state.selected ? styles.transItem : styles.opacItem}>
<TouchableHighlight
onPress={ () => { this.setState({selected: !this.state.selected})}} underlayColor='transparent'>
<View>
<Text>{rowData.firstName}</Text>
<Text>{rowData.lastName}</Text>
</View>
</TouchableHighlight>
</View>
)
}
render() {
return (
<View style={styles.container}>
<ListView contentContainerStyle={styles.list} dataSource={this.state.dataSource} renderRow={this.renderRow.bind(this)}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1
},
list: {
flexDirection: 'row',
flexWrap: 'wrap',
},
opacItem: {
margin: 15,
width: 155,
height: 175,
opacity: 1.0
},
transItem: {
margin: 15,
width: 155,
height: 175,
opacity: 0.5
}
});
export default Interest

I think you set the selected state that is not intended.
In the code above, the selected is a state for the entire app - not just for a selected line. To select a single line, you should keep a selected state for each item. For cleaner code it is recommended to have another module for the item, and keep the state there and not in the parent module.
Code:
'use strict';
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
ListView,
Image,
TouchableHighlight,
TouchableOpacity
} from 'react-native';
class Interest extends Component {
constructor(){
super();
this._renderRow = this._renderRow.bind(this);
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows([
{firstName: 'Johnny', lastName: 'Boy'},
{firstName: 'Shawn', lastName: 'Ke'},
{firstName: 'An', lastName: 'Twon'}
])};
}
render() {
return (
<View style={styles.container}>
<ListView
contentContainerStyle={styles.list}
dataSource={this.state.dataSource}
renderRow={this._renderRow}
/>
</View>
);
}
_renderRow(rowData) {
return(
<InterestItem rowData={rowData} />);
}
}
class InterestItem extends Component {
constructor(props){
super(props);
this.state = {
selected: false
}
}
render(){
const { rowData } = this.props;
return (
<View style={this.state.selected ? styles.transItem : styles.opacItem}>
<TouchableHighlight
onPress={ () => { this.setState({selected: !this.state.selected})}}
underlayColor='transparent'
>
<View>
<Text>{rowData.firstName}</Text>
<Text>{rowData.lastName}</Text>
</View>
</TouchableHighlight>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1
},
list: {
flexDirection: 'row',
flexWrap: 'wrap',
},
opacItem: {
margin: 15,
width: 155,
height: 175,
opacity: 1.0
},
transItem: {
margin: 15,
width: 155,
height: 175,
opacity: 0.5
}
});
export default Interest

Related

dont share state between reusable component react native

Im creating a reusable Text component with a onFocus and onBlur animation, but when I put this in a form; the focus and blur event triggers the animation for every Input in the form... can you help me to avoid this behavior?
Here is the code if you need more details, but I think this is very clear
import React, { Component } from 'react';
import { TextInput, View, Text, Animated, StyleSheet } from 'react-native';
const animatedPlaceholder = new Animated.Value(30);
class Input extends Component {
constructor(props) {
super(props);
this.state = {
id: '',
isFocused: false,
textLength: 0
};
}
secureTextEntry = this.props.secureTextEntry || false;
autoCapitalize = this.props.autoCapitalize || 'sentences';
keyboardType = this.props.keyboardType || 'default';
focus = () => {
this.setState({isFocused: true});
Animated.timing(animatedPlaceholder, {
toValue: 0,
duration: 300
}).start();
}
blur = () => {
this.setState({isFocused: false});
Animated.timing(animatedPlaceholder, {
toValue: 30,
duration: 300
}).start();
}
render() {
return(
<View {...this.props}>
<Animated.Text style={
this.state.isFocused ? styles.usedValue : styles.emptyValue
} > {this.props.placeholder} </Animated.Text>
<TextInput
onFocus={this.focus}
onBlur={this.blur}
autoCapitalize={this.autoCapitalize}
secureTextEntry={this.secureTextEntry}
keyboardType={this.keyboardType}
style={
styles.textInput
}
/>
</View>
);
}
}
export default Input;
I didn't quite got your question, but i created a component which animates the place holder when its focused, animated back if value is empty,
check this snack example https://snack.expo.io/#ashwith00/frowning-cookie
Code
import React, { Component } from 'react';
import { TextInput, View, Text, Animated, StyleSheet } from 'react-native';
export default class Input extends Component {
constructor(props) {
super(props);
this.state = {
id: '',
isFocused: false,
textLength: 0,
};
this.animatedPlaceholder = new Animated.Value(0);
}
secureTextEntry = this.props.secureTextEntry || false;
autoCapitalize = this.props.autoCapitalize || 'sentences';
keyboardType = this.props.keyboardType || 'default';
focus = () => {
Animated.timing(this.animatedPlaceholder, {
toValue: -40,
duration: 300,
}).start();
};
blur = () => {
if (!this.props.value) {
Animated.timing(this.animatedPlaceholder, {
toValue: 0,
duration: 300,
}).start();
}
};
render() {
const {value, onChangeText} = this.props;
return (
<View style={[ {
justifyContent: 'center'
}]}>
<Animated.Text
style={{
position: 'absolute',
transform: [{translateY: this.animatedPlaceholder}]
}}>
{' '}
{this.props.placeholder}{' '}
</Animated.Text>
<TextInput
value={value}
onChangeText={onChangeText}
onFocus={this.focus}
onBlur={this.blur}
autoCapitalize={this.autoCapitalize}
secureTextEntry={this.secureTextEntry}
keyboardType={this.keyboardType}
style={styles.textInput}
/>
</View>
);
}
}
const styles = StyleSheet.create({
usedValue: {} ,
emptyValue: {},
textInput: {
alignSelf: 'stretch',
height: 50,
borderWidth: 0.4
}
})

Flatlist data not showing up on screen

Trying to make a simple to-do list. My AddTodo component works fine and I don't believe it is causing the issue but my Flatlist does not show the data. I have no idea why as there are no errors. The issue appears with or without the scroll view.
I've tried messing around with the width and height of the items and the list itself but nothing seems to do the trick.
my mainTodo file:
import React, { Component } from 'react';
import { Text, View, StyleSheet, FlatList, ScrollView } from 'react-native';
import AddTodo from './AddTodo';
import TodoItem from './TodoItem';
class MainTodo extends Component {
constructor() {
super();
this.state = {
textInput: '',
todos: [
{ id: 0, title: 'walk rocky', completed: false },
{ id: 1, title: 'pickup dinner', completed: false }
]
};
}
addNewTodo() {
let todos = this.state.todos;
todos.unshift({
id: todos.length + 1,
todo: this.state.textInput,
completed: false
});
this.setState({
todos,
textInput: ''
});
}
render() {
return (
<View style={{ flex: 1 }}>
<AddTodo
textChange={textInput => this.setState({ textInput })}
addNewTodo={() => this.addNewTodo()}
textInput={this.state.textInput}
/>
<ScrollView>
<FlatList
style={{ flex: 1 }}
data={this.state.todos}
extraData={this.state}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => {
return (
<TodoItem todoItem={item} />
);
}}
/>
</ScrollView>
</View>
);
}
}
export default MainTodo;
my TodoItem file:
import React, { Component } from 'react';
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
class TodoItem extends Component {
render() {
const todoItem = this.props.todoItem;
return (
<View>
<TouchableOpacity style={styles.todoItem}>
<Text style={(todoItem.completed) ? { color: '#aaaaaa' } : { color: '#313131' }}>
{todoItem.title}
</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
todoItem: {
width: 40,
height: 40,
borderBottomColor: '#DDD',
borderBottomWidth: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingLeft: 15
}
});
export default TodoItem;
Under my addtodo component nothing shows up, it's just a blank screen.
In the maintodo file you are rendering the AddTodo component but i didn't see your AddTodo component. So you can update your code accordingly.
In the TodoItem remove the style applied to TouchableOpacity so that your code looks like
import React, { Component } from 'react';
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
class TodoItem extends Component {
render() {
const todoItem = this.props.todoItem;
return (
<View>
<TouchableOpacity style={styles.todoItem}>
<Text style={(todoItem.completed) ? { color: '#aaaaaa' } : { color: '#313131' }}>
{todoItem.title}
</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
});
export default TodoItem;
And in the MainTodo
update your addNewTodo function as
addNewTodo = () => {
const todo = {
id: this.state.todos.length,
title: this.state.textInput,
completed: false
}
this.setState({todos: [...this.state.todos, todo ], textInput: ""})
}
create the TextInput and Button with parent View as flexDirection: "row" and so when TextInput is changed it's value is set in the textInput and when Button is pressed it will create new object and add it to the todos and set the value of TextInput to empty.
and final code can be as
import React, { Component } from 'react';
import { Text, View, StyleSheet, FlatList, ScrollView, TextInput, Button } from 'react-native';
import TodoItem from './TodoItem';
class MainTodo extends Component {
constructor() {
super();
this.state = {
textInput: '',
todos: [
{ id: 0, title: 'walk rocky', completed: false },
{ id: 1, title: 'pickup dinner', completed: false }
]
};
}
addNewTodo = () => {
const todo = {
id: this.state.todos.length,
title: this.state.textInput,
completed: false
}
this.setState({todos: [...this.state.todos, todo ], textInput: ""})
}
render() {
return (
<View style={{ flex: 1, marginTop: 30, paddingHorizontal: 20 }}>
<View style={{flexDirection: "row", alignItems: "center", justifyContent: "space-between"}}>
<TextInput style={{borderWidth: 1, borderColor: "black"}} onChangeText={textInput => this.setState({textInput})} placeholder="Enter todo text" value={this.state.textInput} />
<Button onPress={this.addNewTodo} title="Add todo" />
</View>
<FlatList
contentContainerStyle={{flexGrow: 1}}
data={this.state.todos}
extraData={this.state.todos}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => {
return (
<TodoItem todoItem={item} />
);
}}
/>
</View>
);
}
}
export default MainTodo;
use the code
mainTodo file:
import React, { Component } from 'react';
import { Text, View, StyleSheet, FlatList, ScrollView } from 'react-native';
import AddTodo from './AddTodo';
import TodoItem from './TodoItem';
class MainTodo extends Component {
constructor() {
super();
this.state = {
textInput: '',
todos: [
{ id: 0, title: 'walk rocky', completed: false },
{ id: 1, title: 'pickup dinner', completed: false }
]
};
}
addNewTodo() {
let todos = this.state.todos;
todos.unshift({
id: todos.length + 1,
todo: this.state.textInput,
completed: false
});
this.setState({
todos,
textInput: ''
});
}
render() {
return (
<View style={{ flex: 1 }}>
<AddTodo
textChange={textInput => this.setState({ textInput })}
addNewTodo={() => this.addNewTodo()}
textInput={this.state.textInput}
/>
<ScrollView>
<FlatList
style={{ flex: 1 }}
data={this.state.todos}
extraData={this.state}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => {
return (
<TodoItem todoItem={item} />
);
}}
/>
</ScrollView>
</View>
);
}
}
export default MainTodo;
TodoItem file:
import React, { Component } from 'react';
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
class TodoItem extends Component {
render() {
const todoItem = this.props.todoItem;
return (
<View>
<TouchableOpacity style={styles.todoItem}>
<Text style={(todoItem.completed) ? { color: '#aaaaaa' } : { color: '#313131' }}>
{todoItem.title}
</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
todoItem: {
width: 40,
height: 40,
borderBottomColor: '#DDD',
borderBottomWidth: 1,
backgroundColor:'red',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingLeft: 15
}
});
export default TodoItem;

React Native: `Image` does show the `uri` source

Image doesn't show the uri source.
Image can show the require('') source.
I don't know the reason.
import React, {Component} from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
ListView,
Image,
Dimensions,
ScrollView,
} from 'react-native';
const deviceWidth = Dimensions.get('window').width;
export default class AwesomeProject extends Component {
// Initialize the hardcoded data
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
imageArray1: [
{
url: 'http://insights.ubuntu.com/wp-content/uploads/8063/react-native-logo.jpg',
description: 'React Logo'
},
],
imageArray2: [
{
url: 'http://insights.ubuntu.com/wp-content/uploads/8063/react-native-logo.jpg',
description: 'React Logo'
},
{
url: 'http://insights.ubuntu.com/wp-content/uploads/8063/react-native-logo.jpg',
description: 'React Logo'
},
],
};
}
renderRow = (tempArray) => {
const imageStyleNumber = tempArray.length;
let imageStyleString;
switch (imageStyleNumber) {
case 1:
imageStyleString = `imgView1`;
break;
default:
imageStyleString = `imgView9`;
}
return tempArray.map((item, index) => {
if(index > 8){
return ;
}
return (
<View key={index} style={styles[imageStyleString]}>
<Image style={styles.imgIstyle} source={{uri: item.url}}/>
<Text style={styles.imgTDesc}>{item.description}</Text>
</View>
)
})
}
render() {
return (
<ScrollView style={styles.container}>
<View style={styles.rowVImageBox}>
{this.renderRow(this.state.imageArray1)}
</View>
</ScrollView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#ebf0f2',
},
rowVImageBox: {
flexDirection:'row',
flexWrap:'wrap',
width: 300,
height: 300,
marginBottom:100,
},
rowVImageBox2: {
flexDirection:'row',
flexWrap:'wrap',
width: 300,
height: 300,
marginBottom:100,
backgroundColor:'blue',
},
imgView1: {
width:290,
height:290,
marginRight:5,
marginBottom:5,
},
imgView2: {
width:140,
height:290,
marginRight:5,
marginBottom:5,
},
imgIstyle:{
width:'100%',
height:'70%',
backgroundColor:'yellow',
},
imgTDesc: {
flex:2,
backgroundColor:'white'
}
});
AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);
Do you run it well?
I have asked my friend. He doesn't know the reason too.
modify the Info.plist.
iOS can load the http resource.

How do I pass strings from one component to another component?

Login component:
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
TouchableHighlight,
TextInput,
Image
} from 'react-native';
var DisplayData = require('./DisplayData');
export default class Login extends Component{
constructor(props){
super(props)
this.state = {
Latitude: '',
Longitude: '',
}
}
onPressDisplayData() {
this.props.navigator.push({
name: 'DisplayView',
component: DisplayData
});
}
render() {
return (
<View style = {styles.container}>
<Image source={require('./logo.png')}/>
<TextInput
style = {styles.input}
placeholder = 'Latitude'
autoCapitalize = 'none'
onChangeText={(text) => this.setState({Latitude:text})}
/>
<TextInput
style = {styles.input}
placeholder = 'Longitude'
autoCapitalize = 'none'
onChangeText={(text) => this.setState({Longitude:text})}
/>
<TouchableHighlight
style = {styles.submit}
onPress = {() => this.onPressDisplayData()
}
>
<Text>
Submit
</Text>
</TouchableHighlight>
</View>
);
}
}
const styles = StyleSheet.create ({
container: {
flex: 1,
alignItems: 'center',
justifyContent:'center',
paddingBottom: 40
},
input: {
margin: 15,
height: 40,
borderColor: 'grey',
borderWidth: 1
},
submit: {
backgroundColor: '#FFDD03',
padding: 10
}
})
module.exports = Login;
DisplayData component:
import React, {
Component,
} from 'react';
import {
AppRegistry,
Image,
ListView,
StyleSheet,
Text,
View,
} from 'react-native';
var REQUEST_URL = 'http://api.geonames.org/earthquakesJSON?north='+4+'&south='+-9.9+'&east='+-22.4+'&west='+55.2+'&username=afdsanfd'
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
class DisplayData extends Component {
constructor(props){
super(props)
this.state = {
earthquakes: ds.cloneWithRows([])
};
}
componentDidMount() {
fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseData) => this.setState({ earthquakes: ds.cloneWithRows(responseData.earthquakes) }))
.catch(function(error) {
console.warn(error);
});
}
render() {
return (
<View>
<ListView
dataSource = {this.state.earthquakes}
renderRow={(rowData) => (
<View style={{flex: 1, paddingTop: 10, paddingBottom: 10, borderWidth: 0.5, paddingLeft: 10, borderColor: '#D3D3D3'}}>
<Text style={{fontSize: 25}}>{rowData.src}</Text>
<Text>DateTime: {rowData.datetime}</Text>
<Text>Magnitude: {rowData.magnitude}</Text>
<Text>EqID: {rowData.eqid}</Text>
<Text>Depth: {rowData.depth}</Text>
</View>
)}
/>
</View>
);
}
}
module.exports = DisplayData;
So what I am trying to accomplish through this app is having someone enter latitude and longitude such that an API call (REQUEST_URL) that'd return earthquake info. However, i'm struggling really hard with passing the entered latitude and longitude into the DisplayData component. I've tried things such as creating a global latitude and longitude but none have been working.

How to check if a component is mounted in React-Native ES6

I am setting a listener in my application and using force update whenever it is broadcasted but it gives error forceUpdate cant be called on unmounted component. How can I check if a component is mounted now that the isMounted() function is deprecated.
'use strict';
var React = require('react-native');
import ExpAndroid from './ExpAndroid';
var {
AppRegistry,
Image,
ListView,
TouchableHighlight,
StyleSheet,
Text,
View,
Component,
AsyncStorage,
Navigator,
DeviceEventEmitter
} = React;
var rowID;
var img=require('./resource/ic_pause_white.png');
class Example1 extends Component{
constructor(props) {
super(props);
this.state = {
};
}
componentWillMount(){
rowID = this.props.rowIdentity;
console.log("rowID "+rowID);
}
componentDidMount(){
console.log('component mounted')
this.start();
DeviceEventEmitter.addListener('playMusicStatus', (data)=> {
if(data.playMusic==true){
img=require('./resource/ic_pause_white.png');
rowID++;
this.setState(this.state);
ExpAndroid.someMethod1("someurl);
}
});
}
componentWillUnmount(){
console.log('componentwill unmounted')
}
start() {
var url = "some url";
ToastAndroid.prepareToPlay(url,true);
}
render() {
return (
<Image source={require('./resource/album_back.png')} style={styles.background}>
<Image
source={{uri:this.state.trackDetails[rowID].thumnail_loc}}
style={styles.thumbnail}
/>
<View style={styles.flowRow}>
<Text
style={styles.titles}
>text1 + {rowID}: </Text>
<Text
style={styles.titles}
>{this.state.details[rowID].text1}</Text>
</View>
<View style={styles.flowRow}>
<Text
style={styles.titles}
>text2 : </Text>
<Text
style={styles.titles}
>{this.state.details[rowID].text2}</Text>
</View>
<View style={styles.flowRow}>
<Text
style={styles.titles}
>Text3 : </Text>
<Text
style={styles.titles}
>{this.state.Details[rowID].Text3}</Text>
</View>
<View style={styles.flowRow}>
<Text
style={styles.titles}
>Text4 : </Text>
<Text
style={styles.titles}
>{this.state.details[rowID].Text4}</Text>
</View>
</Image>
);
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
},
background: {
flex: 1,
width: null,
height: null,
},
flowRow : {
flexDirection :'row',
},
flowRowPlay : {
flexDirection :'row',
alignSelf:'center',
},
backgroundImage: {
flex: 1,
resizeMode: 'cover', // or 'stretch'
},
thumbnail: {
width: 100,
height: 120,
alignSelf:'center',
margin :30
},
controls: {
width: 30,
height: 30,
margin:20
},
titles: {
fontSize: 15,
margin:20,
color: 'white',
},
timings: {
fontSize: 12,
margin:5,
color: 'white',
},
});
module.exports = Example1;
You can handle this yourself in your component:
componentDidMount() {
this._mounted = true;
}
componentWillUnmount() {
this._mounted = false;
}
Then you can check the value of this._mounted in your listener.
Please note that using forceUpdate() should be avoided https://facebook.github.io/react/docs/component-api.html#forceupdate
Normally you should try to avoid all uses of forceUpdate() and only read from this.props and this.state in render(). This makes your component "pure" and your application much simpler and more efficient.
What I did was changing the callback in componentWillMount.
let asyncCallback;
componentDidMount(){
asyncCallback = res=> this.setState({data: res});
asyncTask(asyncCallback);
}
componentWillUnmount(){
asyncCallback = ()=> console.log("AsyncCallback called but component has unmounted");
}
Using ReactUpdateQueue, you can avoid managing your own isMounted state.
const ReactUpdateQueue = require('ReactUpdateQueue');
// Pass the ref to your component.
if (ReactUpdateQueue.isMounted(view)) {
// Your component is mounted!
}