I am using the library react-native-dropdownaler and when I am creating the ref for it in App instead of adding the component in each screen I want to simply pass its ref to my Router and use it later.
the problem with passing its ref to the sibling is - they both render together so when I am passing the ref its still undefined
render() {
return (
<View style={styles.container}>
<StatusBar />
<Router alertRef={this.alertRef}/>
<DropdownAlert ref={ref => (this.alertRef = ref)} />
</View>
);
}
You can use the createRef API to create a ref. The current property will still not be set until after the first render though.
class App extends React.Component {
alertRef = React.createRef();
render() {
return (
<View style={styles.container}>
<StatusBar />
<Router alertRef={this.alertRef} />
<DropdownAlert ref={this.alertRef} />
</View>
);
}
}
I'd pass down the component itself instead and mirror the method in the component:
render() {
return (
<View style={styles.container}>
<StatusBar />
<Router alertRef={this}/>
<DropdownAlert ref={ref => (this.alertRef = ref)} />
</View>
);
}
alertWithType(type, title, message) {
this.alertRef.alertWithType(type, title, message);
}
The solution I end up with is :
class App extends Component {
constructor(props)
super(props)
this.state = {alertRef:null}
componentDidMount() {
this.setState({alertRef:this.alertRef})
}
render() {
return (
<View style={styles.container}>
<StatusBar />
{this.state.alertRef && (
<Router
alertRef={this.state.alertRef}
language={this.props.language}
/>
)}
<DropdownAlert ref={ref => (this.alertRef = ref)} />
</View>
);
}
}
Related
I want to create a Modal component and I want to have possibility to inject to it everything what I want. Firstly I decided to use HOC but then I've changed my solution to render props. Everything works fine but I don't really like this solution. I'm wondering how could I optimize it to make it better. What is the best practise to create such kind of modal where you have button opening this modal beyond modal component. I really don't like that now I have two components with open/close state of modal. And both of them have a toggle method to open/close modal. Any suggestion? Maybe I should stick with the HOC ?
Here's the code with Component.js where CustomModal is used:
toggleModalVisibility = (visible) => {
this.setState({modalVisible: visible});
};
render() {
const question = this.props.questions[this.props.counter];
return (
<View style={styles.questionContainer}>
<CustomModal
visible={this.state.modalVisible}
toggleModalVisibility={this.toggleModalVisibility}
render={() => (
<>
<Text>{question.text}</Text>
<Text>{question.details}</Text>
</>
)}
/>
<View style={styles.questionTextContainer}>
<Text style={styles.questionText}>{question.text}</Text>
<TouchableOpacity onPress={() => this.toggleModalVisibility(!this.state.modalVisible) }>
<FontAwesome5 name='question-circle' size={30} color='#B7DBF3' light />
</TouchableOpacity>
</View>
</View>
)
}
and here's the code of CustomModal.js
export default class CustomModal extends Component {
constructor(props) {
super(props);
this.state = {
isOpen: this.props.visible
};
}
componentDidUpdate(prevProps) {
if (prevProps.visible !== this.props.visible) {
this.setState({isOpen: this.props.visible});
}
}
toggle = (isOpen) => {
this.setState({ isOpen });
this.props.toggleModalVisibility(isOpen)
}
render() {
return (
<View>
<Modal
animationType='slide'
transparent={false}
visible={this.state.isOpen}
>
<View style={{marginTop: 30, marginLeft: 5}}>
<TouchableHighlight
onPress={() => {
this.toggle(!this.state.isOpen)
}}>
<FontAwesome5 name='times-circle' size={30} light />
</TouchableHighlight>
<View>{this.props.render()}</View>
</View>
</Modal>
</View>
)
}
}
I am building a form in a React Native app using Formik.
The form doesn't fire the handleSubmit when I click on the button:
<ButtonLoading
loading={isLoading || isSubmitting}
label="Salvar"
style={styles.button}
onPress={handleSubmit}
/>
Here is my full code for this form:
import React, { Component, Fragment } from 'react';
import { View, ScrollView } from 'react-native';
import { withFormik } from 'formik';
class Form extends Component {
state = {
isCepChecked: false,
isLoading: false,
isNewAddress: true,
};
onChangeCep = () => {
// Not related to the problem
};
render() {
const { isCepChecked, isLoading } = this.state,
{
values,
errors,
touched,
setFieldValue,
setFieldTouched,
handleSubmit,
isSubmitting,
} = this.props;
return (
<View style={styles.container}>
<ScrollView style={styles.formContainer}>
{!isCepChecked ? (
<Fragment>
<View style={styles.lineContent}>
<InputComponent
label="Digite o CEP"
name="nrCepPre"
value={values.nrCepPre}
error={errors.nrCepPre}
touched={touched.nrCepPre}
onTouch={setFieldTouched}
onChange={setFieldValue}
keyboardType="phone-pad"
mask={'[00000]-[000]'}
/>
</View>
<View style={styles.lineContent}>
<ButtonLoading
isLoading={isLoading || isSubmitting}
label="Avançar"
style={styles.button}
onPress={() => this.onChangeCep()}
/>
</View>
</Fragment>
) : (
<Fragment>
<View style={styles.lineContent}>
<InputComponent
label="CEP"
value={values.nrCep}
mask={'[00000]-[000]'}
editable={false}
/>
</View>
<View style={styles.lineContent}>
<InputComponent
label="Identificação"
name="dsEndereco"
value={values.dsEndereco}
error={errors.dsEndereco}
touched={touched.dsEndereco}
onTouch={setFieldTouched}
onChange={setFieldValue}
/>
</View>
<View style={styles.lineContent}>
<ButtonLoading
loading={isLoading || isSubmitting}
label="Salvar"
style={styles.button}
onPress={handleSubmit}
/>
</View>
</Fragment>
)}
</ScrollView>
</View>
);
}
}
export default withFormik({
mapPropsToValues: (props) => ({
nrCepPre: '',
dsEndereco: '',
nrCep: '',
}),
validationSchema: enderecoSchema,
handleSubmit(values, customObject, bag) {
console.warn('handle');
},
})(Form);
Why not include your handleSubmit() func in your Form class instead by defining it as _hanlderSubmit = (e) = {...} so that there isn't a need for binding it. Then just call it as this._handleSubmit.
You can find more on arrow notation here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
i want to pass a value from activity screen to modal screen. I am trying to open a screen when click on flatlist item an dpass value of item to modal,but it shows my detail before rendering modal screen,Here is my activity screen:-
<FlatList
data={this.state.myListDataSource}
renderItem={this._renderItem}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
keyExtractor={({item,index}) => item+index}
refreshControl={
<RefreshControl
refreshing={this.state.isRefreshing}
onRefresh={this.pullToRefresh}
/>
}
/>
<ListProducts
modalVisibility={this.state.listModalVisibility}
closeModal={() =>
this.setState({listModalVisibility:false})}
listName={this.state.listName}
listId={this.state.listId}
/>
handleListItemPress = (item) => {
this.setState({
listModalVisibility:true,
listName : item.name,
listId : item.list_id
})
showMessage('List Item : '+item.listId)
}
_renderItem = ({item}) => {
return(
<TouchableOpacity onPress={() => this.handleListItemPress(item)}>
<View >
<View>
<View style={{flexDirection:'row',marginBottom:2}}>
<ImageView
image={item.pictures[0]}
style={[{marginRight:2},styles.imageStyle]}
/>
<ImageView
image={item.pictures[1]}
style={[{marginLeft:2},styles.imageStyle]}
/>
</View>
<View style={{flexDirection:'row',marginTop:2}}>
<ImageView
style={[{marginRight:2},styles.imageStyle]}
image={item.pictures[2]}
/>
<ImageView
image={item.pictures[3]}
style={[{marginLeft:2},styles.imageStyle]}
/>
</View>
</View>
<View>
<TextViewNonClickable
textViewText={item.name}
/>
<TextViewNonClickable
textViewText={item.description}
/>
</View>
<Icon
name = 'more-vertical'
type = 'feather'
color = {color.colorWhite}
iconStyle={{padding:8}}
containerStyle = {{position:'absolute',top:8,right:8}}
onPress = {() => {
showMessage('Options')
}}
/>
</View>
</TouchableOpacity>
)}
This is my modal screen where i want to get list item id or name.But that screen shows details on the screen rather than rendering modal screen .
Here is my modal screen :-
export default class ListProducts extends Component {
constructor(props){
super(props)
this.state={
products : [],
refreshing : false,
listId : 256,
listName : props.name
}
}
_renderProducts = ({item}) => {
return(
<Product
image={item.image}
name={item.name}
price={item.price}
discountedPrice={item.discounted_price}
quantityAdded={item.quantity_added}
productId={item.product_id}
stock={item.stock}
/>
)
}
render() {
const {modalVisibility,closeModal,listName,listId} = this.props;
return (
<Modal
animationIn='bounceInRight'
animationOut='bounceOutRight'
isVisible={modalVisibility}
onBackButtonPress={closeModal}
>
<View style={{flex:1,backgroundColor:color.colorWhite}}>
<Header
placement='left'
leftComponent={
<FlatList
data={this.state.products}
renderItem={this._renderProducts}
keyExtractor={(item,index) => item+index}
refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={this.pullToRefresh}
/>
}
numColumns={3}
style={{paddingLeft:2,paddingRight:2}}
/>
</View>
</Modal>
)
}
}
Step 1: pass props from modal to class.
In modal like:
this.props.setItem(“sunny”)
Step 2: Get that props in class in render method where modal initialised.
<ModalName SetItem={item => console.log(item)} \>
I'm actually using 'Dialog' from 'react-native-simple-dialogs' for popup. It works better for me than 'Modal', but i think the logic is the same.
Here is a simplified example of a edit email popup that works for me:
import React from 'react';
import { StyleSheet, View, TextInput } from 'react-native';
import { Button, Text } from 'native-base';
import { Dialog } from 'react-native-simple-dialogs';
export default class EditEmailPopup extends React.Component {
constructor(props) {
super(props);
this.state = {
isModalVisible: this.props.isModalVisible,
};
}
componentWillUpdate() {
this.state.isModalVisible = this.props.isModalVisible;
}
_updateEmailAndCloseDialog() {
// update some data...
this._onCloseDialog();
}
_onCloseDialog() {
this.setState({ isModalVisible: false});
this.props.client(); //this is a function transfered from parent that controls the visibility of the dialog.
}
render() {
return (
<View>
<Dialog
visible={this.state.isModalVisible}
onTouchOutside={() => this._onCloseDialog()}
>
<View>
<Text style={styles.text}>{'Update Email text'}</Text>
<View style={styles.popupButtons}>
<Button
transparent
style={styles.cancelButton}
onPress={() => this._onCloseDialog()}
>
<Text> {'cancel'} </Text>
</Button>
<Button
style={styles.okButton}
onPress={() => this._updateEmailAndCloseDialog()}
>
<Text> {'ok'} </Text>
</Button>
</View>
</View>
</Dialog>
</View>
);
}
}
Here is how i add my Dialog in the parent view:
{this.state.emailModalVisibility ? (
<EditEmailPopup
isModalVisible={this.state.emailModalVisibility}
client={this.afterClosePopup}
/>
) : null}
while this.state.emailModalVisibility initiated in the constructor in state as 'false'.
function written in parent:
_afterClosePopup = () => {
this.setState({
emailModalVisibility: false
});
};
and binded in the constructor so 'this' will belong to parent:
constructor(props) {
super(props);
this.afterClosePopup = this._afterClosePopup.bind(this);
this.state = {
emailModalVisibility: false
};
}
I am trying to open an item from my list but my item code is in another js. When I try to use onPress method there is no action. Also I am using Swipeout.
Here is my JobList.js where I am rendering the list of my items.
class JobList extends Component {
onJobDetails = (job) => {
this.props.navigate('JobDetails', job);
}
render() {
const { navigation } = this.props;
var renderJobs = () => {
return this.props.jobs.map((job) => {
return (
<JobItem
key={job._id}
title={job.title}
shortDescription={job.shortDescription}
logo={job.avatar}
company={job.company}
id={job._id}
dispatch={this.props.dispatch}
onPress={() => this.onJobDetails(job)}
/>
)
})
}
return (
<View style={styles.container}>
<ScrollView>
{renderJobs()}
</ScrollView>
</View>
);
}
};
And here is my JobItem.js
class JobItem extends Component {
render() {
return (
<Swipeout {...swipeSettings}>
<View style={styles.jobContainer}>
<View>
<Text style={styles.postTitle}>{this.props.title}</Text>
<Text style={styles.postShortDescription}>{this.props.shortDescription}</Text>
</View>
<View>
<Image
style={styles.postLogo}
source={{uri: '' + this.props.logo + ''}}/>
</View>
</View>
</Swipeout>
)
}
};
Any idea how shall I fix this?
You need to pass onPress prop to the child component in order for it to work.
<Swipeout {...swipeSettings}>
<TouchableWithoutFeedback onPress={this.props.onPress}>
//... other children here
</TouchableWithoutFeedback>
</Swipeout>
Second day using React Native so I've no idea what I'm doing, but how do I switch tabs from within another component with react-native-scrollable-tab-view?
https://github.com/skv-headless/react-native-scrollable-tab-view
I have a MenuButton in a ButtonPage that I'm trying to switch tabs with using an onPress. However when I tap it I get:
undefined is not an object (evaluating 'this.props.tabView.goToPage')
What I've got is this
export default class Home extends Component {
render() {
return (
<View style={{flex: 1}}>
<StatusBar
barStyle='light-content'/>
<ScrollableTabView initialPage={1} renderTabBar={false}
ref={(tabView) => { this.tabView = tabView}}>
<FriendsPage tabView={this.tabView} tabLabel="Friends" />
<ButtonPage tabView={this.tabView} tabLabel="Button" />
<HangoutsPage tabView={this.tabView} tabLabel="Hangouts" />
</ScrollableTabView>
</View>
)
}
}
And in my ButtonPage I have this
export default class ButtonPage extends Component {
render() {
return (
<View style={styles.container}>
<View style={styles.buttonsContainer}>
<HangoutsButton/>
<View style={styles.spacer}/>
<MenuButton/>
</View>
</View>)
}
}
And that MenuButton is described by this
export default class MenuButton extends Component {
constructor(props) {
super(props)
this.goToPage = this.goToPage.bind(this)
}
goToPage(pageNumber) {
this.props.tabView.goToPage(pageNumber)
}
render() {
return (
<Indicator
position='left top'
value='3'
type='warning'>
<TouchableHighlight
onPress={() => this.goToPage(0)}
underlayColor='#ed8600'
style={styles.touchable}>
<Image source={require('./resources/menuicon.png')} style={styles.image} />
</TouchableHighlight>
</Indicator>
)
}
}
How do I get the reference for my scrollableTabView all the way down to my button so I can tap it and change the page with goToPage?
I assumed you could stick the object in a prop and pass that prop all the way down and then use the function goToPage on it from MenuButton.
You don't need to tabview ref all the way down. Just use
export default class MenuButton extends Component {
render() {
return (
<Indicator
position='left top'
value='3'
type='warning'>
<TouchableHighlight
onPress={() => this.props.onPress && this.props.onPress()}
underlayColor='#ed8600'
style={styles.touchable}>
<Image source={require('./resources/menuicon.png')} style={styles.image} />
</TouchableHighlight>
</Indicator>
)
}}
export default class ButtonPage extends Component {
render() {
return (
<View style={styles.container}>
<View style={styles.buttonsContainer}>
<HangoutsButton/>
<View style={styles.spacer}/>
<MenuButton onPress={ () => this.props.goToPage && this.props.goToPage() }/>
</View>
</View>)
}}
And finally
export default class Home extends Component {
goToPage(pageId) {
this.tabView.goToPage(pageId);
}
render() {
return (
<View style={{flex: 1}}>
<StatusBar
barStyle='light-content'/>
<ScrollableTabView initialPage={1} renderTabBar={false}
ref={(tabView) => { this.tabView = tabView}}>
<FriendsPage tabView={this.tabView} tabLabel="Friends" />
<ButtonPage tabView={this.tabView} tabLabel="Button" goToPage={ () => this.goToPage(1) } />
<HangoutsPage tabView={this.tabView} tabLabel="Hangouts" />
</ScrollableTabView>
</View>
)
}
}