Calling a children method of a functional component from parent - reactjs

I am not using class, I'd like to learn how to do it manually. I am dealing with login screen.
https://snack.expo.io/#ericsia/call-function-from-child-component
If you want to show me your code, you need to save and share the link. So I wanted a functional component for display textbox (assume ChildComponent as the function name, so export ChildComponent).
So in Parent/Screen1 I have something like this right?
import * as React from 'react';
import { Text, View, StyleSheet, TouchableOpacity } from 'react-native';
import Constants from 'expo-constants';
// You can import from local files
import ChildComponent from './components/ChildComponent';
export default function App() {
function checkSuccess()
{
// call helloWorld from here
}
return (
<View style={styles.container}>
<ChildComponent />
<TouchableOpacity style={styles.button}
onPress={ checkSuccess } >
<Text>helloWorld ChildComponent</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
button: {
alignItems: "center",
backgroundColor: "#DDDDDD",
padding: 10
},
});
so if the result is invalid right, I wanted to display a tiny red error message.
something like this
My approach is if I can call a function from the ChildComponent then I may still solve it.
I googled it and most of the solution provided is for class.
I tried useEffect React.createRef useImperativeHandle but I didn't get it work.
for start, i am just trying to call the helloWorld()
import * as React from 'react';
import { TextInput , View, StyleSheet, Image } from 'react-native';
export default function ChildComponent() {
function helloWorld()
{
alert("Hello World");
}
return (<TextInput placeholder="Try to call helloWorld from App.js"/>);
}
Another question, if I have a textbox in my ChildComponent how do I retrieve the text/value from parent?

