React native call functions and change states from a component - javascript

So I currently have a function that renders a component NewButton, in that component, how do I call the function toggleModalVisibility, which is defined outside of that component? That function will change many states and call other functions, and change rendering as well.
import React, { useState, useEffect} from 'react'
import {Button, View, Text, StyleSheet,TextInput,ScrollView, Modal} from 'react-native'
import { BottomSheet } from 'react-native-btr';
import NewButton from './NewButton'
export default function CardFolder({navigation, navigation: {setOptions}}){
const [visible, setVisible] = useState(false);
//many other states
const toggleModalVisibility = () => {
//changes states, calls other functions, change rendering, Toggle bottomSheet
};
return(
< >
<Modal> modal containing a text field, change states </Modal>
<BottomSheet>contains other options, upload something to db</BottomSheet>
<View style = {{flex:1}}>
<NewButton style = {{bottom: 50}} />
</View>
</>
)
}
The Component:
import {View, Button} from 'react-native'
export default class NewButton extends React.Component {
return(
<View style = {[styles.container,this.props.style]}>
//how do I call the functon toggleModalVisibility here?
<Button onPress={toggleModalVisibility}/>
</View>
)
}
}

you can add it as a prop to NewButton Component
<NewButton style={{bottom: 50}} toggleModalVisibility={toggleModalVisibility} />
and the call it like
<Button onPress={this.props.toggleModalVisibility}/>

As #Krosf suggested you, you should pass the function by props and then call the function when Button is pressed, in your case you are using class component, so you should use the "this" keyword in order to access your "NewButton props"
like this:
<Button onPress={ this.props.toggleModalVisibility } />

Related

React Native, onPress not firing

Cannot get any output from my function test() which I have written in Boxes.js when I add onPress={test} to my View tag in Dice.js.
I have tried to add test() on tags like Text, then it works, but not when I put it on the View tag.
Boxes.js:
import react from "react";
import { StyleSheet, Text, View, Image } from "react-native";
import Dice from "./Dice";
import PatternDivider from "./PatternDivider";
export function test() {
console.log("hi");
}
export default class Boxes extends react.Component {
render() {
return (
<View style={styles.box}>
<Text style={styles.header}>Advice #117</Text>
<Text style={styles.advice}>
It is easy to sit up and take notice, what's difficult is getting uu
and taking action
</Text>
<PatternDivider />
<Dice />
</View>
);
}
}
Dice.js:
import react from "react";
import { StyleSheet, Text, View, Image } from "react-native";
import { test } from "./Boxes";
export default class Dice extends react.Component {
render() {
return (
<View style={styles.circle} onPress={test}>
<Image
source={require("../assets/icon-dice.png")}
style={styles.dice}
/>
</View>
);
}
}
If you wanna Dice to be clickable, use one the components that handles press events, like Pressable. Not all components accept an onPress property, View is one of them.
import react from "react";
import { StyleSheet, Text, View, Image, Pressable } from "react-native";
import { test } from "./Boxes";
export default class Dice extends react.Component {
render() {
return (
<Pressable style={styles.circle} onPress={test}>
<Image
source={require("../assets/icon-dice.png")}
style={styles.dice}
/>
</Pressable>
);
}
}
React Native's View component doesn't have an onPress property https://reactnative.dev/docs/view

React Native Expo error: Hooks can only be called inside the body of a function component

