Navigating to other screen in combination with redux - reactjs

I have the following to Component:
RestaurantTemplate :
import React from 'react';
import { StatusBar } from 'expo-status-bar';
import { ImageBackground, StyleSheet, View,TouchableOpacity} from 'react-native';
import { RestaurantData } from '../../Data';
import { connect } from 'react-redux';
import Restaurant from './Restaurant';
class RestaurantOverviewTemplate extends React.Component{
render() {
return(
<View style={{ flex: 1, justifyContent: 'center' }}>
<Restaurant restaurants={RestaurantData} onPress={this.props.changeToRestaurantId}/>
</View>
)
}
}
const mapDispatchToProps = (dispatch) => {
return {
changeToRestaurantId: (restaurants) => dispatch({ type: 'UPDATE_ID', payload: restaurants })
}
}
export default connect(null, mapDispatchToProps)(RestaurantOverviewTemplate);
Restaurant.js :
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
export default class Restaurant extends React.Component {
renderRestaurant = (restaurants) =>{
return restaurants.map((item, index) => {
return(
<View key={ index }>
<Button onPress={ () => this.props.onPress(item.id) } title={ item.name }/>
</View>
)
});
}
render() {
return (
<View>
{this.renderRestaurant(this.props.restaurants)}
</View>
)
}
}
So I have the RestaurantTemplate in which the Restaurant.js component is imported. What was my intension is to extract the id of a restaurant and pass it to the mapDispatchToProps function that updates a global variable. But my problem is that I want to navigate to another screen when one restaurant is pressed. I thought I could do this like so:
<Restaurant
restaurants={RestaurantData}
onPress={[this.props.changeToRestaurantId, () => this.props.navigation.navigate('RestaurantDetails']}
/>
But this won't work. I also can't wrap a TouchableOpacity around the <Restaurant../>.
Has anybody an idea how I could fix this?
Thanks in advance!!

It looks as though you want to invoke more than one function in the callback. You can wrap both calls in an anonymous function. Proxy the passed restaurant id to changeToRestaurantId and invoke the navigation. This, of course, assumes you have correctly injected navigation as a prop into RestaurantOverviewTemplate component.
<Restaurant
restaurants={RestaurantData}
onPress={(id) => {
this.props.changeToRestaurantId(id);
this.props.navigation.navigate('RestaurantDetails');
}}
/>

Related

Undefined error while trying to map over array of objects React Native

I've been trying to map an array of items which is passed from my Home Component to another but I'm always getting the error restaurants.map is Undefined. I fetch the data from Yelp and store it in a State(Home Component), then I pass this to another component, retrieve it through props and try to map through it(RestaurantItems Component). Please help. I have attached the code of my two components..
Home Component
import { View, Text, StatusBar, ScrollView } from "react-native";
import React, { useEffect, useState } from "react";
import HeaderTabs from "../components/HeaderTabs";
import SearchBar from "../components/SearchBar";
import Categories from "../components/Categories";
import RestaurantItems from "../components/RestaurantItems";
const YELP_API_KEY =
"6NE-noDkyFUDKVVo2B8POXtwsAIBEe7QTnZEwMpvNE-5asFQ1q0_jh7iJ5KqprLD3sVt2htFrZbe4V2rHbdXgUcqkMSquQADGcGOTh_ANZ1DRT_tnFKZBHT4Hh0eYn";
export default function Home() {
const [restaurantData, setRestaurantData] = useState();
const getRestaurantFromYelp = async () => {
const response = await fetch(
`https://api.yelp.com/v3/businesses/search?term=restaurants&location=san+francisco`,
{
method: "GET",
headers: {
Authorization: `Bearer ${YELP_API_KEY}`,
},
}
);
const data = await response.json();
setRestaurantData(data);
};
useEffect(() => {
getRestaurantFromYelp();
}, []);
return (
<View style={{ backgroundColor: "#eee", flex: 1 }}>
<StatusBar barStyle="dark-content" backgroundColor="#eee" />
<View style={{ backgroundColor: "#fff", padding: 15 }}>
<HeaderTabs />
<SearchBar />
</View>
<ScrollView showsVerticalScrollIndicator={false}>
<Categories />
<RestaurantItems restaurants={restaurantData} />
</ScrollView>
</View>
);
}
RestaurantItems Component
import React from "react";
import { MaterialCommunityIcons } from "react-native-vector-icons";
export default function RestaurantItems({ restaurants }) {
console.log(restaurants);
return (
<View>
{restaurants.map((single_data) => (
<Text>{single_data.name}</Text>
))}
</View>
);
}
This has been asked so many times!
Your state is not initialized, so at first render, when your datas are not loaded yet, you have an error. So initiliaze your state with an empty array
const [restaurantData, setRestaurantData] = useState([]);

Navigation with parameters from custom element in Flatlist in React Native: Error: Invalid hook call

I am new to react native and have a problem figuring out how to navigate from one class to another one with passing parameters and would appreciate your help.
All I want to do is:
ClassA should have a checkbox with state handling and a flatlist containing CustomButton
Navigate from ClassA to TargetScreen by clicking CustomButton
Pass parameter "element" to TargetScreen
Show content of parameter passed in TargetScreen
The error message I get:
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 See https://reactjs.org/warnings/invalid-hook-call-warning.html for tips about how to debug and
fix this problem.
ClassA:
import React, { Component, useState } from 'react';
import { useNavigation } from '#react-navigation/native';
import { CustomButton} from './CustomButton.js';
import { CheckBox, SafeAreaView, FlatList} from 'react-native';
class ClassA extends React.Component {
render() {
const [ASelected, setA] = useState(false);
const NavigateWithParams = () => {
navigation = useNavigation();
this.props.navigation.navigate('TargetScreen', { element: 'elementname' })
}
const renderItemCustom= ({ item }) => (
<CustomButton onPress={() => navigateWithParams()} />
);
}
return (
<CustomConst/>
<CheckBox value={ASelected}
onValueChange={{setA}} />
<SafeAreaView>
<FlatList
data={data}
renderItem={renderItemCustom}
keyExtractor={(item) => item.element}
/>
</SafeAreaView>
);
}
export default ClassA;
TargetScreen:
class TargetScreen extends React.Component {
render() {
const { navigation } = this.props;
return (
<Text> {JSON.stringify(navigation.getParam('element'))} </Text>
);
}
}
export default TargetScreen;
+++++++++++++++++++++++++
Update:
As of now the code looks like this:
class ClassA extends React.Component {
NavigateWithParams = (element) => {
this.props.navigation.navigate('TargetScreen', { element: 'elementname' })
}
renderItemCustom = ({ item }) => (
<CustomButton element={item.title} onPress={() => this.NavigateWithParams(item.element)} />
);
render() {
return (
<SafeAreaView>
<FlatList
data={data}
renderItem={this.renderItemCustom}
keyExtractor={(item) => item.id}
/>
</SafeAreaView>
);
}
}
export default ClassA;
And I am now getting this issue:
TypeError: Cannot read property 'navigate' of undefined
+++++++++++++++++++++++++
Update2
Routing:
function ClassA({ navigation }) {
return (
<ClassAScreen/>
);
function Target({ navigation }) {
return (
<TargetScreen/>
);
//navigation stacks
const SessionStack = createStackNavigator();
function SessionStackScreen({ navigation }) {
return (
<SessionStack.Navigator>
<SessionStack.Screen
name="ClassA"
component={ClassA}
options={{ tabBarLabel: 'ClassA!', headerShown: false }}
/>
<SessionStack.Screen
name="Target"
component={Target}
options={{ tabBarLabel: 'works!' }}
/>
</SessionStack.Navigator>
)
}
Logging gives me this:
renderItemCustom = ({ item }) => (
<CustomButton element={item.title} onPress={() => console.log(this.props)} />
);
+++++++++++++++++
Update:
Solution can be found here:
Navigation with parameters from custom element in Flatlist in React Native: Empty parameters
You cant use hooks inside a class component so remove the line which has the hook
and change like below
const NavigateWithParams = element => {
this.props.navigation.navigate('TargetScreen', { element: element })
}
const renderItemCustom= ({ item }) => (
<CustomButton onPress={() => this.navigateWithParams(item.element)} />
);
And parameter are passed using the route prop
class TargetScreen extends React.Component {
render() {
const { route} = this.props;
return (
<Text> {JSON.stringify(route.params.element)} </Text>
);
}
}
Also for the checkbox instead of using the useState hook, use this.state.
You can’t use Hooks inside a class component
https://reactjs.org/docs/hooks-faq.html#should-i-use-hooks-classes-or-a-mix-of-both
UPDATE:
Work variant, you can try here: https://snack.expo.io/#vasylnahuliak/stackoverflow-67862370
import 'react-native-gesture-handler';
import React from 'react';
import { Text, View, StyleSheet, Button, FlatList } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
const DATA = [
{
id: 0,
title: 'first button',
element: 'something'
},
{
id: 1,
title: 'second button',
element: 'another something'
},
]
const HomeSceen = ({ route, navigation }) => {
return (
<View>
<Text>{JSON.stringify(route, null, 2)}</Text>
<Button
title="Navigate to ProfileScreen"
onPress={() => {
navigation.navigate('ProfileScreen');
}}
/>
</View>
);
};
const ProfileScreen = ({ route, navigation }) => {
const NavigateWithParams = (element) => {
navigation.navigate('HomeSceen', { element });
};
const renderItemCustom = ({ item }) => (
<Button title={item.title} onPress={() => NavigateWithParams(item.element)} />
);
return (
<View>
<Text>{JSON.stringify(route, null, 2)}</Text>
<FlatList
data={DATA}
renderItem={renderItemCustom}
keyExtractor={(item) => item.id}
/>
<Button
title="Navigate to HomeSceen"
color="tomato"
onPress={() => {
navigation.navigate('HomeSceen');
}}
/>
</View>
);
};
const SessionStack = createStackNavigator();
const App = () => {
return (
<NavigationContainer>
<SessionStack.Navigator>
<SessionStack.Screen name="HomeSceen" component={HomeSceen} />
<SessionStack.Screen name="ProfileScreen" component={ProfileScreen} />
</SessionStack.Navigator>
</NavigationContainer>
);
};
export default App;

How Can I Reduce Codes In React Native?

What I'm Trying To Do
My current code is like this.
import React from 'react';
import {
Container, Header, Body, View, Content, Title, Text, Left, Right
} from 'native-base';
import 'react-native-gesture-handler';
import Fire from 'app/src/Fire';
import {
StyleSheet, Image, TouchableOpacity,
} from 'react-native';
export default class All extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [],
};
}
async componentDidMount() {
const querySnapshot = await Fire.shared.getItems(1);
const items = await Fire.shared.pushItems(querySnapshot);
this.setState({ items });
}
render() {
const { items } = this.state;
return (
<Container>
<View>
{items.map((item) => (
<Image
source={{ uri: item.first_img_url }}
/>
<View>
<Text>{item.name}</Text>
</View>
))}
</View>
</Container>
);
}
}
I have another component that has almost same code as above one.
The differences are class name and
await Fire.shared.getItems(1);
or
await Fire.shared.getItems(2);
I know I should combine the same code into one component.
I would appreciate it if you could give me any advices or tips :)
You can extract this code and pass the number 1 or 2 in props.
import React from 'react';
import {
Container, Header, Body, View, Content, Title, Text, Left, Right
} from 'native-base';
import 'react-native-gesture-handler';
import Fire from 'app/src/Fire';
import {
StyleSheet, Image, TouchableOpacity,
} from 'react-native';
export default class All extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [],
};
}
async componentDidMount() {
const querySnapshot = await Fire.shared.getItems(this.props.nbrOfItems);
const items = await Fire.shared.pushItems(querySnapshot);
this.setState({ items });
}
render() {
const { items } = this.state;
return (
<Container>
<View>
{items.map((item) => (
<Image
source={{ uri: item.first_img_url }}
/>
<View>
<Text>{item.name}</Text>
</View>
))}
</View>
</Container>
);
}
}
You can call this component in any component like this
<All nbrOfItems={1} />
Or
<All nbrOfItems={2} />

