Dispatching to and accessing state of store on Redux (React Native) - returning undefined - react-native

Trying to implement redux into one of my existing apps (for the first time). Essentially trying to do something quite simple, use a text input to store a string in the store and relay it back in another component using redux. However although the action dispatches, when I log it to the console, I keep getting 'undefined' for the entered string. Not sure where I'm going wrong and other questions aren't making it much clearer for my beginners mind!
I've defined singular reducers, combined them in an index, created the store (passing my combined reducer), wrapped navigation (entire app) in a provider with my store, created a dispatch and event/action to activate the dispatch (basically when the user enters a character and they press a continue button).
textInputPost.js: (Reducer)
const initialState = {
textInputValue: '',
};
const textInputReducer = (state = initialState, action) => {
console.log('textInputReducer', action);
switch(action.type){
case 'ADD_INPUT_TEXT':
return { textInputValue: action.text };
case 'RESET_INPUT_TEXT':
return { textInputValue: ''}
default:
return state;
}
}
export default textInputReducer;
index.js: (rootReducer - combine reducers)
import { combineReducers } from 'redux';
import photoInputPost from './photoInputPost'
import cameraInputPost from './cameraInputPost'
import textInputPost from './textInputPost'
export default rootReducer = combineReducers({
text: textInputPost
})
Index.js: (Store)
import { createStore } from 'redux'
import rootReducer from './../reducers/index'
export default Store = createStore(rootReducer)
App.js: (Provider wrapped around React Navigation)
return (
<Provider store={Store}>
<Navigation/>
</Provider>
);
AddTextModal.js: (Component for updating store state on textInput)
import React from 'react';
import { StyleSheet, Text, View, TouchableOpacity, KeyboardAvoidingView, TextInput } from 'react-native';
import { AntDesign } from '#expo/vector-icons';
import { createAppContainer, createSwitchNavigator } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import { createBottomTabNavigator } from 'react-navigation-tabs';
import { FontAwesome5, Feather } from "#expo/vector-icons";
import { connect } from 'react-redux'
import ModalContainer from '../../App'
class AddTextModal extends React.Component {
continueUpload = (textInput) => {
this.props.addText(textInput)
console.log(this.props.textInputValue)
this.props.navigation.navigate('Upload')
}
render() {
return(
<View style={{backgroundColor:"#000000CC", flex:1}}>
<View style={{ backgroundColor:"#ffffff", marginLeft: 0, marginRight: 0, marginTop: 180, padding: 20, borderTopLeftRadius: 20, borderTopRightRadius: 20, flex: 1, }}>
<View style={styles.header}>
<TouchableOpacity style={{position: 'absolute'}} onPress={() => this.props.navigation.navigate('Home')}>
<Text style={styles.buttonFont}>Back</Text>
</TouchableOpacity>
<Text style={styles.headerText}>Write Something</Text>
<TouchableOpacity style={{position: 'absolute', right: 0}} onPress={(textInput) => this.continueUpload(textInput)}>
<Text style={styles.buttonFont}>Continue</Text>
</TouchableOpacity>
</View>
<View style={styles.uploadTextInput}>
<Feather name="message-square" size={14} color="grey" />
<TextInput style={{paddingLeft: 5, fontSize: 14}}
placeholder="What do you want to say?"
defaultValue={this.props.textInputValue}
onChangeText={textInput => this.props.addText(textInput)}
/>
</View>
</View>
</View>
);
}
}
//#6 mapStateToProps to access store from our component
function mapStateToProps(state){
return {
textInputValue: state.textInputValue
}
}
//#10. matchDispatchertoProps to establish dispatcher for actions. These actions will then go to functions in the reducer to change the app state
function mapDispatchToProps(dispatch) {
return {
addText: () => dispatch({type: 'ADD_INPUT_TEXT'}),
}
}
UploadScreen.js: (Component for relaying store state of text Input of AddTextModal)
import React from 'react';
import { Text, Image, View, TextInput, TouchableHighlight, TouchableOpacity } from 'react-native';
import { FontAwesome5, Feather } from "#expo/vector-icons";
import { connect } from 'react-redux'
import ToolbarComponent from './ToolbarComponent'
import styles from './../Styles';
import textInput from './../../containers/textInput'
class UploadScreen extends React.Component {
uploadMedia = () => {
//upload to DB - add to vault, home screen
this.props.resetText()
this.props.navigation.navigate('Home')
}
//viewPhoto function for viewing photo on full screen
viewPhoto = () => {
return
}
render() {
return(
<View style={{backgroundColor:"#000000CC", flex:1}}>
<View style={{ backgroundColor:"#ffffff", marginLeft: 0, marginRight: 0, marginTop: 180, padding: 20, borderTopLeftRadius: 20, borderTopRightRadius: 20, flex: 1, }}>
<View style={styles.header}>
<TouchableOpacity style={{position: 'absolute'}} onPress={() => this.props.navigation.goBack()}>
<Text style={styles.buttonFont}>Back</Text>
</TouchableOpacity>
<TouchableOpacity style={{position: 'absolute', right: 10}} onPress={() => this.uploadMedia()}>
<Text style={styles.buttonFont}>Upload</Text>
</TouchableOpacity>
<View style={styles.uploadTextInput}>
<Text>{this.props.textInputValue}</Text>
</View>
</View>
</View>
</View>
);
}
}
function mapStateToProps(state){
return {
textInputValue: state.textInputValue
}
}
function mapDispatchToProps(dispatch) {
return {
resetText:() => dispatch({type: 'RESET_INPUT_TEXT'})
}
}
export default connect(mapStateToProps, mapDispatchToProps)(UploadScreen);

