I have to dispatch logoutUser() action from CustomDrawerContentComponent. How can I do that?
I have a StackNavigator as well as Drawer Navigator in my app.
Also there is a CustomDrawerComponent to show the username of authenticated user as well as a sign up button in Drawer. Since it is outside the class, I'm unable to dispatch using props.
MainComponent.js
...other import statements
import {
fetchDishes,
fetchComments,
fetchPromos,
fetchLeaders,
logoutUser,
} from "../redux/ActionCreators";
const mapDispatchToProps = (dispatch) => ({
fetchDishes: () => dispatch(fetchDishes()),
fetchComments: () => dispatch(fetchComments()),
fetchPromos: () => dispatch(fetchPromos()),
fetchLeaders: () => dispatch(fetchLeaders()),
logoutUser: () => dispatch(logoutUser()),
});
const handleSignOut = () => {
//Here I have to dispatch logoutUser() but props.logoutUser() says undefined.
};
const CustomDrawerContentComponent = (props) => (
<ScrollView>
<SafeAreaView
style={styles.container}
forceInset={{ top: "always", horizontal: "never" }}
>
<View style={styles.drawerHeader}>
<View style={{ flex: 1 }}>
<Image
source={require("./images/newlogo.png")}
style={styles.drawerImage}
/>
</View>
<View style={{ flex: 2 }}>
<Text style={styles.drawerHeaderText}>Ristorante Con Fusion</Text>
</View>
</View>
<View style={styles.displayName}>
<Avatar
title={props.screenProps.name.match(/\b(\w)/g).join("")}
rounded
>
{" "}
</Avatar>
<Text style={{ fontSize: 22, marginLeft: 5, color: "#fff" }}>
Hello, {props.screenProps.name}
</Text>
</View>
<DrawerItems {...props} />
<TouchableOpacity onPress={() => handleSignOut()}> //Here I am calling the function
<View style={{ flexDirection: "row", justifyContent: "center" }}>
<Icon name="sign-out" type="font-awesome" size={24} color="blue" />
<Text>Sign Out</Text>
</View>
</TouchableOpacity>
</SafeAreaView>
</ScrollView>
);
const MainNavigator = createDrawerNavigator(
{
Home: {
screen: HomeNavigator,
navigationOptions: {
title: "Home",
drawerLabel: "Home",
drawerIcon: ({ tintColor, focused }) => (
<Icon name="home" type="font-awesome" size={24} color={tintColor} />
),
},
},
...
...
...
{
initialRouteName: "Home",
drawerBackgroundColor: "#D1C4E9",
contentComponent: CustomDrawerContentComponent,
}
);
class Main extends Component {
componentDidMount() {
this.props.fetchDishes();
this.props.fetchComments();
this.props.fetchPromos();
this.props.fetchLeaders();
};
render() {
var displayName = this.props.user.user.displayName;
if (displayName == undefined) displayName = this.props.user.name;
return (
<Fragment>
<View
style={{
flex: 1,
paddingTop:
Platform.OS === "ios" ? 0 : Expo.Constants.statusBarHeight,
}}
>
<MainNavigator screenProps={{ name: displayName }} />
</View>
</Fragment>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Main);
You can use the following to import useDispatch
import { useDispatch } from 'react-redux'
and then call it in your handleSignOut like so:
const handleSignOut = () => {
const dispatch = useDispatch()
dispatch(logoutUser())
//Continue with normal logout and maybe other dispatches...
};
Your component is not connected to redux. You are not using mapDispatchToProps anywhere else besides declaring it. Please use connect() method https://react-redux.js.org/api/connect
Related
Srry if the title makes no sense. Don't know a better title.
How can I save route.params items that I pass to my second screen using AsyncStorage?
In my first screen i have a bunch of data in a FlatList that can be opened with a Modal. Inside that Modal I have a TouchableOpacity that can send the data thats inside the Modal to my second screen. The data that has been passed to the second screen is passed to a FlatList. The data in the FlatList should be saved to AsyncStorage. Tried alot of things getting this to work, but only getting warning message
undefined. Code below is the most recent progress.
Using React Navigation V5.
FIRST SCREEN
const [masterDataSource, setMasterDataSource] = useState(DataBase);
const [details, setDetails] = useState('');
<TouchableOpacity
onPress={() => {
const updated = [...masterDataSource];
updated.find(
(item) => item.id === details.id,
).selected = true;
setMasterDataSource(updated);
navigation.navigate('cart', {
screen: 'cart',
params: {
items: updated.filter((item) => item.selected),
},
});
setModalVisible(false);
}}>
<Text>Add to cart</Text>
</TouchableOpacity>
SECOND SCREEN
import React, { useEffect, useState } from 'react';
import { View, Text, FlatList, TouchableOpacity } from 'react-native';
import { useTheme } from '../Data/ThemeContext';
import AsyncStorage from '#react-native-async-storage/async-storage';
import Ionicons from 'react-native-vector-icons/Ionicons';
export default function ShoppingList({ route, navigation }) {
const [shoppingList, setShoppingList] = useState([]);
const { colors } = useTheme();
const todo = () => {
alert('Todo');
};
useEffect(() => {
restoreShoppingListAsync();
}, []);
const shoppingListAsync = () => {
const shoppingList = route.params && route.params.items;
setShoppingList(list);
storeShoppingList(list);
};
const asyncStorageKey = '#ShoppingList';
const storeShoppingListAsync = (list) => {
const stringifiedList = JSON.stringify(list);
AsyncStorage.setItem(asyncStorageKey, stringifiedList).catch((err) => {
console.warn(err);
});
};
const restoreShoppingListAsync = () => {
AsyncStorage.getItem(asyncStorageKey)
.then((stringifiedList) => {
console.log(stringifiedList);
const parsedShoppingList = JSON.parse(stringifiedList);
if (!parsedShoppingList || typeof parsedShoppingList !== 'object')
return;
setShoppingList(parsedShoppingList);
})
.then((err) => {
console.warn(err);
});
};
const RenderItem = ({ item }) => {
return (
<View>
<TouchableOpacity
style={{
marginLeft: 20,
marginRight: 20,
elevation: 3,
backgroundColor: colors.card,
borderRadius: 10,
}}>
<View style={{ margin: 10 }}>
<Text style={{ color: colors.text, fontWeight: '700' }}>
{item.name}
</Text>
<Text style={{ color: colors.text }}>{item.gluten}</Text>
<Text style={{ color: colors.text }}>{item.id}</Text>
</View>
</TouchableOpacity>
</View>
);
};
const emptyComponent = () => {
return (
<View style={{ alignItems: 'center' }}>
<Text style={{ color: colors.text }}>Listan är tom</Text>
</View>
);
};
const itemSeparatorComponent = () => {
return (
<View
style={{
margin: 3,
}}></View>
);
};
return (
<View
style={{
flex: 1,
}}>
<View
style={{
padding: 30,
backgroundColor: colors.Textinput,
elevation: 12,
}}>
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
<TouchableOpacity onPress={() => navigation.goBack()}>
<Ionicons name="arrow-back-outline" size={25} color="#fff" />
</TouchableOpacity>
<Text style={{ color: '#fff', fontSize: 20 }}>Inköpslista</Text>
<TouchableOpacity>
<Ionicons
name="trash-outline"
size={25}
color="#fff"
onPress={() => todo()}
/>
</TouchableOpacity>
</View>
</View>
<View style={{ flex: 1, marginTop: 30 }}>
<FlatList
data={shoppingList}
renderItem={RenderItem}
ListEmptyComponent={emptyComponent}
ItemSeparatorComponent={itemSeparatorComponent}
initialNumToRender={4}
maxToRenderPerBatch={5}
windowSize={10}
removeClippedSubviews={true}
updateCellsBatchingPeriod={100}
showsVerticalScrollIndicator={true}
contentContainerStyle={{ paddingBottom: 20 }}
/>
</View>
</View>
);
}
As you are using async storage to maintain the cart.
I would suggest an approach as below
Update the asyn storage when new items are added to or removed from the cart
Retrieve the items from the cart screen and show the items there
Before you navigate store the items like below
AsyncStorage.setItem(
'Items',
JSON.stringify(updated.filter((item) => item.selected))
).then(() => {
navigation.navigate('Cart', {
items: updated.filter((item) => item.selected),
});
});
The cart screen would be something like below
function Cart({ navigation, route }) {
const [data,setData]=useState([]);
React.useEffect(() => {
async function fetchMyAPI() {
const value = await AsyncStorage.getItem('Items');
setData(JSON.parse(value));
}
fetchMyAPI();
}, []);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button title="Go back" onPress={() => navigation.goBack()} />
<FlatList
data={data}
renderItem={RenderItem}
/>
</View>
);
}
Working Example
https://snack.expo.io/#guruparan/cartexample
I'm trying to have a custom header component in react native navigation 5, however I'm getting the following error:
TyperError: undefined is not an object (evaluating 'scene.route')
In my component:
export default CustomHeader2 = (props) =>
{
const {scene, previous, navigation } = props;
return (
<View style={{ width:'100%',height:50, flexDirection:'row', alignItems:'center', justifyContent:'space-between', backgroundColor:'white' }}>
<View style={{ height:50, flexDirection:'row', alignItems:'center' }}>
{previous ?
(<TouchableOpacity onPress={ ()=>{ navigation.goBack() } } style={{ width:50, height:'100%',flexDirection:'column',alignItems:'center', justifyContent:'center' }}>
<MaterialCommunityIcon name="arrow-left" style={{ color:'black', marginRight:0, fontSize:30 }}/>
</TouchableOpacity>)
:null
}
<Text style={{ fontSize:18, color:'rgb(68,68,68)',fontWeight:'bold' }}>{ scene.route.params.venue.name }</Text>
</View>
</View>
);
};
In my stack:
<Stack.Screen
name="VenueCard8"
component={VenueCard8}
headerShown={true}
options={({ route }) => ({ title: route.params.venue.name, header: () => (<CustomHeader2></CustomHeader2>) })}
/>
screen options
options={() => ({
header: (props) => (
<CustomHeader
{...props}
/>
)
})}
custom header:
const CustomHeader = (props) => {
console.log('props = ', props);
console.log('scene = ', props.scene);
return (
<View>
<Text>Hello</Text>
</View>
);
};
Try this way
export default CustomHeader2 = (props) => {
return (
.........
<Text style={.....}>{ props.route.params.venue.name }</Text>
......
);
};
I'm trying to get each button to activate and "switch-on" when pressed, and I've used some documentation to help me. However, now it is not switching on.
Code Adds Switches in a FlatList
The Data should look like this:
https://imgur.com/a/761PSjre
Also, feel free to judge my code to the darkest depths of hell. I'm trying to improve my code.
import React from 'react'
import {StyleSheet, View,Text, Switch, Button, Alert, ScrollView, FlatList, SafeAreaView} from 'react-native'
export default () => {
const DATA = [
{
index: 1,
title: 'Toggle Night Mode',
},
{
index: 2,
title: 'Remind me to take a break',
},
{
index: 3,
title: "Remind me when it's bedtime",
},
];
const [enabledSwitches, setEnabledSwitches] = React.useState(DATA.length);
const toggleSwitch = () => setEnabledSwitches(previousState => !previousState);
function Item({title, index}) {
return (
<View>
<Text style={styles.text}> {title} </Text>
<Switch
trackColor={{ false: "#767577", true: "#81b0ff" }}
thumbColor="#f5dd4b"
ios_backgroundColor="#3e3e3e"
value={enabledSwitches[index]}
onValueChange={() => toggleSwitch(switches => {
switches[index] = !switches[index];
return switches;
})}
/>
</View>
)
}
function Header(){
return(
<View style = {styles.header}>
<Text style={styles.headertext}>Settings</Text>
</View>
)
}
return (
<>
<View style = {styles.container}>
<FlatList
data = {DATA}
keyExtractor = {item => item.id}
renderItem = {({ item, index }) => <Item title={item.title} index={index} /> }
ListHeaderComponent = {Header()}
/>
</View>
<View>
<Button
title = "Clear Search History"
color = "#6fb6f0"
onPress = {() => Alert.alert('Food History Has Been Cleared!')}
/>
</View>
<View>
<Button
title = "Logout"
color = "#6fb6f0"
onPress = {() => Alert.alert('Successfully Logged Out!')}
/>
</View>
</>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: 20,
fontWeight: "300"
},
headertext: {
fontSize: 30,
fontWeight: "300"
},
header:{
flex:1,
justifyContent: 'center',
alignItems: 'center',
padding: 10,
backgroundColor: '#f5f5f5'
}
})
Try this way
import React from 'react'
import {StyleSheet, View,Text, Switch, Button, Alert, ScrollView, FlatList, SafeAreaView} from 'react-native'
export default () => {
// use data set in default state
const [data, setData] = React.useState([ {index: 1,title: 'Toggle Night Mode'},...]);
function toggleSwitch(value, index){
const newData = [...data];
const newData[index].isEnable = value;
setData(newData);
}
function Item({item, index}) {
return (
<View>
<Text style={styles.text}> {item.title} </Text> // use `title` here like this
<Switch
.....
value={item.isEnable || false} // change here
onValueChange={(value) => toggleSwitch(value, index) } // change here
/>
</View>
)
}
return (
<>
<View style = {styles.container}>
<FlatList
data = {data}
keyExtractor = {item => item.id}
renderItem = {({ item, index }) => <Item item={item} index={index} /> } // send `item` as prop
/>
</View>
</>
);
}
I want to be able to pass the current user from my Comment component to my CommentList component
class Comment extends Component {
render() {
return(
<View>
<Header
rounded
style={{
backgroundColor: '#ffffff',
position: 'relative',
}}
>
<View style={{flexDirection: 'row', flexWrap: 'wrap', right: '43%', top: '50%'}}>
<Icon name='chevron-left' size={10} color='#006FFF' style={{top: '6%'}}/>
<NativeText
onPress={() => this.props.history.push('/')}
style ={{color: '#006FFF', fontSize: 12, fontFamily: 'Montserrat-Regular'}}
>
Back
</NativeText>
</View>
</Header>
<View
style={{paddingLeft: '2%', paddingTop: '2%'}}
>
<CommentList
options={this.props.location.state.comments}
currentUser={this.props.location.state.currentUser}
/>
</View>
</View>
)
}
}
export default withRouter(Comment)
const CommentList = (options, currentUser) => {
const [modalVisible, setModalVisible] = useState(false)
const [parentId, changeParentId] = useState('')
const [commentInput, changeComment] = useState('')
return (
<View>{console.log(currentUser)}
{options.options.map(option => (
<View>
<NativeText
style={{fontSize: 12, fontFamily: 'Montserrat-Regular'}}
>
{option.content}
</NativeText>
<Icon
name='reply'
size={12}
onPress={() => {
setModalVisible(true)
changeParentId(option._id)
}}
/>
{
<View
style={{left: '10%'}}
>
<CommentList
options={option.reply}
/>
</View>
}
</View>
))}
</View>
)
}
When I try to pass it through currentUser, it returns an empty object; options does show up in CommentList.
You are missing the object destructuring syntax for CommentList. It should be as follows:
const CommentList = ({ options, currentUser }) => {
// component content
}
The props you pass to functional component are consolidated into an object. So you need to destructure the values.
Or,
const CommentList = (props) => {
//props.options //props.currentUser
}
I want to pass props to Drawer so that i can display the name of user in the drawer component. If i export DrawerContent, i dont get the props like navigation etc.
routes.js
const navitems =[
{
name:'Home',
nav:'classesnew',
},
{
name:'Device',
nav:'start',
},
]
const mapDispatchToProps = (dispatch) => (
{
loadInitialData: () => dispatch(loadInitialData()),
}
);
const mapStateToProps = createStructuredSelector({
user: selectUser(), // fetch the name of user to show below
});
class DrawerContent extends React.Component{
constructor(props) {
super(props)
}
render(){
return (
<Image source={require('../images/logo.png')}
style={styles.container}>
<View style={{justifyContent: 'center',
alignItems: 'center',}}>
<Image style={styles.image} source={{uri: ''}}/>
<Text>{user.get('name')}</Text> {/* show username */}
</View>
<View>
{
navitems.map((l,i)=>{
return (
<TouchableOpacity
key={i}
style={{marginBottom:0.5}}
onPress={()=>{
this.props.navigation.navigate(l.nav)
}
}>
<View style={{flexDirection:'row', padding: 15, paddingLeft:0, backgroundColor:'#fff0', borderTopWidth:0.5, borderColor:'rgba(255,255,255, 0.5)', marginLeft: 20, marginRight:20}}>
<Icon name={l.icon} size={20} style={{paddingLeft:10, paddingRight: 20, height: 25, }} color="#ffffff" />
<Text style={{fontSize:16, fontWeight: 'bold',color:'#fff'}}>{l.name}</Text>
</View>
</TouchableOpacity>)
})
}
</View>
</Image>)
}
}
const DrawerRoutes = (
{
Main: { screen: App, title: 'Main' },
Device: { screen: Device, title: 'Device' },
})
const Drawer = DrawerNavigator(DrawerRoutes ,{
contentComponent:({navigation})=> <DrawerContent navigation={navigation} routes={DrawerRoutes} />,
});
Drawer.navigationOptions = {
contentOptions: {
activeBackgroundColor: '#ff5976',
style: {
backgroundColor: '#000000',
zIndex: 100,
paddingTop: 0
}
},
header: ({ state, setParams, navigate, dispatch }) => ({
visible: true,
tintColor: '#ffffff',
title: "LokaLocal",
style: {
backgroundColor: '#ff5976'
},
right: (
<TouchableOpacity
onPress={() => navigate('DrawerOpen')}
>
<Icon name="search" size={16} style={{ padding: 10, paddingRight: 20 }} color="#ffffff" />
</TouchableOpacity>
),
left: (
<TouchableOpacity
onPress={}
>
<Icon name="bars" size={16} style={{ padding: 10, paddingLeft: 20 }} color="#ffffff" />
</TouchableOpacity>
),
})
}
export const Routes = StackNavigator(
{
Login: { screen: Login },
Dashboard: {screen: Drawer},
},
index.js
import { Drawer } from './routes';
const App = () => (
<Provider store={store}>
<Drawer />
</Provider>
);
where should i use connect so i can use redux state as props to show username?
UPDATED
const mapDispatchToProps = (dispatch) => (
{
loadInitialData: () => dispatch(loadInitialData()),
}
);
const mapStateToProps = createStructuredSelector({
user: selectUser(),
});
class DrawerContent extends React.Component{
constructor(props) {
super(props)
}
componentDidMount() {
console.log('props are', this.props);
this.props.loadInitialData();
}
render() {
console.log('props', this.props);
return (
<View style={styles.container}>
<View>
<Image style={styles.image} source={require('../images/logo.png')}/>
<Text style={{ color: '#fff', fontSize: 11, paddingTop: 10, paddingBottom: 20 }}>username here</Text>
</View>
<View>
{
navitems.map((l,i)=>{
return (
<TouchableOpacity
key={i}
style={{marginBottom:0.5}}
onPress={()=>{
this.props.navigation.navigate(l.nav)
}
}>
<View style={{flexDirection:'row', padding: 15, paddingLeft:0, borderTopWidth:0.5, borderColor:'rgba(255,255,255, 0.5)',}}>
<Icon name={l.icon} size={20} style={{paddingLeft:10, paddingRight: 20, height: 25, }} color="#ffffff" />
<Text style={{fontSize:16, fontWeight: 'bold',color:'#fff'}}>{l.name}</Text>
</View>
</TouchableOpacity>)
})
}
</View>
</View>
)
}
}
const DrawerRoutes = (
{
Main: { screen: App, title: 'Main' },
Device: { screen: Device, title: 'Device' },
})
export const Drawer = DrawerNavigator(DrawerRoutes ,{
contentComponent:({navigation})=> <ContentDrawer navigation={navigation} routes={DrawerRoutes} />,
});
const ContentDrawer = connect(mapStateToProps, mapDispatchToProps)(DrawerContent);