Send value to function in React-Native

I am trying to send value of TextInput to another Class Function in console.log. My approach is when the button is pressed the value FromStr in TextInput will got passed into another class function. Here's my code
import React, { Component } from "react";
import { StyleSheet, Text, View, TextInput, Button } from "react-native";
import receiveMessage from "./receiveMessage"
export default class WeatherProject extends Component{
constructor (props) {
super(props);
this.state={
From:'',
FromStr:'',
}
}
changeText=(From)=>{
this.setState({From})
}
changeText1=(To)=>{
this.setState({To})
}
onPress = ()=>{
this.setState({FromStr: this.state.From})
receiveMessage.receiveMessage();
}
render(){
return (
<View>
<View style={styles.inputFields}>
<TextInput placeholder="From" id="from" style={styles.fromField} onChangeText={this.changeText} />
<View style={styles.buttonStyle}>
<Button
title={"Go Back"}
color="#f194ff"
onPress={this.onPress}
></Button>
</View>
</View>
</View>
);
}
}
receiveMessage.js
import React, { Component } from "react";
export default class receiveMessage extends Component {
static receiveMessage=()=>{
console.log(this.state.FromStr)
}
}
React does not allow to pass the data between react components in this way.
Following is way to pass the data between components in React. To get more insights please follow
import React, { Component } from 'react';
class WeatherProject extends Component {
render() {
const messageToPassed = 'Hello';
return (
<div>
<ReceiveMessage message={messageToPassed} />
</div>
);
}
}
const ReceiveMessage = props => <h1>{props.message}</h1>;
export default App;
here we pass the value from sidemenu component by raising an event
App.js
class App extends React.Component {
handleSubmit = (e, data) => console.log(`my data from props`, data);
render() {
return (
<Sidemenu
onSubmit={(e, data)=>this.handleSubmit(e, data)} />
);
}
}
SideMenu.js
const Sidemenu = props => {
const { onSubmit} = props;
return (
<button onClick={(e, type)=>this.onSubmit(e, 'mydata')} />
);
}