I am developing mobile app using React Native expo.. I am getting the following exception:
Invalid hook call. Hooks can only be called inside the body of a function component....
I have gone through the other answers posted here on SO and have confirmed that the hook in my code is indeed inside a function. But still I am unable to resolve the error. KIndly help. Please see my code below. Let me know if more clarification is needed.
import React, { useState, useEffect } from 'react';
import { SafeAreaView, StatusBar, Button, View, Platform , Text} from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import Constants from 'expo-constants';
const statusBarPadding = Platform.OS === 'android' ? StatusBar.currentHeight: 0;
export default function OpenGallery () {
const [image, setImage] = useState(null);
useEffect(() => { // hook is inside function but still getting error
(async () => {
if (Platform.OS !== 'web') {
const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (status !== 'granted') {
alert('Sorry, we need camera roll permissions to make this work!');
}
}
})();
}, []);
return (
<SafeAreaView style={{ paddingTop: statusBarPadding }}>
<Text> Some text </Text>
</SafeAreaView>
);
}
Second file:
import React from 'react';
import { SafeAreaView, StatusBar, Button, View, Platform , Text} from 'react-native';
import OpenGallery from './OpenGallery'
const statusBarPadding = Platform.OS === 'android' ? StatusBar.currentHeight: 0;
export default function CameraScreen() {
return (
<SafeAreaView style={{ paddingTop: statusBarPadding }}>
<Text> Upload image from gallery.</Text>
<Button title="Select from Gallery" onPress={OpenGallery} />
</SafeAreaView>
);
}
it is in fact not being called from inside the component. you are calling it from the JSX, which is entirely different. there are two issues here that break the rules of hooks.
hooks must be called unconditionally. you are breaking that rule here.
<Button title="Select from Gallery" **onPress={OpenGallery}** />
Hooks must be called from inside a functional component. you cannot import another component and call it as a function. this is what you're doing. you are calling the react component on a onPress method which is wrong.
What can you do to fix it?
bring the state down. make the check to see if it's on a web or a mobile in the second file itself. i would post the code but i don't have expo installed at the moment.
It might sound out of scope here but I think in your case, you need to specify the behaviour on how do you want the OpenGallery to appear after pressing the button on CameraScreen... you may be using navigation (may be react-navigation) or using a modal.
Let's say you're using react-navigation. (as you're using expo, it's normally included in the project) https://reactnavigation.org/
On CameraScreen
export default function CameraScreen({navigation}) {
const gotoOpenGallery = () => {
navigation.navigate('OpenGallery');
}
return (
<SafeAreaView style={{ paddingTop: statusBarPadding }}>
<Text> Upload image from gallery.</Text>
<Button title="Select from Gallery" onPress={gotoOpenGallery} />
</SafeAreaView>
);
}
You will also need to create a StackNavigator
Your App.js (or your entry point of the app)
import * as React from 'react';
import { View, Text } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import OpenGallery from './OpenGallery';
import CameraScreen from './CameraScreen';
const Stack = createStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="CameraScreen" component={CameraScreen} />
<Stack.Screen name="OpenGallery" component={OpenGallery} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;

Example to call a costant with "onValueChange" on a Checkbox in react native

Imagine to have two files:
constant.js, which contains a constant, for example a text to show, that i export
app.js, which contains my checkbox
I would like to import and run (in App.js) the constant contained in constant.js (means showing the text) once i "check" the checkbox using OnValueChange.
Can anyone write a short working example to show me how to do it?
You can use the checkbox from React native elements like below
https://reactnativeelements.com/docs/checkbox/
import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
import { CHECKBOX_TEXT } from './constants';
import { CheckBox } from 'react-native-elements';
export default function App() {
const [checked, setChecked] = React.useState(false);
return (
<View>
<CheckBox
center
title={'Set visible'}
checked={checked}
onPress={() => setChecked(!checked)}
/>
{checked && <Text>{CHECKBOX_TEXT}</Text>}
</View>
);
}
And have a constants file like this
export const CHECKBOX_TEXT='TEXT';
You can try this in the below snack
https://snack.expo.io/kityqhINE

React native not passing props between components?

