How to navigate through a function React Native - react-native

trying to navigate to a screen after a function is called. The navigation works perfectly well when rendered in the component but not when the function is called and its conditions are met. I've tried passing navigation but that does not work. Why does React Navigation not work directly when outside render()?
onSubmit = () => {
const { base64URI } = this.props
const { captionData } = this.state
if (base64URI !== null && captionData !== null ) {
console.log('post both image data and caption data as type photo')
this.addPhoto(base64URI, captionData);
navigate.navigation('Vault') //navigation not recognised
} else {
console.log('no data')
}
}
render() {
return(
<View style={styles.headerPost}>
<TouchableOpacity style={{position: 'absolute'}} onPress={() => this.props.navigation.goBack()}> // this navigation works..
<Text style={styles.cancelButton}>Cancel</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.shareButton} onPress={() => this.onSubmit()}> //when this function is called and conditions met, I want navigation to happen
<Text style={styles.shareText}>Share</Text>
</TouchableOpacity>
</View>
</View>
);
}
}

You are using navigation in the wrong way.
it should be
this.props.navigation.navigate('Vault')

Related

How can I hide/show components by touching not button but screen on React Native?

I'm learning React Native for the first time. I want to implement a function to show/hide the component by touching the screen, not a specific button.
(Please check the attached file for the example image.)
enter image description here
In this code, I've tried to make a function. if I touch the screen (<View style={style.center}>, then show/hide the renderChatGroup() and renderListMessages() included in <View style={style.footer}>. The source code is below.
In my code, it works. However, the two <View> tag is not parallel. the footer view is center View's child.
I want to make them parallel. but I couldn't find the contents about controlling another <View> tag, not a child. In this code, I used setState, then I couldn't control another the below <View>.
Of course, I tried Fragment tag, but it didn't render anything.
How could I do implement this function? Please help me!
export default class Streamer extends React.Component {
constructor(props) {
super(props);
this.state = {
isVisibleFooter: true,
};
}
renderChatGroup = () => {
const { isVisibleFooter } = this.state;
if (isVisibleFooter) {
return (
<ChatInputGroup
onPressHeart={this.onPressHeart}
onPressSend={this.onPressSend}
onFocus={this.onFocusChatGroup}
onEndEditing={this.onEndEditing}
/>
);
}
return null;
};
onPressVisible = () => {
const { isVisibleFooter } = this.state;
this.setState(() => ({ isVisibleFooter: !isVisibleFooter }));
};
render() {
return (
<SafeAreaView style={styles.container}>
<SafeAreaView style={styles.contentWrapper}>
<View style={styles.header} />
<TouchableWithoutFeedback onPress={this.onPressVisible}>
<View style={styles.center}>
<View style={styles.footer}>
{this.renderChatGroup()}
{this.renderListMessages()}
</View>
</View>
</TouchableWithoutFeedback>
</SafeAreaView>
</SafeAreaView>
);
}
}
Firstly I would highly recommend you use react native with functional components and React Hooks as they alternative will soon will be deprecated.
Since onPress is not available on the View Component, you would need to replace it with TouchableWithoutFeedback as you have already done in your code.
For Showing/Hiding a view you would need to use a conditional operator.
export default class Streamer extends React.Component {
constructor(props) {
super(props);
this.state = {
isVisibleFooter: true,
};
}
renderChatGroup = () => {
const { isVisibleFooter } = this.state;
if (isVisibleFooter) {
return (
<ChatInputGroup
onPressHeart={this.onPressHeart}
onPressSend={this.onPressSend}
onFocus={this.onFocusChatGroup}
onEndEditing={this.onEndEditing}
/>
);
}
return null;
};
onPressVisible = () => {
this.setState(() => ({ isVisibleFooter: !isVisibleFooter }));
const { isVisibleFooter } = this.state;
};
render() {
return (
<SafeAreaView style={styles.container}>
<SafeAreaView style={styles.contentWrapper}>
<View style={styles.header} />
<TouchableWithoutFeedback onPress={this.onPressVisible}>
<View style={styles.center}>
{isVisibleFooter && <View style={styles.footer}>
{this.renderChatGroup()}
{this.renderListMessages()}
</View>}
</View>
</TouchableWithoutFeedback>
</SafeAreaView>
</SafeAreaView>
);
}
}
Here you can see i have replaced
<View style={styles.footer}>
{this.renderChatGroup()}
{this.renderListMessages()}
</View>
with
{isFooterVisible && <View style={styles.footer}>
{this.renderChatGroup()}
{this.renderListMessages()}
</View>}
stating that to only display the Footer View when
const isFooterVisible = true;

React Native - Rerunning the render method

