Getting data from a class component to Functional component - React Native - javascript

I'm pretty new to React Native and don't have the most experience with javascript so although this may be very simple I have no idea what to do. I have a class Component which acts like a popup in my app. It pops up on press of a touchable opacity and displays a little menu. I am trying to get the id of the button that is pressed on that menu and send it back to the functional component main screen (where the touchable opacity to open the menu is). I can get the id of the button but I'm struggling to get this data out of the popup and back into my main screen(functional component)
Here is my Code for the Main Screen:
import { StyleSheet, Text, View, Image,TouchableOpacity} from 'react-native'
import React,{useState} from 'react'
import tw, { create } from 'twrnc';
import Map from "../components/Map"
import { SafeAreaView } from 'react-native-safe-area-context';
import { BottomPopup } from '../components/popup';
import { selectTrucks} from '../slices/navSlice';
import { useSelector } from 'react-redux';
const Logistics = () => {
const Trucks = useSelector(selectTrucks);
let popupRef = React.createRef()
const onShowPopup = () => {
popupRef.show()
}
const onClosePopup = () => {
popupRef.close()
}
return (
<View style={{flex:1,resizeMode:'contain'}}>
<View style={tw`h-12/16`}>
<Map/>
</View>
<TouchableOpacity onPress={onShowPopup} style={{top:'-68%'}}>
<Image
source = {{uri: "https://icon-library.com/images/app-menu-icon/app-menu-icon-21.jpg" }}
style = {{width:'20%', height:40,resizeMode:'contain'}}
/>
<Text style={{color:'black',paddingLeft:2,fontSize: 26,fontWeight:"bold",top:-37,left:70}}>Truck {}</Text>
</TouchableOpacity>
<BottomPopup
title = "Select Option"
ref = {(target)=> popupRef = target}
onTouchOutside={onClosePopup}
data={Trucks.TrucksInfo}
/>
<SafeAreaView style={{top:'-13%'}}>
<Text style={{color:'darkgreen',paddingLeft:2,fontSize: 36,fontWeight:"bold"}}>123 Elmo Street</Text>
<Text style={{color:'darkgreen',paddingLeft:2,fontSize: 24,fontWeight:"bold"}}>L4L 6L8, Mississauga, Ontario</Text>
<Text style={{color:'darkgreen',paddingLeft:2,fontSize: 18,fontWeight:"bold"}}>Phone Number: 416-749-6857{'\n'}Client: Bobby Jeff{'\n'}Order Number: 7187181{'\n'}Packing Slip Number: 882929</Text>
<Text style={{color:'black',fontSize: 18,paddingLeft:2}}>Customer Notified: Yes at 2:32 PM</Text>
</SafeAreaView>
</View>
)
}
export default Logistics
const styles = StyleSheet.create({})
And Here is the code for the class component popup:
import { Modal,Dimensions,TouchableWithoutFeedback,StyleSheet,View,Text,FlatList,TouchableOpacity} from "react-native";
import React from "react";
import { useNavigation } from '#react-navigation/native';
import Logistics from "../screens/Logistics";
const deviceHeight = Dimensions.get('window').height
export class BottomPopup extends React.Component {
constructor(props){
super(props)
this.state = {
show:false
}
}
show = () => {
this.setState({show:true})
}
close = () => {
this.setState({show:false})
}
renderOutsideTouchable(onTouch) {
const view = <View style = {{flex:1,width:'100%'}}/>
if (!onTouch) return view
return (
<TouchableWithoutFeedback onPress={onTouch} style={{flex:1,width:'100%'}}>
{view}
</TouchableWithoutFeedback>
)
}
renderTitle = () => {
const {title} = this.props
return (
<View style={{alignItems:'center'}}>
<Text style={{
color:'#182E44',
fontSize:25,
fontWeight:'500',
marginTop:15,
marginBottom:30,
}}>
{title}
</Text>
</View>
)
}
renderContent = () => {
const {data} = this.props
return (
<View>
<FlatList
style = {{marginBottom:130}}
showsVerticalScrollIndicator = {false}
data={data}
renderItem={this.renderItem}
extraData={data}
keyExtractor={(item,index)=>index.toString()}
ItemSeparatorComponent={this.renderSeparator}
contentContainerStyle={{
paddingBottom:40
}}
/>
</View>
)
}
renderItem = ({item}) => {
return(
<TouchableOpacity
onPress={() => {this.close(),console.log(item.id)}}
>
<View style = {{height:50,flex:1,alignItems:'flex-start',justifyContent:'center',marginLeft:20}}>
<Text style={{fontSize:18,fontWeight:'normal',color:"#182E44"}}>{item.name}</Text>
</View>
</TouchableOpacity>
)
}
renderSeparator = () => {
return(
<View
style={{
opacity:0.1,
backgroundColor:'#182E44',
height:1
}}
/>
)
}
render() {
let {show} = this.state
const {onTouchOutside,title} = this.props
return (
<Modal
animationType={"fade"}
transparent={true}
visible={show}
onRequestClose={this.close}
>
<View style={{flex:1, backgroundColor:"#000000AA",justifyContent:'flex-end'}}>
{this.renderOutsideTouchable(onTouchOutside)}
<View style={{
backgroundColor:'#FFFFFF',
width:'100%',
paddingHorizontal:10,
maxHeight:deviceHeight*0.4}}
>
{this.renderTitle()}
{this.renderContent()}
</View>
</View>
</Modal>
)
}
}
The console log in the render item function gets me the number i need i just need to get this number out and back into my logistics screen

