Pass Function to Functional Component in React Native - reactjs

I have an ItemForm component in a react-native expo app I would like to use for both Creates and Updates. It is a functional component which I am trying to pass a handler from a parent component to either create a new item or update an existing item. I am having trouble understanding how to pass the onPress handler down from the parent Class Component down to the functional child form Component.
Parent Component:
import React, { Component } from "react";
import { Text, View, StyleSheet } from 'react-native';
import Constants from 'expo-constants';
// You can import from local files
import ItemForm from './components/ItemForm';
// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';
export default class App extends Component {
handleSubmit = ({ name }) => {
alert(name);
// todo: get form data and create or update database
}
render() {
return (
<View style={styles.container}>
<Card>
<ItemForm onSubmit={() => this.handleSubmit}/>
</Card>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
});
ItemForm.js
import React, { useState } from "react";
import { View, Text, StyleSheet } from "react-native";
import { Button, Card, Icon, Input } from "react-native-elements";
export default function ItemForm({ onSubmit }) {
const [name, setName] = useState("");
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerText}>Add an Item</Text>
</View>
<Card
containerStyle={{
height: "80%",
margin: 0,
elevation: 1,
borderWidth: 0,
shadowOpacity: 0,
shadowRadius: 0,
}}
>
<Input
label="Item Name"
labelStyle={styles.itemText}
placeholder="Name Here"
onChangeText={(name) => setName(name)}
/>
<Button
icon={
<Icon
name="check-circle"
size={20}
style={{ paddingRight: 10 }}
onPress={onSubmit({name: name})}
/>
}
title="Save Item"
/>
</Card>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: "column",
},
header: {
alignItems: "center",
paddingTop: 40,
},
headerText: {
fontSize: 32,
},
itemText: {
fontSize: 25,
}
});
This is currently throwing:
onSubmit is not a function. (In 'onSubmit({name: name})', 'onSubmit' is undefined)
I've tried various different syntaxes of passing or calling the function by reading the docs here but I run into a lot of errors like Please attach a method to this component and I can't seem to get past this?

onPress should be a function that calls onSubmit, not the return value of calling onSubmit (onPress={onSubmit({name: name})}).
onPress needs to be on the Button.
You need to pass down this.handleSubmit from the parent component, and not a function that returns this.handleSubmit (onSubmit={() => this.handleSubmit}):
So in App:
<ItemForm onSubmit={this.handleSubmit}/>
In ItemForm:
<Button
onPress={() => onSubmit({name: name})}
icon={
<Icon
name="check-circle"
size={20}
style={{ paddingRight: 10 }}
/>
}
title="Save Item"
/>
demo

Related

I am getting maximum depth exceeded while running react native code

import { StyleSheet, Text, View, ImageBackground, SafeAreaView } from 'react-native';
import react, {useState} from 'react';
import InputComponent from './sharedComponent/InputComponent';
import ButtonComponent from './sharedComponent/ButtonComponent';
export default function App() {
const[text, setText] = useState('');
const[todoList, setToDoList] = useState([]);
return (
<ImageBackground
source={require('./assets/todoimages.png')}
style={{width: '100%', height: '100%'}}
>
<SafeAreaView>
<View style={styles.container}>
<InputComponent onchangeValue={setText}
/>
<ButtonComponent
addItem={setToDoList}
itemToAdd={text}
/>
</View>
<View>
{todoList.length > 0 && todoList.map((value) => <Text>{value}</Text>)}
</View>
</SafeAreaView>
</ImageBackground>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'baseline',
justifyContent: 'center',
paddingBottom:'10%'
},
});
I am getting the error :
Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
can anyone tell me why I am getting the error and what is the solution for that?
below is the button component:
import {View,Button, StyleSheet} from 'react-native';
const ButtonComponent = (props) =>{
return(
<View style={styles.container}>
<View style={styles.buttonContainer}>
<Button
onPress={props.addItem(prevArray=> [...prevArray, props.itemToAdd])}
title="ADD"
/>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
buttonContainer: {
margin: 20
},
})
export default ButtonComponent;
below is the input component:
import { Text, TextInput, View } from 'react-native';
const InputComponent = (props)=>{
return(
<TextInput
style={{height: 40, backgroundColor:'white'}}
placeholder="add the item in to do list"
onChangeText={newText => props.onchangeValue(newText)}
/>
)
}
export default InputComponent;
You must use arrow-functions in the onPress method of your buttonComponent like this:
onPress={()=>props.addItem(...)}

React Native - React Navigation Cannot use Hooks in DrawerContent