I have a file here that defines an icon for the title.
static navigationOptions = ({ navigation }) => {
return {
headerRight: () => (<HomeHeaderIcon/>)
}
};
HomeHeaderIcon.js
export default class HomeHeaderIcon extends Component {
async componentDidMount(){
const token = await AsyncStorage.getItem('token');
this.setState({token});
}
state={
token:null
};
render() {
return (
<View>
{
this.state.token ===null
?
(
<TouchableOpacity
onPress={() => (NavigationService.navigate("LogStack"))}>
<Icon name="ios-power" size={30} style={{color: "white",marginRight:wp("5%")}}/>
</TouchableOpacity>
)
:
(
<TouchableOpacity
onPress={() => (NavigationService.navigate("Profile"))}>
<Icon name="ios-home" size={30} style={{color: "white",marginRight:wp("5%")}}/>
</TouchableOpacity>)
}
</View>
);
}
}
The system works exactly as I want. If there is a token, I say icon1 or icon2 show. The problem is I do this in componentDidMount, the icon does not change without refreshing the page. How do I render it again?
componentDidMount is called, as the name suggests, just once, when the component is mounted. Use componentDidUpdate to decide how your component behaves based on what piece of props or state has changed.
Read the documentation for more information regarding lifecycle methods.

How to create a custom alert using Redux ? (Warning: Cannot update during an existing state transition)

I want, instead of using the default Alert.alert method, to create my own so that I could change the background of my Alert, whether it's successful or not. To do so, I use a Modal, which put in my root component, App.js (so that the modal can appear on each screen) :
App.js
// ...
const Navigation = createAppContainer(MainNavigator);
// Render the app container component with the provider around it
class App extends React.Component {
render() {
return (
<Provider store={store}>
<Navigation />
<CustomAlert /> {/* <--- here */}
</Provider>
);
}
}
I do so because I don't want to place my <CustomAlert /> component on each Screen that I use. If someone has another approach I'll take it.
CustomAlert.js
// ... (imports)
class CustomAlert extends Component {
render() {
const { customStyle } = this.props;
const { title, description, enabled } = this.props;
const { reinit } = this.props; // dispatch
return (
<View>
<Modal
animationType="none"
transparent
visible={enabled}
>
<View style={styles.fullScreen}>
<View style={[styles.content, customStyle]}>
<View style={{ flex: 1 }}>
<View style={styles.title}>
<Text style={{ fontWeight: 'bold' }}>{title}</Text>
</View>
<View style={styles.desc}>
<Text style={{ textAlign: 'center' }}>{`${description}`}</Text>
</View>
<View style={styles.buttons}>
<TouchableOpacity
onPress={() => {
reinit();
}}
style={styles.button}
>
<Text>OK</Text>
</TouchableOpacity>
</View>
</View>
</View>
</View>
</Modal>
</View>
);
}
}
function mapStateToProps(state) {
return {
type: state.alert.type,
title: state.alert.title,
description: state.alert.description,
enabled: state.alert.enabled,
};
}
function mapDispatchToProps(dispatch) {
return {
reinit: () => dispatch(reinitState()),
};
}
export default stylesWrapper(connect(mapStateToProps, mapDispatchToProps)(CustomAlert));
I proceed like that:
In my function that shows the alert, I simply change my redux store:
helper.js
// ...
export const infoAlert = (title, msg, type = 'error') => {
store.dispatch(setTitle(title));
store.dispatch(setDescription(msg));
store.dispatch(setType(type));
store.dispatch(setEnabled(true));
};
// ...
The behaviour is good but I have this warning :
Warning: Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state.
I suspects this has one reason between those two:
Using store.dispatch as a general function doesn't know anything about the component that called him, so this message indicates that It might rerender it (which is bad)
infoAlert is called inside a functionnal component (Redux Form), and it's state shouldn't change (something like that)
Here an example of call infoAlert (at the bottom):
const renderField = ({
input,
label,
keyboardType,
secureTextEntry,
autoCapitalize,
autoCorrect,
meta: {
touched, error, warning, active, dirty,
},
}) => (
<View>
<TextInput
style={[styles.textInput, (active) && styles.active,
(touched) && (((error) && styles.error)
|| ((warning) && styles.warning))]}
{...input}
onChangeText={input.onChange}
onBlur={input.onBlur}
onFocus={input.onFocus}
value={input.value}
keyboardType={keyboardType}
placeholder={label}
secureTextEntry={secureTextEntry}
autoCapitalize={autoCapitalize}
autoCorrect={autoCorrect}
/>
{(touched && !active && dirty) && (((error) && infoAlert('Erreur', error))
|| ((warning) && infoAlert('Attention', warning)))}
</View>
);
Do someone has an idea of how to remove this Warning ?
Do someone has another way (maybe simpler) to create a custom alert (without putting the component itself everywhere) ?
Screens

React native Unable to render the page with force update or set state