I believe your issue is that you haven't specified a payload in your mapDispatchToProps function. Try re-writing your mapDispatchToProps function to look something like:
function mapDispatchToProps(dispatch) {
return {
addText: (payload) => dispatch({type: 'ADD_INPUT_TEXT', payload: payload}),
}
}
And since I renamed your payload, 'payload', change 'action.text' to 'action.payload' in your reducer:
const initialState = {
textInputValue: '',
};
const textInputReducer = (state = initialState, action) => {
console.log('textInputReducer', action);
switch(action.type){
case 'ADD_INPUT_TEXT':
return { textInputValue: action.payload };
case 'RESET_INPUT_TEXT':
return { textInputValue: ''}
default:
return state;
}
}
export default textInputReducer;

Related

Redux - mapStateToProp don't get the params and reducer don't read the action

The mapStateToProps don't get the state from store. the console.log response is:
Object {
"selectStateClassify": Object {
"newClassify": "É pra vir algo",
},
}
and is given a message error that says Can't find variable: stateClassify, I know it's a property, but i don't know how to use this property like an object. I would like to use stateClassify in the tag <Text>{stateClassify}</Text>
import { StyleSheet, View, Text, TouchableOpacity } from 'react-native';
import { connect } from 'react-redux';
function Classificacao({ navigation }){
return(
<View style={{
flex: 1,
paddingTop: 30,
flexDirection: 'column',
alignItems: 'center',
}}
>
<Text style={{
textAlign: 'center',
paddingHorizontal: 60,
fontSize: 25,
}}
>Com base no Manual de Quadros do AIDPI, o paciente pode apresentar:</Text>
<Text style={{
textAlign: 'center',
paddingHorizontal: 60,
marginTop: 80,
fontSize: 25,
fontWeight: 'bold',
}}
>{stateClassify}</Text>
<TouchableOpacity
style={styles.goButton}
onPress={() => { navigation.navigate('Doencas'); }}
>
<Text style={styles.textButton}>Resultado</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({...});
const mapStateToProps = state => {
console.log(state);
return{
stateClassify: state.selectStateClassify.newClassify,
}
};
export default connect(mapStateToProps)(Classificacao);
Another issue that i found is on reducer, I don't know what kind of problem it is, but the reducer don't get the action params to change the state, the code is below:
import { SHOW_CLASSIFY } from '../actions/type';
const initialState = {
newClassify: 'É pra vir algo'
}
export const selectClassify = (state = initialState, action) => {
switch(action.type){
case SHOW_CLASSIFY:
return{
...state,
newClassify: action.newClassify
};
default:
return state;
}
}
action code:
import { SHOW_CLASSIFY } from './type';
export const showClassify = (classify) => ({
type: SHOW_CLASSIFY,
newClassify: classify
});
store code:
import { createStore } from 'redux';
import { Reducers } from '../reducers/index';
export const Store = createStore(Reducers);
App.js code:
import React from 'react';
import { StatusBar } from 'react-native';
import { Provider } from 'react-redux';
import Routes from './src/routes';
import { Store } from './src/redux/store/store';
export default function App() {
return (
<>
<Provider store={Store}>
<StatusBar barStyle="light-content" backgroundColor="#f89747" />
<Routes />
</Provider>
</>
);
}
Add stateClassify to your destructured props so you can use it like that, remember that you are mapping the props to your component but as you are destructuring props in function params you need to add it also.
function Classificacao({ navigation, stateClassify }){
...
}
Code to connect a component and reducer actions looks ok. Check for global store (createStore from redux is used.) file where we use the state and reducers to configure store and supply to Global Parent component to make the state available all over the App. Also check middleware configuration to confirm its not the reason for reducers failing.

Force unmounting on screen change

I recently integrated React Redux and Redux Thunk into my application in the hope that it would better allow me to manage state across screens.
However, using my navigation library (react native router flux), when ever I navigate between screens I get warnings of trying to set state across unmounted components and I am not sure what I would even need to unmount in componentWillUnmount as no calls should happen after a screen navigation.
My question then is, how can I force unmount everything on componentWillUnmount? Is there something built into React Native that I should use? Or, in my navigation library?
import React, { Component } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import * as Font from 'expo-font'
class CustomText extends Component {
async componentDidMount() {
await Font.loadAsync({
'varelaround-regular': require('../../../assets/fonts/varelaround-regular.ttf'),
'opensans-regular': require('../../../assets/fonts/opensans-regular.ttf'),
'opensans-bold': require('../../../assets/fonts/opensans-bold.ttf'),
});
this.setState({ fontLoaded: true });
}
state = {
fontLoaded: false,
};
setFontType = type => {
switch (type) {
case 'header':
return 'varelaround-regular';
case 'bold':
return 'opensans-bold';
default:
return 'opensans-regular';
}
};
render() {
const font = this.setFontType(this.props.type ? this.props.type : 'normal');
const style = [{ fontFamily: font }, this.props.style || {}];
const allProps = Object.assign({}, this.props, { style: style });
return (
<View>
{
this.state.fontLoaded ? (
<Text {...allProps}>{this.props.children}</Text>
) : <Text></Text>
}
</View>
);
}
}
export default CustomText;
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
}
});
And one of my screens:
import React from "react";
import { ActivityIndicator, Image, StyleSheet, View } from "react-native";
import { Actions } from "react-native-router-flux";
import { connect } from "react-redux";
import * as profile from "../actions/profile";
import {
CustomText
} from "../components/common/";
class Home extends React.Component {
componentDidMount() {
this.props.loadProfile();
}
renderScreen() {
return (
<View style={{ flex: 1 }}>
<View style={{ flex: 0.3 }}>
<CustomText type="header" style={styles.headerTextStyle} onPress={() => Actions.home()}>
Hello {this.props.name}!
</CustomText>
</View>
</View>
);
}
renderWaiting() {
return (
<GradientBackground type="purple">
<View
style={{ flex: 1, justifyContent: "center", alignItems: "center" }}
>
<ActivityIndicator size="large" color="#FFF" />
</View>
</GradientBackground>
);
}
render() {
return (
<View style={{ flex: 1 }}>
{this.props.isLoading == true
? this.renderWaiting()
: this.renderScreen()}
</View>
);
}
}
function mapStateToProps(state) {
return {
name: state.profile.profile.friendly_name,
isLoading: state.profile.isLoading,
error: state.profile.error
};
}
function mapDispatchToProps(dispatch) {
return {
loadProfile: () => dispatch(profile.loadProfile())
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Home);
const styles = StyleSheet.create({
headerTextStyle: {
color: "#FFFFFF",
fontSize: 40,
textAlign: "center",
marginVertical: 50
},
basicViewStyle: {
flex: 1
}
});

How to send the state of a Text Input variable to a reducer with dispatch and display it on another screen?

I want the state of my variable (with which it is given a value from a textInput) is sent to a reducer and change the state of that reducer by the state of the variable that I sent, so that way I can show it in different screens using mapStateToProps and I get it globally.
Is there any way to do that? I researched and found examples but not the way I want to do it.
I clarify my code is just an example so that you understand what I want to do, do not take it as a guide as I do not know if it works that way
I show you some of my code to give you an idea of ​​what I
import React, { Component } from "react";
import {
View,
Text,
StyleSheet,
TextInput,
TouchableOpacity
} from "react-native";
import { connect } from 'react-redux';
class ScreenHome extends Component {
static navigationOptions = {
header: null
}
constructor(props) {
super(props);
this.state = {
Texto: '',
}
}
render() {
this.props.ChangeState({type: 'ACTION_TYPE', Texto: this.state.Texto});
const { navigation } = this.props;
return (
<View style={styles.container}>
<TextInput
placeholder="Enter Text"
value={this.state.Texto}
onChangeText={Texto => this.setState({ Texto })}
/>
<View style={{ marginBottom: 10, marginTop: 10, backgroundColor: 'black', padding: 10 }}>
<TouchableOpacity onPress={this.props.ChangeState}>
<Text style={{ color: 'white' }}>Send Text Input status to the reducer</Text>
</TouchableOpacity>
</View>
<View>
<TouchableOpacity style={{ backgroundColor: 'blue', padding: 10 }} onPress={() => { navigation.navigate('Other') }}>
<Text style={{ color: '#fff' }}>Go</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
const mapStateToProps = (state) => {
return {
// prop: state.prop
}
}
const mapDispatchToProps = (dispatch) => {
return {
ChangeState: () => {
// dispatch({ type: 'ACTION_TYPE', Texto: this.state.Texto });
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ScreenHome)
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
}
});
OTHER SCREEN:
import React, { Component } from "react";
import {
View,
Text,
StyleSheet,
TouchableOpacity
} from "react-native";
import { connect } from 'react-redux';
class ScreenOther extends Component {
static navigationOptions = {
header: null
}
render() {
const { navigation } = this.props;
return (
<View style={styles.container}>
<Text>{this.props.StateInitial}</Text>
<TouchableOpacity onPress={() => { navigation.navigate('Home') }}>
<Text>Go back</Text>
</TouchableOpacity>
</View>
);
}
}
const mapStateToProps = (state) => {
return {
StateInitial: state.reducerText
}
}
const mapDispatchToProps = (dispatch) => {
return {
// ChangeState: () => {
// dispatch({type: 'CHANGE_TEXT'})
// }
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ScreenOther)
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
}
});
Store
import { createStore, combineReducers } from 'redux';
const reducerText = (state = [0], action) => {
switch (action.type) {
case 'ACTION_TYPE':
return {...state, Texto:action.Texto};
default:
return state;
}
};
const Reducers = combineReducers({
reducerText
})
const Store = createStore(Reducers)
export default Store;
dispatch1 should be visible in your props in the homescreen. So if you do
this.props.dispatch1({type: 'YOUR_ACTION_TYPE', Text: this.state.Text});
Your reducer function will be called where you can do:
reducer: (state, action) => {
switch (action.type) {
case 'YOUR_ACTION_TYPE':
return {...state, Text:action.Text};
}
}
Then in the other screen you should get the changed Text prop.
For those who look at this post and want to do something similar, I mean send the status of the textInput variable to a reducer and ask for the status from another screen with redux feel free to see the code that I will leave below since I was investigating and I got it after a while.
This is the code of redux-form
import React, { Component } from "react";
import {
View,
TextInput,
StyleSheet,
Button,
Text
} from "react-native";
import { Field, reduxForm } from 'redux-form';
import { connect } from 'react-redux';
const ScreenFormHome = (props) => (
<View>
<Field name="Text" component={fieldNombre} ph="Enter Text" />
<Button title="Send Dispatch" onPress={props.handleSubmit((values) => props.SendDispatch(values))} />
</View>
);
const fieldNombre = (props) => (
<View style={styles.textInput}>
<TextInput
placeholder={props.ph}
onChangeText={props.input.onChange}
value={props.input.value}
autoCapitalize="none"
onBlur={props.input.onBlur}
/>
<View style={styles.linea} />
</View>
);
const styles = StyleSheet.create({
textInput: {
marginBottom: 16,
},
linea: {
backgroundColor: '#DCDCDC',
height: 2,
},
});
const mapDispatchToProps = (dispatch) => {
return {
SendDispatch: (values) => {
dispatch({ type: 'ACTION_TYPE', Text: values.Text })
}
}
}
const mapStateToProps = (state) => {
return {
// StateInitial: state.reducerText
}
}
export default reduxForm({
form: 'ScreenFormHome',
})(connect(mapStateToProps, mapDispatchToProps)(ScreenFormHome));
and this is the component code
import React, { Component } from "react";
import {
View,
Text,
StyleSheet,
TouchableOpacity
} from "react-native";
import ScreenFormHome from "./ScreenFormHome";
class ScreenHome extends Component {
static navigationOptions = {
header: null
}
render() {
const { navigation } = this.props;
return (
<View style={styles.container}>
<TouchableOpacity style={{ backgroundColor: 'blue', padding: 10, marginBottom: 10 }} onPress={() => { navigation.navigate('Other') }}>
<Text style={{ color: '#fff' }}>Go</Text>
</TouchableOpacity>
<ScreenFormHome />
</View>
);
}
}
export default ScreenHome;
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
}
});
This is the Store code
import { createStore, combineReducers } from 'redux';
import { reducer as form } from 'redux-form'
const reducerText = (state = [], action) => {
switch (action.type) {
case 'ACTION_TYPE':
return (action.Text)
default:
return state;
}
};
const Reducers = combineReducers({
reducerText,
form
})
const Store = createStore(Reducers)
export default Store;