I am developing an e-commerce application using React Native and I am trying to use useState in the drawerContent and it tells me this
Error: Invalid hook call. Hooks can only be called inside of the body
of a function component. This could happen for one of the following
reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
Thank you in advance for your answers.
Here's the code
import React, { useState } from 'react'
import { View, Text, TouchableOpacity, FlatList, StyleSheet, StatusBar } from 'react-native'
import IonIcons from "react-native-vector-icons/Ionicons"
import { categories } from '../../../services/DataTest'
import DrawerSearch from './DrawerSearch'
import DrawerItem from './DrawerItem'
export default function DrawerContent (props) {
const [search, setSearch] = useState("");
return (
<View>
<TouchableOpacity
style={styles.customDrawerTouch}
>
<View style={styles.backButtonRow}>
<IonIcons
name="ios-arrow-back"
size={25}
style={styles.customDrawerIcon}
color="#666666"
/>
<Text style={{ color: '#666666' }}>Back to Components</Text>
</View>
</TouchableOpacity>
<DrawerSearch value={search} setValue={setSearch}/>
<FlatList
data={categories}
keyExtractor={(item, index) => index.toString()}
renderItem={DrawerItem}
/>
</View>
);
}
const styles = StyleSheet.create({
customDrawerTouch: {
marginTop: StatusBar.currentHeight,
paddingLeft: 13,
paddingTop: 15,
},
customDrawerIcon: {
paddingRight: 10
},
backButtonRow: {
flexDirection: 'row',
alignItems: 'center',
paddingBottom: 17,
paddingLeft: 3,
borderBottomColor: '#F0F0F0',
borderBottomWidth: 1,
},
});
I'm using this component here
import * as React from 'react';
import { View, StyleSheet, StatusBar } from 'react-native';
import { createDrawerNavigator } from '#react-navigation/drawer';
import HeaderCategorie from '../../components/categories/index/HeaderCategorie';
import SearchBar from '../../components/home/index/SearchBar';
import DrawerContent from '../../components/categories/index/DrawerContent';
const Drawer = createDrawerNavigator();
function CategoriesScreen({ navigation }) {
return (
<View style={styles.container}>
<HeaderCategorie navigation={navigation}/>
<View style={styles.headerSearch}>
<SearchBar />
</View>
</View>
)
}
export default function Categories() {
return (
<Drawer.Navigator initialRouteName="Categories"
drawerContent={DrawerContent}
screenOptions={{headerShown:false}}
>
<Drawer.Screen name="Categories" component={CategoriesScreen} />
</Drawer.Navigator>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "flex-start",
alignItems: "center",
marginTop: StatusBar.currentHeight,
},
headerSearch: {
marginVertical:10
},
headerSearchText: {
fontWeight:"bold",
fontSize:35,
marginLeft:20,
marginVertical:15,
}
});
Reason: By using drawerContent={DrawerContent}, you are actually passing the reference of the DrawerContent function, which ends up breaking rules of hooks.
So to resolve this, change the following line:
<Drawer.Navigator initialRouteName="Categories"
drawerContent={DrawerContent}
screenOptions={{headerShown:false}}
>
to this
<Drawer.Navigator initialRouteName="Categories"
drawerContent={(props)=> <DrawerContent {...props}/>} // here
screenOptions={{headerShown:false}}
>
demo snack

use native base button with props

i want to create a reusable button component but i am having some issues. I get back undefined is not an onject evaluting '_nativebase.stylrsheetcreate'. I tried destructing the onPress and title but no luck. Can someone give a clear explanation on how to resolve this? thanks
import React from 'react';
import { View, Text, Button, StyleSheet } from 'native-base';
export const StyledButton = props => {
return (
<View style={styles.button}>
<Button
block
full
bordered
light
onPress={this.props.onPress}
>
<Text
style={{
color: '#FFFFFF',
}}
>
{this.props.title}
</Text>
{this.props.children}
</Button>
</View>
);
};
const styles = StyleSheet.create({
button: {
flex: 1,
padding: 10,
}
});
to Render
<StyledButton
title='Cancel'
onPress={this.somefunction}
/>
Remove this use props.someprop
import React from 'react';
import { StyleSheet } from 'react-native';
import { View, Text, Button } from 'native-base';
export const StyledButton = props => {
return (
<View style={styles.button}>
<Button block full bordered light onPress={props.onPress}>
<Text
style={{
color: '#FFFFFF',
}}>
{props.title}
</Text>
{props.children}
</Button>
</View>
);
};
const styles = StyleSheet.create({
button: {
flex: 1,
padding: 10,
}
});

