I'm using react-native-simple-modal for displaying models in android devices. Here the model always opens at the center of the screen. Setting the initial offset value in constructor has no effect. However moveUp function is working well. How to show the model at the top of the screen initially? Moreover I need to put the model just below a component.
CODE:
constructor(props) {
super(props);
this.state = {
open: false,
offset: -200,
};
}
moveUp = () => this.setState({offset: -200})
openModal = () => this.setState({open: true})
closeModal = () => this.setState({open: false})
render() {
return (
<View style={{flex: 1, justifyContent: 'flex-start', alignItems: 'center'}}>
<TouchableOpacity onPress={this.openModal}>
<Text>Open modal</Text>
</TouchableOpacity>
<Modal
offset={this.state.offset}
open={this.state.open}
modalDidOpen={this.modalDidOpen}
modalDidClose={this.modalDidClose}>
<View style={{alignItems: 'center'}}>
<Text style={{fontSize: 20, marginBottom: 10}}>Hello world!</Text>
<TouchableOpacity
onPress={this.moveUp}>
<Text>Move modal up</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={this.resetPosition}>
<Text>Reset modal position</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={this.closeModal}>
<Text>Close modal</Text>
</TouchableOpacity>
</View>
</Modal>
</View>
)
};
Currently there is no option to align your modal according to your offset in a initial stage. I just went through the internal code of react-native-simple-modal and found out that on the first time this doesn't understand the offset sent through the props. If you want this to be implemented then you can ask the author of this module to update the following code or you can just merge a PR on this module.
Inside the main index.js file of react-native-simple-modal you can see that the default state offset is being set as 0 instead of understanding it through the offset sent from props. So change the below line as:-
state = {
opacity: new Animated.Value(0),
scale: new Animated.Value(0.8),
offset: new Animated.Value(0)
};
to the code below:-
state = {
opacity: new Animated.Value(0),
scale: new Animated.Value(0.8),
offset: new Animated.Value(this.props.offset) ===>> update this initial value.
};
This offset works fine after the initial phase because the author of the module has understood the props offset in the componentWillReceiveProps lifecycle hook of the component. So just ask the author to update this or you can just raise the PR for this. I have checked this by updating the value in node_modules and if you just want to check if its working according to your requirement then you can also update it once and check that out.
I hope this will help you and still if you have any problem then let me know.
Related
in my react-native application, there are two screens. I want to navigate from one screen to another such that if I press a button in the next screen, the first screen should be refreshed using a method doRefreshing() in the first screen.
Example:
const FirstScreen = (props) => {
const doRefreshing = () => {
...
};
return (
<View>
<TouchableOpacity
onPress={()=>props.navigation.navigate("secondScreen",doRefreshing())}
>
</TouchableOpacity>
</View>
);
}
The example code of second screen:
const SecondScreen = (props) => {
const doRefreshingInFirstScreen = () => {
/** What should I do here? */
};
return (
<View>
<Button
onPress={()=>doRefreshingInFirstScreen()}
>
</Button>
</View>
);
}
Can someone please tell me how shall I refresh the first screen when the button in second screen is pressed and call goes to doRefreshingInFirstScreen()? Or what else I can do to refresh the first screen upon clicking the button in the second screen? Will be really grateful for this help. Thanks in advance.
Did you mean this?
Snack
function HomeScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details', {onRefresh: () => console.log('refreshing...')})}
/>
</View>
);
}
function DetailsScreen(props) {
const { route } = props;
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
<Button onPress={route.params.onRefresh} title="Refresh" />
</View>
);
}
React Native re-renders the screen whenever there is state change but this re-rendering will not happen if the screen is not being displayed. As for your scenario, I will suggest using redux to store the values that need to be refreshed from the second screen. Now whenever you go back to the first screen, then if you are reading value from the redux store, it will read the new values for all the variables that needed to be refreshed from the second screen, thus you will have the new values instead of the old ones.
Background:
I've designed a custom footer for my app in React Native, I've set some images to act as icons. I'm trying to have them redirect to other pages of the app upon touch.
What I have tried
I've been trying to use the same images nested within TouchableOpacity components to have them redirect to other pages using react navigation.
This is my code:
export class Footer extends React.Component {
render (){
return (
<View style = { styles.footStyle } >
<TouchableOpacity onPress={ () => navigation.push('Home')} >
<Image
style = { styles.iconStyle }
source = {require('./img/home.png')}/>
</TouchableOpacity>
<TouchableOpacity onPress={ () => navigation.push('Favoritos')} >
<Image
style = { styles.iconStyle }
source = {require('./img/heart.png')}/>
</TouchableOpacity>
<TouchableOpacity onPress={ () => navigation.push('Search')} >
<Image
style = { styles.iconStyle }
source = {require('./img/search.png')}/>
</TouchableOpacity>
<TouchableOpacity onPress={ () => navigation.push('Notifications')} >
<Image
style = { styles.iconStyle }
source = {require('./img/bell.png')}/>
</TouchableOpacity>
<TouchableOpacity onPress={ () => navigation.push('Help')} >
<Image
style = { styles.iconStyle }
source = {require('./img/circle.png')}/>
</TouchableOpacity>
</View>
)
}
}
const styles = StyleSheet.create({
footStyle: {
paddingBottom: 0,
paddingRight: 10,
backgroundColor: '#ffffff',
flex: 0.4,
flexDirection: 'row',
borderTopWidth: 1,
borderTopColor: '#000000'
},
iconStyle: {
flex: 0.2,
height: undefined,
width: undefined
}
})
Problem
When I try and run the app in expo, the images are not rendering at all. I get my blank footer without any content. I've tried touching the footer to see if the images weren't rendering but the "button" actually worked, that didn't work.
Question
How exactly can I nest an image within a TouchableOpacity component? Is it even possible to use this method with React Navigation?
Thanks a lot!
For an Image component to work you should provide a height and width in style.
Here you are setting it as undefined
Try something like
iconStyle: {
flex: 0.2,
height: 100,
width: 100
}
Also on the navigation, you will have to pass the navigation prop to the Footer. As its a class you should access it as this.props.navigation.navigate()
As your code for integrating the Footer is not here, its hard to comment on how to pass the prop to the footer.
I am new to React Native and trying to accomplish something like below, where simple list from server is rendering in a list with a button. Button, upon click, will be disabled and opacity will be changed.
I am able to create the UI but when I click any button that says Join, previous clicked button resets it state to original. In other words, only one button displays clicked state all the time.
so my code is something like
constructor(props){
super(props);
this.state = {selectedIndices: false, groupsData: ''};
}
Flatlist inside render method looks like
<FlatList style={styles.list}
data={this.state.groupsData}
keyExtractor={(groups) => {
return groups.groupId.toString();
}}
renderItem={(item, index) => {
return this.renderGroupItem(item);
}}/>
RenderGroupItem
renderGroupItem = ({item} )=>(
<GroupItem group = {item} style={{height: '10%'}} onPress = {() => this.onJoinButtonPress(item)}
index = {this.state.selectedIndices}/>
)
onJoinButtonPress
onJoinButtonPress = (item) =>{
this.setState({selectedIndices: true});
}
GroupItem
render(){
if(this.props.group.groupId === this.props.index){
return(
<View style = {[styles.container]}>
<Image source={{uri: 'some Image Url'}} style={styles.roundImage}/>
<Text style={styles.groupText}>{this.props.group.name}</Text>
<View >
<TouchableOpacity style = {[styles.button, {opacity: 0.4}]} activeOpacity = { .5 } onPress = {this.props.onPress}
disabled = {true}>
<Text style = {{color: 'white', fontSize: 12}}>Joined</Text>
</TouchableOpacity>
</View>
</View>
);
}else{
return(
<View style = {styles.container}>
<Image source={{uri: 'Some Image Url'}} style={styles.roundImage}/>
<Text style={styles.groupText}>{this.props.group.name}</Text>
<View >
<TouchableOpacity style = {styles.button} activeOpacity = { .5 } onPress = {this.props.onPress}>
<Text style = {{color: 'white', fontSize: 12}}>Join</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
Now I know that I need to pass an array or hasmap which contains mapping of items that have been clicked but I dont know how to do that. Need desperate help here.
I was able to overcome above problem after maintaining a boolean in the groupsData. Upon selection, I update a boolean "groupsJoined" in groupsData and update the state of groupsData which will invoke render. Inside GroupsItem class, I added a check that if data from props has joinedGroups as true then render selected state else non selected state.
Courtesy https://hackernoon.com/how-to-highlight-and-multi-select-items-in-a-flatlist-component-react-native-1ca416dec4bc
I want to directly update the value of a component due to performance reasons.
render(){
<View>
<Text style={styles.welcome} ref={component => this._text = component}>
Some Text
</Text>
<TouchableHighlight underlayColor='#88D4F5'
style={styles.button}>
<View>
<Text style={styles.buttonText}
onPress={this.useNativePropsToUpdate.bind(this)}>
Iam the Child
</Text>
</View>
</TouchableHighlight>
</View>
}
This is the method I use to update the text component. I dont know if I am setting the right attribute/ how to figure out which attribute to set:
useNativePropsToUpdate(){
this._text.setNativeProps({text: 'Updated using native props'});
}
Essentially trying to follow the same approach from this example:
https://rnplay.org/plays/pOI9bA
Edit:
When I attempt to explicitly assign the updated value:
this._text.props.children = "updated";
( I know this this the proper way of doing things in RN ). I get the error "Cannot assign to read only property 'children' of object'#'"
So maybe this is why it cant be updated in RN for some reason ?
Instead of attempting to change the content of <Text> component. I just replaced with <TextInput editable={false} defaultValue={this.state.initValue} /> and kept the rest of the code the same. If anyone know how you can change the value of <Text> using setNativeProps OR other method of direct manipulations. Post the answer and ill review and accept.
The text tag doesn't have a text prop, so
this._text.setNativeProps({ text: 'XXXX' })
doesn't work.
But the text tag has a style prop, so
this._text.setNativeProps({ style: { color: 'red' } })
works.
We can't use setNativeProps on the Text component, instead, we can workaround and achieve the same result by using TextInput in place of Text.
By putting pointerEvent='none' on the enclosing View we are disabling click and hence we can't edit the TextInput (You can also set editable={false} in TextInput to disbale editing)
Demo - Timer (Count changes after every 1 second)
import React, {Component} from 'react';
import {TextInput, StyleSheet, View} from 'react-native';
class Demo extends Component {
componentDidMount() {
let count = 0;
setInterval(() => {
count++;
if (this.ref) {
this.ref.setNativeProps({text: count.toString()});
}
}, 1000);
}
render() {
return (
<View style={styles.container} pointerEvents={'none'}>
<TextInput
ref={ref => (this.ref = ref)}
defaultValue={'0'}
// editable={false}
style={styles.textInput}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 0.7,
justifyContent: 'center',
alignItems: 'center',
},
textInput: {
fontSize: 60,
width: '50%',
borderColor: 'grey',
borderWidth: 1,
aspectRatio: 1,
borderRadius: 8,
padding: 5,
textAlign: 'center',
},
});
export default Demo;
As setNativeProps not solving the purpose to alter the content of <Text />, I have used below approach and is working good. Create Simple React Component like below...
var Txt = React.createClass({
getInitialState:function(){
return {text:this.props.children};
},setText:function(txt){
this.setState({text:txt});
}
,
render:function(){
return <Text {...this.props}>{this.state.text}</Text>
}
});
I have created an app which uses the Navigator component, I'm wondering is there a way I can implement a header and footer component outside of the scene?
A screenshot of what I have currently:
http://i.stack.imgur.com/XHDKC.png
This first attempt was accomplished by making the header and footer a single component with absolute styles.
//index.js
<Navigator initialRoute={{id: 'home', title: window.title}}
renderScene={renderScene}
navigationBar={<DefaultHeader toggleSideMenu={this.toggleSideMenu}
route={route.id} />}/>
//DefaultHeader.js
<View style={styles.navContainer}>
<View style={styles.header}>
</View>
<View style={styles.footer} shouldUpdate={false}>
</View>
</View>
Although appeared to work, I was unable to click around anything within the scene due to the render order in React's Navigator component.
I decided to re-think my approach and fully separated navigation bars from the Navigator component. This relies on you passing down a routing function and any other route info.
routeTo: function (route) {
if (route.to == "back") {
this.refs.navigator.pop();
} else {
this.refs.navigator.push(route);
}
},
canGoBack: function () {
return this.refs.navigator && this.refs.navigator.getCurrentRoutes().length > 1
},
getDefaultRoute: function () {
return {id: 'home', title: window.title};
},
getCurrentRoute: function () {
if (this.refs.navigator) {
return _.last(this.props.navigator.getCurrentRoutes());
}
return this.getDefaultRoute();
},
render() {
return (
<View style={styles.container}>
<DefaultHeader routeTo={this.routeTo} route={this.getCurrentRoute()}
toggleSideMenu={this.toggleSideMenu}/>
<Navigator
ref="navigator"
initialRoute={this.getDefaultRoute()}
renderScene={renderScene}
/>
<DefaultFooter routeTo={this.routeTo} route={this.getCurrentRoute()}/>
</View>
)
}
Although it is pretty "hacky" - why don't you add a third view (expanding fully) between the header and footer and set onStartShouldSetResponder and onMoveShouldSetResponder to return false for both: the middle view and the navContainer view). See https://facebook.github.io/react-native/docs/gesture-responder-system.html. I am not sure if it will work but it might be worth trying.
The best way, however, would be to modify the Navigator component and add footer props and displaying there. It's pure javascript, so it should be fairly easy to do.
I am using RN 0.36 and I was able to workaround this by using the navigator height to margin the footer:
<View style={{flex: 1}}>
<ScrollView>
...
</ScrollView>
<View style={{
height: 40,
borderTopWidth: 1,
borderTopColor: colors.grey,
flex: 0,
marginBottom: Navigator.NavigationBar.Styles.General.TotalNavHeight
}}>
<Text>Footer</Text>
</View>
</View>
where my index files (ie index.ios.js) looks like
<NavigatorIOS
style={{flex: 1}}
initialRoute={{
title: ' ',
component: Main
}}
...
Check NavigatorIOS and Navigator