class Wait extends Component{
constructor(props) {
super(props);
this.state = { fetchingData: true, data: [], check: ''}
this.forceUpdateHandler.bind(this);
}
getData = async() => {
try {
data = await AsyncStorage.getItem('restaurants');
if (data != null) {
this.setState({fetchingData: false , data: JSON.parse(data)})
}
} catch(error){
console.log(error)
}
}
forceUpdateHandler(){
this.forceUpdate();
};
componentDidMount(){
this.getData();
}
renderRestaurant(){
return this.state.data.map((item) => {
return (
<View style ={{marginTop: 20, backgroundColor: 'red', marginTop: 20 }}>
<Text> {item.name} </Text>
<Text> {item.time} </Text>
<Text> {item.wait} </Text>
<Text> {item.people} </Text>
<Button title = 'cancel' onPress = { async () => {
let data = await AsyncStorage.getItem('restaurants');
let temp = JSON.parse(data)
let i = -1
temp.map((value, index) => {
if (value.name == item.name){
i = index;
}
})
if (i > -1){
temp.splice(i, 1)
await AsyncStorage.setItem('restaurants', JSON.stringify(temp))
}
this.forceUpdateHandler() // First way
this.forceUpdate() // Second way
this.setState({check: 'checked'}) // Third way
}
}
/>
</View>
)
})
}
render(){
const { navigate } = this.props.navigation;
const { navigation } = this.props;
return (
<View style={{width:200, height:200, justifyContent:'center', alignItems:'center', }}>
{this.state.fetchingData ? null : this.renderRestaurant()}
</View>
)
}
}
I am trying to make the page re-render each time after I click the button. Once click the button, it access the AsyncStorage and delete the corresponding element in the array, then it update the AsyncStorage with the new array and re-render the page.
I have tried the following:
1) call forUpdate directly after the update of the AsyncStorage
2) define the forceUpdateHandler function and bind it with this
3) call this.setState after the update of the AsyncStorage
But none of the above options re-renders the page. Can someone help to fix it? An example would be great! Thanks in advance.
The answer is simple. It doesn't re-render because it has nothing to re-render. It calls the render, check each component in the render if the data used to render it has changed and render them if needed. If you look at your code, you see that on the button press, you save in the async storage the new data. However, your rendering uses this.state.data to render the item. The problem is that you never update the state of your component with the new data.
Sure, you do this.setState({check: 'checked'}), but nothing in the render is using it. So there's no point in updating the UI.
An easy way to fix it would be to call this.getData() at the end of the button onPress. That way, you would update the data state which would update your UI.
Get the updated list of restaurants { removing the selected restaurant}
Stored the updated list to Asyncstorage.
fetch the updated list from asyncStorage and set the state.
storeData = async (restaurants) => {
try {
await AsyncStorage.setItem('restaurants', JSON.stringify(restaurants))
} catch (error) {
console.log("Error", error);
}
}
renderRestaurant(){
return this.state.data.map((item, index, restaurants) => {
return (
<View key={index}>
<Text> {item.name} </Text>
<Text> {item.time} </Text>
<Button
title = 'cancel'
onPress = {() => {
let restaurantListWithoutCurrentRestaurant = restaurants.filter((restaurant)=> restaurant.name !== item.name);
this.storeData(restaurantListWithoutCurrentRestaurant);
this.getData();
}}/>
</View>
)
})
}
I think this will solve your problem.

How to access to a function in a specific js file from navigation Bar in React Native?

I'm trying to access to a function in a specific js(chat.js in my local) file from the nagivation Bar in React Native.
Naviagation Bar is stated in index.ios.js and the code is given below.
render() {
....
<Navigator
initialRoute={{ title: 'Log In Page', index: 0, component: FirstPage }}
configureScene={() => {
return Navigator.SceneConfigs.FloatFromRight;
}}
navigationBar={
<Navigator.NavigationBar
routeMapper={{
LeftButton: (route, navigator, index, navState) =>
{
if (route.index === 0) {
return null;
} else {
return (
<TouchableHighlight onPress={() => navigator.pop()}>
<Text style={styles.route_title}> Back </Text>
</TouchableHighlight>
);
}
},
RightButton: (route, navigator, index, navState) =>
{
if (route.index === 10000){
return (<Text>Done</Text>);
}else{
return null;
}
},
Title: (route, navigator, index, navState) =>
{ return (<Text style={styles.route_title}> {route.title} </Text>); },
}}
style={{backgroundColor: '#28b496'}} />
}
...
When I click 'back' in the page(chat.js), I want to execute a specific function that is stated in chat.js file, such as pusher.unsubscribe('test_channel');.
How would I be able to access an internal function from the top in React-native?
I'm looking forward to seeing any opinion on this matter!
Best
export this function
then import it in your index file
In order to achieve that you can use 'navigator' parameter. You can access component's functions through this parameter. You can access it by using structure below
First write a method in Navigator component
<Navigator initialRoute = {{name:'home'}}
renderScene = {this.renderScene.bind(this)}
navigationBar = {<Navigator.NavigationBar routeMapper={NavigationBarRouteMapper} style={styles.navBar}/>}
callerFunction= {() => this.yourFunctionName()} // this is what you should add
/>
then assign this method the function you want to call
then you can access yourFunction through this callBack function(callerFunction).
{navigator.props.callerFunction()}
Definetely you can send parameters as well.