undefined is not a function (evaluating 'fetch('apiurl')')

I am using below versions
react-native-router-flux ^3.39.1
react-native 0.44.0
I expecting that will call API which I am using with "fetch"
Have used componentDidMount but it's showing another error
undefined is not an object (evaluating 'this._component.getScrollableNode')
But I am getting below error outputs
Steps to reproduce
Create three scene using router flux (In my case App, Login, Home)
Use ScrollView for creating the Login.js Create a button
using TouchableHighlight after that call the fetch with a function
using onPress like onPress={ () => this.fetchData() }
Below code, I am using for App.js
import React, { Component } from 'react';
import {
View,
Text,
StyleSheet,
AsyncStorage,
} from 'react-native';
import Login from './components/Login'
import Register from './components/Register'
import Home from './components/Home'
import { Scene, Router, TabBar, Modal, Schema, Actions, Reducer, ActionConst } from 'react-native-router-flux'
const reducerCreate = params=>{
const defaultReducer = Reducer(params);
return (state, action)=>{
console.log("ACTION:", action);
return defaultReducer(state, action);
}
};
export default class App extends Component {
constructor(props, context) {
super(props, context);
this.state = {
logged: false,
loading: true,
};
};
componentWillMount(){
self = this;
AsyncStorage.getItem('token')
.then( (value) =>{
if (value != null){
this.setState({
logged: true,
loading: false,
});
}
else {
this.setState({
loading: false,
})
}
});
};
render() {
if (this.state.loading) {
return <View><Text>Loading</Text></View>;
}
return (
<Router>
<Scene hideNavBar={true} key="root">
<Scene key="logIn" component={Login} title="Login" initial={!this.state.logged}/>
<Scene key="regisTer" component={Register} title="Register"/>
<Scene key="home" component={Home} title="home" initial={this.state.logged}/>
</Scene>
</Router>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
And below code, using for Login.js
/* #flow */
import React, { Component } from 'react';
import {
View,
StyleSheet,
Image,
ScrollView,
TextInput,
Text,
TouchableHighlight,
Alert,
} from 'react-native';
import { Container, Content, InputGroup, Input, Icon, Item } from 'native-base';
import Button from 'react-native-button'
import {Actions} from 'react-native-router-flux'
import ResponsiveImage from 'react-native-responsive-image'
export default class Login extends Component {
constructor(props){
super(props)
this.state = {
email: '',
password: '',
data: '',
}
}
fetchData() {
fetch('http://allstariq.tbltechnerds.com/api/login/?username=andress&password=23434')
.then((response) => response.json())
.then((responseData) => {
this.setState({
data: responseData.movies,
});
})
.done();
}
render() {
return (
<View style={styles.container}>
<ScrollView>
<View style={ styles.logoContainer }>
<View style={{flexDirection: 'row',}}>
<ResponsiveImage
source={require('../assets/logo.png')}
initWidth="300"
initHeight="160" />
</View>
</View>
<View style={ styles.formContainer }>
<Item>
<Icon active name='mail' />
<Input
onChangeText={(text) => this.setState({email: text})}
value={this.state.email}
placeholder='Email'/>
</Item>
<Item>
<Icon active name='key' />
<Input
onChangeText={(text) => this.setState({password: text})}
value={this.state.password}
placeholder='Password'/>
</Item>
<TouchableHighlight
style={ styles.loginButton }
onPress={ () => this.fetchData() }>
<Text style={ styles.btnText}>Login</Text>
</TouchableHighlight>
</View>
<View style={ styles.bottomContainer }>
<Text style={ styles.cenText }>Dont worry if you haven't an account yet . . </Text>
<Text
style={ styles.blueText}
onPress={ Actions.regisTer }
>Register Now</Text>
</View>
</ScrollView>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
logoContainer: {
flex: .5,
padding: 10,
justifyContent: 'center',
alignItems: 'center',
},
logoItem: {
width: null,
height: null,
resizeMode: 'cover',
},
formContainer: {
flex: 4,
padding: 10,
},
inputelm: {
marginBottom: 10,
backgroundColor: '#999',
borderWidth: 0,
fontSize: 20,
color: '#FFF',
fontFamily: 'AmaticSC-Bold',
},
loginButton: {
borderRadius: 3,
marginBottom: 20,
marginTop: 20,
paddingLeft: 10,
paddingRight: 10,
backgroundColor: '#2196f3',
elevation: 4,
},
signupButton: {
borderRadius: 3,
marginBottom: 20,
marginTop: 20,
paddingLeft: 10,
paddingRight: 10,
backgroundColor: '#7cb342',
elevation: 4,
},
btnText: {
textAlign: 'center',
color: '#FFF',
fontSize: 30,
lineHeight: 40,
},
blueText: {
textAlign: 'center',
color: '#2196f3',
fontSize: 20,
lineHeight: 40,
},
bottomContainer: {
flex: 1,
padding: 10,
},
cenText: {
textAlign: 'center',
fontSize: 16,
},
});
What is the actual way to use fetch with react-native-router-flux? I am new to react, please help me.
well, its a couple of months late but the problem here is that your fetchData method hasn't got access to this because when you declare a method like this:
fetchData() {
}
Under the hood react is creating a function this way:
this.fetchData = function() {
}
when you declare a function using the function keyword, everything between {} will have its own "this" and your method will not have access to the "this" of the component context.
That is the reason why you are getting the "undefined", because you are calling "this.setState" inside the promise returned by fetch, so the error is not fetch, but this.
To solve this issue you could just define your method this way:
fetchData = () => {
}
And because the functions defined with a fat arrow do not create their own scope, the component "this" will be available from inside your method.
Did you try maybe to import the library?
Import fetch from "fetch";
You have not imported the fetch function, import it from the node-fetch module like
import fetch from 'node-fetch'

How to add stateless component to a touchable component

I am trying to add a stateless component to my button.
const button = ({onButtonPress, buttonText}) => {
return (
<TouchableHighlight
onPress={() => onButtonPress()}>
<ButtonContent text={buttonText}/>
</TouchableHighlight>
)
};
and get this error:
Warning: Stateless function components cannot be given refs (See ref "childRef"
in StatelessComponent created by TouchableHighlight).
Attempts to access this ref will fail.
I have read up on the issue but I am still new to javascript and RN and have not found a solution. Any help would be appreciated.
full code:
GlossaryButtonContent:
import React from 'react';
import {
View,
Text,
Image,
StyleSheet
} from 'react-native';
import Colours from '../constants/Colours';
import {
arrowForwardDark,
starDarkFill
} from '../assets/icons';
type Props = {
text: string,
showFavButton?: boolean
}
export default ({text, showFavButton} : Props) => {
return (
<View style={styles.container}>
{showFavButton &&
<Image
style={styles.star}
source={starDarkFill}/>}
<Text style={[styles.text, showFavButton && styles.favButton]}>
{showFavButton ? 'Favourites' : text}
</Text>
<Image
style={styles.image}
source={arrowForwardDark}
opacity={showFavButton ? .5 : 1}/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
alignItems: 'center'
},
favButton: {
marginLeft: 10,
color: Colours.darkTextHalf
},
text: {
flex: 1,
paddingTop: 5,
marginLeft: 20,
fontFamily: 'Bariol-Bold',
fontSize: 24,
color: Colours.darkText
},
image: {
marginRight: 20
},
star: {
marginLeft: 10
}
});
GlossaryButton:
import React from 'react';
import {
TouchableHighlight,
StyleSheet
} from 'react-native';
import Colours from '../constants/Colours';
import ShadowedBox from './ShadowedBox';
import GlossaryButtonContent from './GlossaryButtonContent';
type Props = {
buttonText: string,
onButtonPress: Function,
rowID: number,
sectionID?: string,
showFavButton?: boolean
}
export default ({buttonText, onButtonPress, rowID, sectionID, showFavButton} : Props) => {
return (
<ShadowedBox
style={styles.container}
backColor={showFavButton && Colours.yellow}>
<TouchableHighlight
style={styles.button}
underlayColor={Colours.green}
onPress={() => onButtonPress(rowID, sectionID)}>
<GlossaryButtonContent
text={buttonText}
showFavButton={showFavButton}/>
</TouchableHighlight>
</ShadowedBox>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
height: 60,
marginBottom: 10,
borderRadius: 5
},
button: {
flex: 1,
borderRadius: 5
}
});
Basically, Stateless components cannot have refs.
So, having a stateless component in the middle of the render tree will create a void in the ref chain, meaning you cannot access lower-down components.
So, the problem comes from trying to do this:
let Stateless = (props) => (
<div />
);
let Wrapper = React.createClass({
render() {
return <Stateless ref="stateeee" />
}
});
TouchableHighlight needs to give a ref to its child. And this triggers that warning.
Answer:
You can't actually make a stateless component a child of TouchableHighlight
Solution:
Use createClass or class to create the child of TouchableHighlight, that is GlossaryButtonContent.
See this github issue for more info and this one

Resources