Invariant Violation: App(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null

This is my first project in React Native with Redux, I have a component(MyApp) which i am importing in index.js. but it is giving above error.
index.js -
import React from 'react';
import { AppRegistry } from 'react-native';
import MyApp from './component/MyApp';
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import rootReducer from './reducers';
import thunk from 'redux-thunk';
const store = createStore(rootReducer, applyMiddleware(thunk));
const App = () => {
<Provider store={store}>
<MyApp/>
</Provider>
}
AppRegistry.registerComponent('learningRedux', () => App);
MyApp.js
import React, { Component } from 'react';
import {
Text, View, TouchableHighlight
} from 'react-native';
import { connect } from 'react-redux';
import { fetchPeopleFromAPI } from '../actions/index';
export class MyApp extends Component {
render() {
const { people, isFetching } = props.people
return (
<View style={{
margin: 100,
paddingLeft: 20,
paddingRight: 20
}}
>
<Text style={{
fontSize: 30,
textAlign: 'center'
}}> Redux App </Text>
<TouchableHighlight style={{
backgroundColor: 'blue',
height: 60,
justifyContent: 'center',
alignItems: 'center'
}}
onPress={() => {
props.getPeople
}}>
<Text style={{
color: 'white'
}}> Fetch Data </Text>
</TouchableHighlight>
{
isFetching && <Text> Loading... </Text>
}
{
people.length ? (
people.map((person, index) => {
return (
<View key={index}>
<Text> Name : {person.name} </Text>
<Text> Birth Year : {person.birth_year} </Text>
</View>
)
}
)
) : null
}
</View>
);
}
}
mapStateToProps = (state) => {
return {
people: state.peopleReducer
}
}
mapDispatchToProps = (dispatch) => {
return {
getPeople: () => dispatch(fetchPeopleFromAPI())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(MyApp)
I tried another method by creating a const MyApp instead of class and used arrow function by passing props as an argument and then only used return(not render) but it is still not working.
Thanks.
const App = () => {
<Provider store={store}>
<MyApp/>
</Provider>
}
Arrow function above return null. If you're using curly brackets, you should add return keyword. Try add a return keyword first.
const App = () => {
return (
<Provider store={store}>
<MyApp/>
</Provider>
)
}
Compare this code below.
const add = (a, b) => a + b;
const anotherAdd = (a, b) => { return a + b; };
That code equal. The difference is that the first one doesn't need return keyword since it is not using curly brackets.

React Native Auth with React Navigation and Redux

I have just started integrating Redux into my first React Native (Expo.io) project. I have got login working great with Redux, but when I try and create a logout button on one of the screens in my app, it actually triggers the log out dispatch as soon as it loads it. I think I must be misunderstanding the way Redux connect and mapDispatchToProps work. I have read the documentation many times but am still stuck. Here is the code in it's non-working state.
Log In - Works until I add the log out dispatch on profile page
import { connect } from "react-redux";
import React, { Component } from "react";
import {
Button,
View,
Text,
ActivityIndicator,
Alert,
FlatList
} from "react-native";
import { NavigationActions } from "react-navigation";
import { SocialIcon, Card } from "react-native-elements";
import Reactotron from "reactotron-react-native";
import { logIn } from "../actions";
import { signIn } from "../components/Auth";
class SignIn extends Component {
async handleClick() {
res = await signIn();
if (res != false) {
this.props.logIn(res.token);
} else {
console.log("Login Failed");
}
}
render() {
return (
<View style={{ paddingVertical: 20 }}>
<Card title="finis Requires A Facebook Account To Operate">
<SocialIcon
title="Fred"
button
type="facebook"
onPress={() => this.handleClick()}
/>
</Card>
</View>
);
}
}
const mapDispatchToProps = dispatch => {
return {
logIn: fbToken => {
dispatch(logIn(fbToken));
}
};
};
LoginScreen = connect(null, mapDispatchToProps)(SignIn);
export default LoginScreen;
Reducers
import { combineReducers } from "redux";
import Reactotron from "reactotron-react-native";
import { LOG_IN, LOG_OUT, ADD_PHONE_CONTACTS } from "../actions/actions";
const initialState = {
signedIn: false,
fbToken: "fred",
test: undefined,
phoneContacts: {}
};
const finis = combineReducers({
auth,
phoneContacts
});
function auth(state = initialState, action) {
switch (action.type) {
case LOG_IN:
Reactotron.log("LOG IN");
return {
...state,
signedIn: true,
fbToken: action.fbToken
};
case LOG_OUT:
Reactotron.log("LOG OUT");
return {
...state,
signedIn: false,
fbToken: undefined
};
default:
return state;
}
}
function phoneContacts(state = [], action) {
switch (action.type) {
case ADD_PHONE_CONTACTS:
console.log("Adding Contacts");
return {
...state,
phoneContacts: action.phoneContacts
};
default:
return state;
}
}
export default finis;
Profile Non working. Triggers LOG_OUT action without the button being pushed.
import React, { Component } from "react";
import { Button, Card } from "react-native-elements";
import { View, Text, ActivityIndicator, AsyncStorage } from "react-native";
import { MapView } from "expo";
import { connect } from "react-redux";
import { bindActionCreators } from "redux";
import { SimpleLineIcons } from "#expo/vector-icons";
import Reactotron from "reactotron-react-native";
import * as ActionCreators from "../actions";
import { signOut } from "../components/Auth";
class ProfileWrap extends Component {
handleClick() {
Reactotron.log(this.Actions);
this.props.logOut();
}
render() {
return (
<View style={{ paddingVertical: 20 }}>
<Card title="Profile">
<View
style={{
backgroundColor: "#bcbec1",
alignItems: "center",
justifyContent: "center",
width: 80,
height: 80,
borderRadius: 40,
alignSelf: "center",
marginBottom: 20
}}
>
<Text style={{ color: "white", fontSize: 28 }}>JD</Text>
</View>
<Button title="Log Out" onPress={this.handleClick} />
</Card>
</View>
);
}
}
mapDispatchToProps = dispatch => {
return {
logOut: dispatch(logOut())
};
};
const Profile = connect(null, mapDispatchToProps)(ProfileWrap);
export default Profile;
Any help would be appreciated, even if it's telling me I'm doing the whole thing wrong :) Been at this for hours.
NEW Profile.js - Gives Cannot read property 'logOut' of undefined
import React, { Component } from "react";
import { Button, Card } from "react-native-elements";
import { View, Text, ActivityIndicator, AsyncStorage } from "react-native";
import { MapView } from "expo";
import { connect } from "react-redux";
import { SimpleLineIcons } from "#expo/vector-icons";
import { logOut } from "../actions";
import { signOut } from "../components/Auth";
class ProfileWrap extends Component {
handleClick() {
console.log(this.props);
this.props.logOut();
}
render() {
return (
<View style={{ paddingVertical: 20 }}>
<Card title="Profile">
<View
style={{
backgroundColor: "#bcbec1",
alignItems: "center",
justifyContent: "center",
width: 80,
height: 80,
borderRadius: 40,
alignSelf: "center",
marginBottom: 20
}}
>
<Text style={{ color: "white", fontSize: 28 }}>JD</Text>
</View>
<Button title="Log Out" onPress={this.handleClick} />
</Card>
</View>
);
}
}
const mapDispatchToProps = dispatch => {
return {
logOut: function() {
dispatch(logOut());
}
};
};
const Profile = connect(null, mapDispatchToProps)(ProfileWrap);
export default Profile;
Your mapDispatchToProps should be returning an object with functions. The way you have it now, logOut() will be called immediately since it's not inside a function. Changing it to this should fix it:
const mapDispatchToProps = dispatch => {
return {
logOut: function () {
dispatch(logOut());
}
};
};
Here's a slightly cleaner way of doing it:
const mapDispatchToProps = dispatch => ({
logOut() {
dispatch(logOut());
}
});
Also, you're missing const in front of mapDispatchToProps but that shouldn't have affected anything.
Edit:
You don't have to use this now, but it'll be helpful in the future - if your component is only using the render method you can change it to a stateless functional component. It's currently the recommended way of creating components when possible:
const ProfileWrap = props => (
<View style={{ paddingVertical: 20 }}>
<Card title="Profile">
<View
style={{
backgroundColor: "#bcbec1",
alignItems: "center",
justifyContent: "center",
width: 80,
height: 80,
borderRadius: 40,
alignSelf: "center",
marginBottom: 20
}}
>
<Text style={{ color: "white", fontSize: 28 }}>JD</Text>
</View>
<Button title="Log Out" onPress={props.logOut} />
</Card>
</View>
);