Element type is invalid: Expected a string/object got: undefined

I'm having a hard time figuring out why I'm getting Input returned undefined with this code. The Button, Card, CardSection components are being exported/imported the same way.
The rest of the components in the LoginForm render fine if I comment out the <Input/> tag.
Any help would be greatly appreciated!
Input.js -- Not working
import React from 'react';
import { Text, TextInput, View } from 'react-native';
const Input = ({ label, value, onChangeText }) => {
return (
<View>
<Text>{label}</Text>
<TextInput
value={value}
onChangeText={onChangeText}
style={{ height:20, width:100 }}
/>
</View>
);
};
export { Input };
Button.js -- Working
const Button = ({ whenPressed, children }) => {
const { buttonStyle, textStyle } = styles;
return (
<TouchableOpacity onPress={whenPressed} style={buttonStyle}>
<Text style={textStyle}>
{children}
</Text>
</TouchableOpacity>
);
};
export { Button };
LoginForm.js
import React, { Component } from 'react';
import { View } from 'react-native';
import { Button, Card, CardSection, Input } from './';
class LoginForm extends Component {
state = { text: '' };
render() {
return(
<Card>
<CardSection>
<Input
value={this.state.text}
onChangeText={text=> this.setState({ text })}
/>
</CardSection>
<CardSection/>
<CardSection>
<Button style={styles.buttonStyle}>LOGIN</Button>
</CardSection>
</Card>
);
}
}
It was returned as undefined because it was never exported in the index.js file that holds all the exports to App.
In the sample code you show the <Input/> component in Index.js but the import in LoginForm.js imports <Input/ from ./.

Resources