The Easy Way: Passing Props
You can move the helloWorld function up to the parent component and pass it down to the child as a prop. That way both components can call it. I recommend using an arrow function when you are going to be passing around a function, though it doesn't matter in this case.
Parent
export default function App() {
const helloWorld = () => {
alert('Hello World');
}
const checkSuccess = () => {
helloWorld();
}
return (
<View style={styles.container}>
<ChildComponent helloWorld={helloWorld} />
<TouchableOpacity style={styles.button} onPress={checkSuccess}>
<Text>helloWorld ChildComponent</Text>
</TouchableOpacity>
</View>
);
}
Child
const ChildComponent = ({ helloWorld }) => {
// could do anything with helloWorld here
return <TextInput placeholder="Try to call helloWorld from App.js" />;
};
The Hard Way: Ref Forwarding
If you want to keep the function in the child component then you need to go through a lot of hoops. I do not recommend this approach.
You have to do all of these steps:
Create a ref object in the parent using useRef: const childRef = React.useRef();
Pass the ref to the child as a prop: <ChildComponent ref={childRef} />
Call the function on the current value of the child component ref, using ?. to avoid errors if .current has not yet been set: childRef.current?.helloWorld();
Accept the ref prop in the child by using forwardRef: React.forwardRef( (props, ref) => {
Expose the helloWorld function as an instance variable of the child component by using useImperativeHandle: React.useImperativeHandle(ref , () => ({helloWorld}));
Parent:
export default function App() {
const childRef = React.useRef();
function checkSuccess() {
childRef.current?.helloWorld();
}
return (
<View style={styles.container}>
<ChildComponent ref={childRef} />
<TouchableOpacity style={styles.button} onPress={checkSuccess}>
<Text>helloWorld ChildComponent</Text>
</TouchableOpacity>
</View>
);
}
Child:
const ChildComponent = React.forwardRef((props, ref) => {
function helloWorld() {
alert('Hello World');
}
React.useImperativeHandle(ref, () => ({ helloWorld }));
return <TextInput placeholder="Try to call helloWorld from App.js" />;
});
Edit: Expo Link

Related

React Native: hooks can only be called inside of the body of a function component

code:
import React, { Component } from 'react'
import {
View,
Text,
TextInput
} from 'react-native'
export default class Home extends Component {
static navigationOptions = ({ navigation }) => {
return {
title: 'Home'
}
}
render () {
const [value, onChangeText] = React.useState('Useless Placeholder');
return (<View>
<Text>Home</Text>
<TextInput
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
onChangeText={text => onChangeText(text)}
value={value}
/>
</View>);
}
}
Learning React Native, encountered this error, how to correct the above code?
The official example of my imitation: https://reactnative.cn/docs/textinput
You are using class component and hooks can only be used in functional components.
Either use setState or convert the class to functional, like so
export default const Home = props => {
// your code
}
Moreover, it is not a good idea to set state inside the render, so you should take it outside.

React-Native the child component don't render the information within the parent component

I'm developing a React Native app that using a ScrollView. I want to display an amount of items (A card with title and a child component).
The problem comes when I have to render each item, while the parent renders ok, the child does not.
I don't know where could be the issue, here's my code:
import React, {Component} from 'react';
import {View, Text} from 'react-native';
const mismo =[
'Mismo',
'Mismo',
'Mismo',
'Mismo',
'Mismo'
];
class Mismo extends Component {
renderMismo2(){
mismo.map((item) =>{
return(
<View>
<Text>{item}</Text>
</View>
)
})
}
render(){
return(
<View>
{this.renderMismo2()}
</View>
);
}
}
export default Mismo;
=================================
import React, {Component} from 'react';
import {View, Text, ScrollView} from 'react-native';
import {Card} from 'react-native-elements';
import PriceCard from '../components/PriceCard';
import Mismo from '../components/Mismo';
class OrderPricingCard extends Component{
renderAllPrices(){
this.props.data.orders.map((item, i) => {
return(
<View>
<PriceCard
key={item.transporterName}
data={item}
/>
</View>
);
})
}
renderMismo(){
return(
<Mismo />
);
}
render () {
return (
<Card
containerStyle={styles.cardContainer}
title={`Pedido: ${this.props.data.id}`}
>
<ScrollView
horizontal
>
{this.renderMismo()}
{this.renderAllPrices()}
</ScrollView>
</Card>
);
}
}
const styles = {
cardContainer:{
borderRadius: 10,
shadowColor: "#000",
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 3.84,
elevation: 5,
}
}
export default OrderPricingCard;
This can be an easy mistake to make! I've done it several times. What's happened is you've forgotten the return statement for the render methods (renderMismo2() and renderAllPrices()) found in each component. Although the map methods correctly have return statements, you're not actually returning anything from the functions themselves.
If you were to console.log() either of those function calls above the return in the React render() method, you would see undefined in the console.
Here's what they would look like corrected.
renderAllPrices(){
// added the 'return' keyword to begin the line below
return this.props.data.orders.map((item, i) => {
return(
<View>
<PriceCard
key={item.transporterName}
data={item}
/>
</View>
);
})
}
renderMismo2(){
// same here, added the 'return' keyword
return mismo.map((item) =>{
return(
<View>
<Text>{item}</Text>
</View>
)
})
}
I tested the above in a React Native sandbox and it works. Hope that helps!

Proper use of onChangeText in React Native

I am fairly new to React Native and I have problem with using onChangeText.
I am trying to have a TextInput to type in a word and updating a state. When I use onChangeText I can only type 1 symbol at a time until it re-renders. I can keep the value by using
value = {this.state.text} but the input field still lose focus everytime I write a letter.
I have also tried using onBlur and onSubmitEditing with no success.
This is my current code. Which is inside render().
<View style={{padding: 10}}>
<TextInput
onChangeText={(text) => { this.setState({text: text})} }
/>
<TouchableHighlight style={styles.button} onPress={this.handlePress.bind(this)}>
<Text style={styles.buttonText}>Login</Text>
</TouchableHighlight>
<Text style={{padding: 10, fontSize: 42}}>
{this.state.text}
</Text>
</View>
So by using this method I can currently only write one letter at a time as this.state.text will only consist of one letter at a time.
Any help appreciated.
Example
SOLVED
I used react-native-tab-view which uses it's own router.
I wrote my code as this
And as you see the rendering part happens outside of return(). That's what caused my problem.
I've removed react-native-tab-view and rewritten it like this
<TextInput style={styles.input}
placeholder='username'
onChangeText={(text) => { this.setState({ username: text})}}>
</TextInput>
You need { } to open and close the function block, else it return the setState
() => callFn is equivalent with () => {return callFn} so you return your setState call.
You need here () => {callFn}
And remove the {this.state.text} from your <Text> tag, that will trigger rerender every time you change the state
Try with this full component hope so this helpfull for u.
'use strict';
import React, { Component } from "react";
import { Text, View, TextInput } from 'react-native';
class Home extends Component {
constructor(props) {
super(props);
this.state = {
text:''
};
}
render() {
let {text}=this.state;
return (
<View style={{padding: 10}}>
<TextInput onChangeText={(text) => { this.setState({ text: text})}}/>
<Text style={{padding: 10, fontSize: 42}}>
{text}
</Text>
</View>
)
}
}
export default Home;
It is not best practice to create functions within component props. This will always force a re-render even if nothing was changed due to the fact that the prop value is a new function.
Try it like this.
I also gave you a way to have multiple text inputs without creating a single inline function by use of "currying", along with making them into controlled inputs whereby their value is "controlled" by the state. Socialism in React!
'use strict';
import React, { Component } from "react";
import { Text, View, TextInput, StyleSheet } from 'react-native';
class Home extends Component {
constructor(props) {
super(props);
this.state = {
name:''
email:''
nameError:''
emailError:''
};
}
onChangeText = name => text => this.setState({ [name]: text });
render() {
let { name, email, nameError, emailError } = this.state;
return (
<View style={styles.container}>
<TextInput onChangeText={this._onChangeText("name")} value={name} />
<Text style={styles.text}>{nameError}</Text>
<TextInput onChangeText={this._onChangeText("email"} value={email} />
<Text style={styles.text}>{emailError}</Text>
</View>
)
}
}
const styles = StyleSheet.create({
text: {
padding: 10,
fontSize: 42
},
container: {
padding: 10
}
});
export default Home;

React Native What I'm doing wrong here with constant

I'm trying to implement one functional component. Here I am doing below, But I'm getting error about props. Could someone help me.
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
const TextField = () => {
return (
<Text> {this.props.title} </Text>
)
}
export default TextField;
// APP Component
import TextField from './Components/TextField'
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<TextField title= "Simple App"/>
</View>
);
}
}
The reason is this.props is not defined in a functional component. They receive the props as an argument.
Change your TextField to take argument props and use props.title
const TextField = (props) => {
return (
<Text> {props.title} </Text>
)
}