I have a simple container component that should handle some logic at some point:
import React, {Component} from 'react';
import {Text, View, Button, Image} from 'react-native';
import Title from '../Presentational/Title';
class AppList extends React.Component {
render() {
return (
<Title titleProperty={'To Do Something'}></Title>
);
}
}
export default AppList;
I try to pass some props to a Title component so that component would display them:
import React, {Component} from 'react'
import {View, Text, StyleSheet} from 'react-native'
export default class Title extends React.Component {
render() {
const {children} = this.props.titleProperty;
return (
<View style = {styles.header}>
<Text style={styles.title}>{children}</Text>
</View>
)
}
}
const styles = StyleSheet.create({
header: {
backgroundColor: 'skyblue',
padding: 15,
},
title: {
textAlign: 'center',
color: 'white',
},
})
The result I'm getting is a blue bar without any text
imgur link
Why is it not working?
The reason it is not working is because in Title.js you are trying to get value of titleProperty incorrectly.
const {children} = this.props.titleProperty; implies that you want to store value of this.props.titleProperty.children inside children constant.
what you should do is read value of titleProperty and then it will it display correctly in your component.
You can do this in various ways, few of them are listed below
const children = this.props.titleProperty;
const { titleProperty } = this.props;
In the first option you can read titleProperty from props and assign it to any named variable you want. In the later option it will read the value of key from this.props and only assign the value of key is present otherwise undefined
Find output here
The problem is this line:
const {children} = this.props.titleProperty;
In this way you're trying to deconstruct the titleProperty which should be an object and should have the children property.
More about destructuring on MDN
I am not sure if you are confused with the children props of React, in this case I recommend you to read this answer: https://stackoverflow.com/a/49706920/9013688

Accessing data from service in other components

I was given below task in an interview, here the task is about getting a response from API using ajax call on button click and display it on a page.
I have a top component inside App.js, with two child components as MyButton.js and MyPage.js and the service code in MyAPI.js
Below are the file contents:
App.js
import React, { Component } from 'react';
import MyAPI from './services/MyAPI';
import MyButton from './components/MyButton';
import MyPage from './components/MyPage';
class App extends Component {
constructor() {
super();
this.state= {
'apiResponse': ''
};
}
handleButtonClick = () => {
MyAPI.getAPIResponse().then((res) => {
res => this.setState({ apiResponse })
});
}
render() {
return (
<div>
<center><MyButton onClickButton={this.handleButtonClick}></MyButton></center>
<MyPage apiResponse={this.props.apiResponse}></MyPage>
</div>
);
}
}
export default App;
MyButton.js
import React from 'react';
import PropTypes from 'prop-types';
import Button from '#material-ui/core/Button';
const MyButton = (() => (
<div className="button-container">
<MyButton variant="extendedFab" color="primary"
onClick={this.props.onClickButton}>
Call API
</MyButton>
</div>
));
MyButton.propTypes = {
onClickButton: PropTypes.func
}
export default MyButton;
MyPage.js
import React from 'react';
import PropTypes from 'prop-types';
import List from '#material-ui/core/List';
import ListItem from '#material-ui/core/ListItem';
import ListItemText from '#material-ui/core/ListItemText';
import Paper from '#material-ui/core/Paper';
const MyPage = (() => (
<Paper className="container">
<List>
<ListItem>
<ListItemText>Name: {this.props.apiResponse.split(" ")[0]}</ListItemText>
</ListItem>
</List>
</Paper>
));
MyPage.propTypes = {
apiResponse: PropTypes.string
}
export default MyPage;
MyAPI.js
import axios from 'axios';
export default {
getAPIResponse() {
return axios.get("--url to get user name and age as json--").then(response => {
return response.data;
});
}
};
Here the JSON data contains the name of a sample user just for demo purpose eg: John Doe. I need to display only John on my page as per the given task.
When I run this application I am getting errors at my MyButton.js and MyPage.js in logs.
In MyButton.js the error is at line onClick={this.props.onClickButton}, it says cannot access props on undefined. If I change it to onClick={this.onClickButton}, I got an error as, cannot access onClickButton on undefined. What is the correct way to do this here, please help.
Also same applies to MyPage.js at line {this.props.apiResponse.split(" ")[0], also is it the right way to use the split method here to get the first name from John Doe?
Your MyButtn and MyPage both are functional components. To access the props you do not need to use this. props are taken as params in case of functional components.
MyButton
const MyButton = ((props) => (
<div className="button-container">
<MyButton variant="extendedFab" color="primary"
onClick={props.onClickButton}>
Call API
</MyButton>
</div>
));
MyPage
const MyPage = ((props) => (
<Paper className="container">
<List>
<ListItem>
<ListItemText>Name: {props.apiResponse.split(" ")[0]}</ListItemText>
</ListItem>
</List>
</Paper>
));
once the response success you have to store in the variable
var a = "jhon doe";
var data = a.split(" ");
data[0];
you can do this in a parent component.

Categories