What you can do is like onTouchOutside you can pass getId function as props which will return id.
I don`t have much experience with class component. So I have add sample code in functional component. You can refer this
const MainComponent = ()=>{
const getIdFunc = (id)=>{
//You will get id here
}
return(<View>
{/* pass function here */}
<PopUpComponent getIdFunc={getIdFunc}/>
</View>)
}
const PopUpComponent = (props)=>{
return(<TouchableOpacity
onPress={()=>{
//call function by passing id here
props.getIdFunc(id)
}}
>
</TouchableOpacity>)
}

Related

react native element listItem.accordion

//I am rendering an API response with the help of FLatlist but when I press the expand option it will open all the accordions.................
import { View, Text, StyleSheet, FlatList } from 'react-native'
import React, { useState } from 'react'
import SearchBox from '../../components/SearchBox/SearchBox'
import { ListItem, Icon, Slider } from '#rneui/themed'
import { useSelector } from 'react-redux'
import { getAllPackages } from '../../feature/packageSlice'
const Rounds = () => {
const [expanded, setExpanded] = useState(false)
const pack = useSelector(getAllPackages)
//flatlist render item
const renderItem = ({ item }) => {
return (
<ListItem.Accordion
content={
<>
<ListItem.Content>
<ListItem.Title style={styles.header}>
{item.name}
</ListItem.Title>
</ListItem.Content>
</>
}
isExpanded={expanded}
onPress={() => {
setExpanded(!expanded)
}}
>
<View style={styles.card}>
<Text style={styles.font}>Water Supply Pressure</Text>
</View>
</View>
</ListItem.Accordion>
)
}
//main render
return (
<View>
<FlatList
data={pack}
renderItem={renderItem}
keyExtractor={(item) => item.id}
/>
</View>
</View>
)
}
export default Rounds
I want to open up the selected accordion only how can I achieve that, please help thanks...................................................................................................................................
If i understand your code correctly and this is one component (not fraction of few) your problem is following:
const [expanded, setExpanded] = useState(false)
This state variable is on top of your parent Component, so each rendered item points to it.
Therefore if you change it from any of your ListItem.Accordion, it will affect all of them.
BUT
If you change your renderItem to render Component. like this:
const renderItem = ({ item }) => {
return (
<AccordionListItem item={item}/>
)
}
Then you can move this state inside AccordionListItem itself, so it will create unique instance for each unique instance of component.
//imports
import React from ...
const AccordionListItem = ({item}) => {
const [expanded, setExpanded] = useState(false) <========= !
return (
<ListItem.Accordion
content={
<>
<ListItem.Content>
<ListItem.Title style={styles.header}>
{item.name}
</ListItem.Title>
</ListItem.Content>
</>
}
isExpanded={expanded}
onPress={() => {
setExpanded(!expanded)
}}
>
<View style={styles.card}>
<Text style={styles.font}>Water Supply Pressure</Text>
</View>
</View>
</ListItem.Accordion>
)
}
export default AccordionListItem ;

Dynamically render component for each item in array React Native

Beginner question here, not sure exactly what this would be considered, but I'm trying to make a form where a user can add and remove input rows upon pressing a button. What do I need to change to render new components or remove components when the Add or Remove buttons are pressed? Right now, the Add and Remove button change the textInput array appropriately, but components are not actually being added or removed.
Here is my current code:
FormScreen.js
import React from 'react';
import { View, StyleSheet, ScrollView } from 'react-native';
import { Button, Caption } from 'react-native-paper';
import InputCard from '../components/InputCard';
const FormScreen = props => {
const textInput = [1,2,3];
const addTextInput = () => {
let currArr = textInput;
let lastNum = currArr[currArr.length -1]
let nextNum = lastNum + 1
console.log(currArr, lastNum, nextNum);
textInput.push(
nextNum
);
console.log(textInput);
};
const removeTextInput = () => {
textInput.pop();
console.log(textInput);
};
return (
<ScrollView>
<View style={styles.col}>
<View style={styles.row}>
<Caption>Favorite colors?</Caption>
</View>
<View style={styles.row}>
<View>
{textInput.map(key => {
return (
<InputCard key={key}/>
);
})}
</View>
</View>
<View>
<View style={styles.col}>
<Button title='Add' onPress={() => addTextInput()}>Add</Button>
</View>
<View style={styles.col}>
<Button title='Remove' onPress={() => removeTextInput()}>Remove</Button>
</View>
</View>
</View>
</ScrollView>
);
};
export default FormScreen;
InputCard.js
import React, { useState } from "react";
import { View, StyleSheet } from 'react-native';
import { Caption, Card, TextInput } from "react-native-paper";
const InputCard = (props) => {
const [input, setInput] = useState('');
return (
<View>
<Card>
<Card.Content>
<Caption>Item {props.key}</Caption>
<View style={styles.row}>
<View style={styles.half}>
<TextInput
label="Input"
value={input}
onChangeText={input => setInput(input)}
mode="outlined"
style={styles.textfield}
/>
</View>
</View>
</Card.Content>
</Card>
</View>
);
}
export default InputCard;
Instead of storing it in a array, try to do something like this, using 2 states.
const [totalTextInput, setTotalTextInput] = useState([])//initial state, set it to any data you want.
const [count, setCount] = useState(0);
const addTextInput = () => {
setCount((prevState) => prevState + 1);
setTotalTextInput((prevState) => {
const newTextInput = Array.from(prevState); // CREATING A NEW ARRAY OBJECT
newTextInput.push(count);
return newTextInput;
});
};
const removeTextInput = () => {
setTotalTextInput((prevState) => {
const newTextInput = Array.from(prevState); // CREATING A NEW ARRAY OBJECT
newTextInput.pop();
return newTextInput;
});
};
And in your code:
<View>
{totalTextInput.map(key => {
return (
<InputCard key={key}/>
);
})}
</View>

react-native : TypeError:undefined is not an object (evaluating 'this.state')

I'm the newbie of the react-native and study with the complete code. But I can't understand the diffrence between "const" before export and after render. for example:
const { height, width } = Dimensions.get("window");
export default class App extends React.Component {
state = {
newToDo: ""
};
render() {
const { newToDo } = this.state;
why I ask this is because my first init is not "export default class App extends React.Component {" but "export default function App() {". So I can't assign const or assign it and it cause the Error showing the message
TypeError:undefined is not an object (evaluating 'this.state')
this is my code :
import React from "react";
import {
StyleSheet,
Text,
View,
Dimensions,
TextInput,
Platform,
ScrollView
} from "react-native";
import ToDo from "./ToDo";
const { height, width } = Dimensions.get("window");
export default function App() {
const state = {
newToDo: ""
};
const { newToDO } = this.state;
return (
<View style={styles.container}>
<View style={styles.titleContainer}>
<Text style={styles.title}>Good or Bad</Text>
<Text style={styles.subTitle}>Check Daily your habit</Text>
</View>
<View style={styles.content}>
<Text style={styles.contentTitle}>To Do List</Text>
<TextInput
style={styles.input}
placeholder={"New to do"}
value={newToDO}
onChangeText={this._controllNewToDo}
returnKeyType={"done"}
autoCorrect={false}
/>
<ScrollView>
<ToDo />
</ScrollView>
</View>
</View>
);
_controllNewToDo = text => {
this.setState({
newToDO: text
});
};
}
You can't use this.setState({}) inside a functional components. So you should use a normal class component to be able to call this.setState or use Hooks to update state inside functionals components.
import React, {Component} from "react";
import {
StyleSheet,
Text,
View,
Dimensions,
TextInput,
Platform,
ScrollView
} from "react-native";
import ToDo from "./ToDo";
const { height, width } = Dimensions.get("window");
class App extends Component {
state = {
newToDo: ""
};
_controllNewToDo = text => {
this.setState({
newToDO: text
});
};
render(){
const { newToDO } = this.state;
return (
<View style={styles.container}>
<View style={styles.titleContainer}>
<Text style={styles.title}>Good or Bad</Text>
<Text style={styles.subTitle}>Check Daily your habit</Text>
</View>
<View style={styles.content}>
<Text style={styles.contentTitle}>To Do List</Text>
<TextInput
style={styles.input}
placeholder={"New to do"}
value={newToDO}
onChangeText={this._controllNewToDo}
returnKeyType={"done"}
autoCorrect={false}
/>
<ScrollView>
<ToDo />
</ScrollView>
</View>
</View>
);
}
}
export default App;
Try this code!
As i mentioned below, If you need to use state and setState({}) you should use inside class components. otherwise you should use Hooks, check this .I think this will help you to understand.
import React from 'react';
import {
StyleSheet,
Text,
View,
Dimensions,
TextInput,
Platform,
ScrollView,
} from 'react-native';
const { height, width } = Dimensions.get('window');
export default class App extends React.Component {
state = {
newToDo: 'wwe',
};
_controllNewToDo = text => {
this.setState({
newToDO: text,
});
};
render() {
const { newToDO } = this.state;
return (
<View>
<View>
<Text>Good or Bad</Text>
<Text>Check Daily your habit</Text>
</View>
<View>
<Text>To Do List</Text>
<TextInput
style={{ height: 60 }}
placeholder={'New to do'}
value={newToDO}
onChangeText={this._controllNewToDo}
returnKeyType={'done'}
autoCorrect={false}
/>
<ScrollView
style={{ justifyContent: 'center', alignItems: 'center' }}>
<Text>{newToDO}</Text>
</ScrollView>
</View>
</View>
);
}
}
Feel free for doubts
In functional component you have use useState hook to manage state.Try this,
import React, { useState } from "react";
import {
StyleSheet,
Text,
View,
Dimensions,
TextInput,
Platform,
ScrollView
} from "react-native";
import ToDo from "./ToDo";
const { height, width } = Dimensions.get("window");
export default function App() {
const [newToDo, setNewToDo] = useState("");
return (
<View style={styles.container}>
<View style={styles.titleContainer}>
<Text style={styles.title}>Good or Bad</Text>
<Text style={styles.subTitle}>Check Daily your habit</Text>
</View>
<View style={styles.content}>
<Text style={styles.contentTitle}>To Do List</Text>
<TextInput
style={styles.input}
placeholder={"New to do"}
value={newToDo}
onChangeText={_controllNewToDo}
returnKeyType={"done"}
autoCorrect={false}
/>
<ScrollView>
<ToDo />
</ScrollView>
</View>
</View>
);
const _controllNewToDo = text => {
setNewToDo(text);
};
}

Screen switching by press on button react native

I just started to learn React-native. In this app I have a two buttons in header, first 'Todo', second 'Tags'. I want to chang content by press on these buttons. I think I need to change state.for clarityWhat I mean, when i tap on the button Tags, below I get TagScreen component, exactly the same for the button Todo. How to connect these components so that they work correctly?
app.js
import React, { useState } from 'react'
import { StyleSheet, View, FlatList } from 'react-native'
import { Navbar } from './src/Navbar'
import { TagScreen } from './src/screens/TagScreen'
import { TodoScreen } from './src/screens/TodoScreen'
export default function App() {
const [todos, setTodos] = useState([])
const [tags, setTags] = useState([])
const [appId, setAppId] = useState([])
const addTodo = title => {
setTodos(prev => [
...prev,
{
id: Date.now().toString(),
title
}
])
}
const addTags = title => {
setTags(prev => [
...prev,
{
id: Date.now().toString(),
title
}
])
}
const removeTags = id => {
setTags(prev => prev.filter(tag => tag.id !== id))
}
const removeTodo = id => {
setTodos(prev => prev.filter(todo => todo.id !== id))
}
return (
<View>
<Navbar title='Todo App!' />
<View style={styles.container}>
<TagScreen addTags={addTags} tags={tags} removeTags={removeTags}/>
{/* <TodoScreen todos={todos} addTodo={addTodo} removeTodo={removeTodo} /> */}
{/* HERE MUST CHANGED COMPONENTS */}
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
paddingHorizontal: 30,
paddingVertical: 20
}
})
navbar.js
import React from 'react'
import { View, Text, StyleSheet, Button, TouchableOpacity } from 'react-native'
export const Navbar = ({ title }) => {
return (
<View style={styles.padding}>
<View style={styles.navbar}>
<TouchableOpacity
style={styles.button}
>
<Text>Todo</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
>
<Text>Tags</Text>
</TouchableOpacity>
<Text style={styles.text}>{title}</Text>
</View>
</View>
)
}
well you need to track the visiblity of what is visible in your state,
in your App component, do this;
const [showTodos, setShowTodos] = useState(false);
const makeTodosInvisible= () => setShowTodos(false);
const makeTodosVisible = () => setShowTodos(true);
return (
<View>
<Navbar onTodoPress={makeTodosVisible } onTagPress={makeTodosInvisible} title='Todo App!' />
<View style={styles.container}>
{showTodos
? <TodoScreen todos={todos} addTodo={addTodo} removeTodo={removeTodo} />
: <TagScreen addTags={addTags} tags={tags} removeTags={removeTags}/>
}
{/* <TodoScreen todos={todos} addTodo={addTodo} removeTodo={removeTodo} /> */}
{/* HERE MUST CHANGED COMPONENTS */}
</View>
</View>
)
and in your navbar.js do this
export const Navbar = ({ title, onTodoPress, onTagPress}) => {
return (
<View style={styles.padding}>
<View style={styles.navbar}>
<TouchableOpacity
style={styles.button}
onPreesed={onTodoPress} // will hide Tags and show Todos
>
<Text>Todo</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPreesed={onTagPress} // will show Tags and hide Todos
>
<Text>Tags</Text>
</TouchableOpacity>
<Text style={styles.text}>{title}</Text>
</View>
</View>
)
}

React native navigation with a Flatlist in a stateless component

I am trying to create stateless component to display a Flatlist and I am struggling with the navigation.
This is my 'main' app.js, stripped down:
import React from 'react';
import { AppRegistry, FlatList, ActivityIndicator, Text, View, Image, StyleSheet } from 'react-native';
import { StackNavigator } from 'react-navigation'
import Strip from '../components/Strip'
export default class FetchData extends React.Component {
constructor(props){
super(props);
this.state ={ isLoading: true}
}
componentDidMount(){
...
}
render(){
if(this.state.isLoading){
...
}
return (
<View>
<Strip props={this.state.dataSource.strips} />
</View>
)
}
}
And this is the component:
import React from 'react';
import { FlatList, Text, View, Image, StyleSheet, TouchableOpacity } from 'react-native';
renderItem = ({item}) => (
<TouchableOpacity onPress={() => this.props.navigation.navigate('Details')} >
<View style={{width: 136}}>
...
<Text>some text</Text>
</View>
</TouchableOpacity>
);
const Strip = (props) => {
return Object.keys(props).map(function(key, i) {
return Object.keys(props[key]).map((strips, i) => {
var strip = props[key][strips];
return(
<View>
<Text>{strip.title}</Text>
<FlatList horizontal
data={strip.someobjects}
renderItem={({item}) => this.renderItem(item)}
/>
</View>
)
});
})
}
export default Strip;
The list is displayed but, of course, when I touch an item I get the 'undefined is not an object (evaluating '_this.props.navigation')' error.
I know that I can't use this.props.navigation.navigate on a stateless component but I just don't understand how I can pass the navigation props via a flatlist in a stateless component.
It has to be something really simple like using navigate('Details') and put const { navigate } = this.props.navigation; somewhere. But where?
Instead of navigate from Strip component, provide an onPress handler to Strip and navigate from there. If FetchData is part of StackNavigator then you could easily navigate from this component.
Consider following example
...
export default class FetchData extends React.Component {
...
handleOnPress = () => {
this.props.navigation.navigate('Details')
}
render() {
if(this.state.isLoading){
...
}
return (
<View>
<Strip
props={this.state.dataSource.strips}
onPress={this.handleOnPress}
/>
</View>
);
}
}
In the Strip component, you can bind the onPress handler
const Strip = (props) => {
renderItem = ({item}) => (
<TouchableOpacity onPress={props.onPress} >
<View style={{width: 136}}>
...
<Text>some text</Text>
</View>
</TouchableOpacity>
);
return Object.keys(props).map(function(key, i) {
return Object.keys(props[key]).map((strips, i) => {
var strip = props[key][strips];
return(
<View>
<Text>{strip.title}</Text>
<FlatList horizontal
data={strip.someobjects}
renderItem={({item}) => renderItem(item)}
/>
</View>
)
});
})
}
Hope this will help!
Define a function for navigation in parent and pass it to child via props.
Eg:
Parent
parentNavigate(destination){
this.props.navigation.navigate(destination);
}
render(){
if(this.state.isLoading){
...
}
return (
<View>
<Strip props={this.state.dataSource.strips} parentNavigate={(destination) => this.parentNavigate(destination)}/>
</View>
)
}
Child
renderItem = (item, parentNavigate) => (
<TouchableOpacity onPress={parentNavigate('Details')} >
<View style={{width: 136}}>
...
<Text>some text</Text>
</View>
</TouchableOpacity>
);
const Strip = (props, parentNavigate) => {
return Object.keys(props).map(function(key, i) {
return Object.keys(props[key]).map((strips, i) => {
var strip = props[key][strips];
return(
<View>
<Text>{strip.title}</Text>
<FlatList horizontal
data={strip.someobjects}
renderItem={(item, parentNavigate) => this.renderItem(item, parentNavigate)}
/>
</View>
)
});
})
}

Categories

Resources