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>
)
});
})
}
Related
//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 ;
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>)
}
how can I solve this problem, I'm facing an error that said
Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.
when I want to show my JSON file in my screen of List (CurrenciesList.js) the code is here
import React from 'react'
import { FlatList, View } from 'react-native'
import RowItem from '../../components/RowItem'
import currencies from '../../data/currencies.json'
export default () => {
<View>
<FlatList
data={currencies}
renderItem={({item}) => {
return <RowItem title={item}/>
}} />
</View>
}
RowItem is the reusable component and it's passing a text param only.
const RowItem = ({title, onPress}) => {
return (
<TouchableOpacity onPress={onPress}>
<Text style={styles.title}>{title}</Text>
</TouchableOpacity>
)
}
export default RowItem
and my file JSON (currencies.json) is like this
[
"AUD",
"BGN",
"BRL",
"CAD",
"CHF",
"CNY",
"CZK",
"etc.."
]
Your function does not return anything
change
export default () => {
<View>
</View>
}
to:
export default () => {
return <View>
...
</View>
}
or to
// no curly braces
export default () =>
<View>
...
</View>
You can try this,
import React from 'react'
import { FlatList, View } from 'react-native'
import RowItem from '../../components/RowItem'
import currencies from '../../data/currencies.json'
export default () => {
return (
<View>
<FlatList
data={currencies}
renderItem={({item}) => {
return <RowItem title={item}/>
}} />
</View>
);
}
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>
)
}
I am creating a chat application in React Native that receives different "message types" as a response. One of them contains an array of buttons that I am currently rendering in a Flatlist in Component "ChatQuickReply" that looks like this:
import React from "react";
import {
StyleSheet,
FlatList,
View,
TouchableOpacity,
Text
} from "react-native";
class ChatQuickReply extends React.Component {
constructor(props) {
super(props);
}
renderItem({ item }) {
return (
<TouchableOpacity onPress={this._onPressQuickReply}>
<View style={styles.quickButton}>
<Text style={styles.quickButtonText}>{item.title}</Text>
</View>
</TouchableOpacity>
);
}
_onPressQuickReply = () => {
alert(Hello);
};
render() {
return (
<View>
<FlatList
data={this.props.buttons}
keyExtractor={(item, index) => "key" + index}
renderItem={this.renderItem}
/>
</View>
);
}
}
I am rendering this component in a different component also in a Flatlist which works fine.
The problem is, that I am not able to call the function that I am assigning to my TouchableOpacity. How can I call this function from a different component?
I think, what you can try to trigger onPress event for your TouchableOpacity component.
You need ref for this.
ref={touchableOpacity => this._touchableOpacity = touchableOpacity}
then when you want to launch onPress without clicking it just call
this._touchableOpacity.touchableHandlePress();
Depending on the relationship between both components, if the ChatQuickReply reply is a parent component, you can pass the function in the child as props and call it.
import React from "react";
class ChatQuickReply extends React.Component {
renderItem({ item }) {
return (
<TouchableOpacity onPress={this._onPressQuickReply}>
<View style={styles.quickButton}>
<Text style={styles.quickButtonText}>{item.title}</Text>
</View>
</TouchableOpacity>
);
}
_onPressQuickReply = () => {
alert(Hello);
};
render() {
return (
<View>
<FlatList
data={this.props.buttons}
keyExtractor={(item, index) => "key" + index}
renderItem={this.renderItem}
/>
**<ChildComponent clickParentFunction={this._onPressQuickReply} />**
</View>
);
}
}
class ChildComponent extends React.Component {
onClick = () => {
const {clickParentFunction} = this.props
clickParentFunction() // We can call it here
}
// We create a trigger to the onclick
}
or you can take the _onPressQuicklyReply function to the component that renders both components and pass it in to make it more generic
import OtherComponent from './Othercomponent';
class ParentComponent {
_onPressQuickReply = () => {
alert(Hello);
}
return (
<View>
<ChatQuicklyReply onParentFunction={this._onPressQuickly}>
<OtherComponent onParentFunctionCall={this._onPressQuickly} />
</View>
)
}