React Native : undefined is not an object

I am currently learning React Native.
I just a built a very simple app to test out the Button component.
When I click on the button component the console log is printed as expected.
But after printing out the console log it pops out the following error.
**undefined is not an object (evaluating '_this2.btnPress().bind')**
I am not sure what is wrong ?
Can anyone let me know what I am doing wrong ?
import React from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
export default class App extends React.Component {
btnPress() {
console.log("Fn Button pressed");
}
render() {
return (
<View style={styles.container}>
<Button title="this is a test"
onPress={()=> this.btnPress().bind(this)} />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
You are invoking the function instead of passing a reference through bind.
loose the ().
And you should not wrap it with an arrow function as bind is already returning a new function instance
onPress={this.btnPress.bind(this)} />
By the way, this will return and create a function instance on each render, you should do it once in the constructor (which runs only once):
export default class App extends React.Component {
constructor(props){
super(props);
this.btnPress = this.btnPress.bind(this);
}
btnPress() {
console.log("Fn Button pressed");
}
render() {
return (
<View style={styles.container}>
<Button title="this is a test"
onPress={this.btnPress} />
</View>
);
}
}
Or use an arrow function which uses a lexical context for this:
export default class App extends React.Component {
btnPress = () => {
console.log("Fn Button pressed");
}
render() {
return (
<View style={styles.container}>
<Button title="this is a test"
onPress={this.btnPress} />
</View>
);
}
}

Resources