Check if function is dispatching action with jest and enzyme - react-native

I have a Login Screen that has a method that when called dispatches an action. I want to test using jest and enzyme if when the button is pressed, this function is called and, therefore, the action is dispatched. I tried in many different ways, but I couldn't achieve this.
screens/login.js
import React, { Component } from 'react';
import {
KeyboardAvoidingView,
TextInput,
StyleSheet,
Text,
Button,
TouchableOpacity
} from 'react-native';
import { connect } from 'react-redux';
import { login } from 'actions/sessions.js';
export class LoginScreen extends Component {
constructor(props){
super(props);
this.state = {
email: '',
password: '',
error: ''
}
}
requestLogin = async () => {
if(this.checkFields()){
this.setState({error: ''});
this.props.loginRequest(this.state.email, this.state.password);
}
}
checkFields = () => {
let { email, password } = this.state;
const re = /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if(!re.test(email)){
this.setState({error: 'E-mail must be valid'})
return false;
}else if(email == '' || password == ''){
this.setState({error: 'All fields are required!'});
return false
}else{
return true;
}
}
handleEmailText = (email) => {
this.setState({email})
}
handlePasswordText = (password) => {
this.setState({password})
}
render(){
return(
<KeyboardAvoidingView style={styles.container} enabled={true} behavior="padding">
<Text>{this.state.error || this.props.error}</Text>
<TextInput
onChangeText={(e) => this.handleEmailText(e)}
value={this.state.email}
keyboardType="email-address"
textContentType="emailAddress"
autoCapitalize="none"
placeholder="E-mail"
style={styles.input}
/>
<TextInput
onChangeText={(e) => this.handleEmailPassword(e)}
value={this.state.password}
placeholder="Password"
textContentType="password"
autoCapitalize="none"
secureTextEntry={true}
style={styles.input}
/>
<Button style={styles.button}
title="Sign In"
onPress={() => this.requestLogin()}/>
</KeyboardAvoidingView>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
input: {
width: "50%",
height: 40,
borderColor: 'gray',
borderWidth: 1,
paddingLeft: 20,
paddingRight: 20,
margin: 10
}
})
const mapDispatchToProps = dispatch => {
return {
loginRequest: (email, password) => dispatch(login(email, password))
}
}
const mapStateToProps = state => {
return {
error: state.sessions.error
}
}
export default connect(mapStateToProps, mapDispatchToProps)(LoginScreen);
tests/screens/login.test.js
import React from 'react';
import { shallow } from 'enzyme';
import toJson from 'enzyme-to-json';
import renderer from 'react-test-renderer';
import configureStore from 'redux-mock-store';
import { LoginScreen } from 'screens/Login';
describe('LoginScreen', () => {
it('should dispatch login action when button clicked', async () => {
const mockStore = configureStore();
const initialState = {};
const store = mockStore(initialState);
const wrapper = shallow(<LoginScreen store={store}/>)
wrapper.setState({email: 'foo#bar.com', password: '1234'})
const component = await wrapper.dive();
component.find('button').simulate('click');
expect(store.getActions()).toMatchSnapshot();
});
})
When I have this approach, it says that simulate is meant to be run on 1 node. 0 found instead. I have no clue how to test it.

In react native you can use:
component.find('Button').props().onPress();
to simulate user interaction.
Also, you should use Button instead of button (your component name).

Related

How to properly navigate between screen with react-native-navigation?

In my react-native app I have a log-in screen and a home screen.
The logic is: of the log-in process (using Firebase) is successful, and we have a "user" object,
we want to navigate from the log-in screen to the home screen.
When I attempt to implement this, the auth / log-in part works fine, but the "navigation.navigate" part results into this error:
"TypeError: undefined is not an object (evaluating 'navigation.navigate')".
Here is my App.js:
import React, { useEffect, useState } from 'react';
import { Navigation } from 'react-native-navigation';
import LoginScreen from './src/screens/LoginScreen/LoginScreen';
import {
SafeAreaView,
StyleSheet,
ScrollView,
View,
Text,
StatusBar,
Button
} from 'react-native';
import {
Header,
LearnMoreLinks,
Colors,
DebugInstructions,
ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';
const App = (props) => {
return (
<View style={styles.root}>
<LoginScreen />
</View>
);
};
App.options = {
topBar: {
title: {
text: 'Home',
color: 'white'
},
background: {
color: '#4d089a'
}
}
}
Navigation.registerComponent('Home', () => App);
Navigation.events().registerAppLaunchedListener(async () => {
Navigation.setRoot({
root: {
stack: {
children: [
{
component: {
name: 'Home'
}
}
]
}
}
});
});
const styles = StyleSheet.create({
root: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'whitesmoke'
}
});
export default App;
Here is my log-in screen, which has this error-inducing part:
navigation.navigate('Home', {user: user})
:
import React, { useState, useEffect } from 'react'
import { Image, Text, TextInput, TouchableOpacity, View } from 'react-native'
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import styles from './styles';
import { firebase } from '../../firebase/config'
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
export default function LoginScreen({ navigation }) {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [loading, setLoading] = useState(true)
const [user, setUser] = useState(null)
const Stack = createStackNavigator()
useEffect(() => {
const usersRef = firebase.firestore().collection('users');
firebase.auth().onAuthStateChanged(user => {
if (user) {
usersRef
.doc(user.uid)
.get()
.then((document) => {
const userData = document.data()
setLoading(false)
setUser(userData)
})
.catch((error) => {
setLoading(false)
});
} else {
setLoading(false)
}
});
}, []);
const onFooterLinkPress = () => {
navigation.navigate('Registration')
}
const onLoginPress = () => {
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then((response) => {
const uid = response.user.uid
const usersRef = firebase.firestore().collection('users')
usersRef
.doc(uid)
.get()
.then(firestoreDocument => {
if (!firestoreDocument.exists) {
alert("User does not exist anymore.")
return;
}
const user = firestoreDocument.data()
navigation.navigate('Home', {user: user})
})
.catch(error => {
alert(error)
});
})
.catch(error => {
alert(error)
})
}
return (
<View style={styles.container}>
<KeyboardAwareScrollView
style={{ flex: 1, width: '100%' }}
keyboardShouldPersistTaps="always">
<Image
style={styles.logo}
source={require('../../../assets/icon.png')}
/>
<TextInput
style={styles.input}
placeholder='E-mail'
placeholderTextColor="#aaaaaa"
onChangeText={(text) => setEmail(text)}
value={email}
underlineColorAndroid="transparent"
autoCapitalize="none"
/>
<TextInput
style={styles.input}
placeholderTextColor="#aaaaaa"
secureTextEntry
placeholder='Password'
onChangeText={(text) => setPassword(text)}
value={password}
underlineColorAndroid="transparent"
autoCapitalize="none"
/>
<TouchableOpacity
style={styles.button}
onPress={() => onLoginPress()}>
<Text style={styles.buttonTitle}>Log in</Text>
</TouchableOpacity>
<View style={styles.footerView}>
<Text style={styles.footerText}>Don't have an account? <Text onPress={onFooterLinkPress} style={styles.footerLink}>Sign up</Text></Text>
</View>
</KeyboardAwareScrollView>
</View>
)
}
What is the proper way of handling navigation in this scenario?
according to this link , you are trying to register the App component as the 'Home' screen in your navigation container. I don't think that's what you want.
Navigation.registerComponent('Home', () => App);
You will also need to register your Home screen, otherwise you won't be able to navigate to it. Your App component will automatically get injected with the navigation prop, so you just pass it along to the Login component. try the following:
const App = (props) => {
return (
<View style={styles.root}>
<LoginScreen ...props/>
</View>
);
};
App.options = {
topBar: {
title: {
text: 'Home',
color: 'white'
},
background: {
color: '#4d089a'
}
}
}
Navigation.registerComponent('Login', () => App);
Navigation.registerComponent('Home', () => YourHomeComponent);

invariant violation: element type is invalid React native

I'm trying to import a component into my App.js class but when I try I get the error in my android emulator
invariant violation element type is invalid: expected a string or a class/function but got undefined you likely forgot to export your component from the file its defined in, or you might have mixed up default and named imports
check the render method of 'login'
my problem is that I checked that i'm importing a default import of the login component in my App.js and I am exporting the Login component correctly from what I can tell:
import React, {Component} from 'react';
import {StyleSheet,ScrollView,View,Text,Input,Button,SecureTextEntry,ActivityIndicator} from 'react-native';
import * as firebase from 'firebase';
import auth from '#react-native-firebase/auth';
import {
Header,
LearnMoreLinks,
Colors,
DebugInstructions,
ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';
export default class Login extends Component {
state={
email: '',
password: '',
authenticating: false
};
componentDidMount(){
const firebaseConfig = {
apiKey: 'garbagekey',
authDomain: 'garbage auth domain'
}
firebase.initializeApp(firebaseConfig)
}
onButtonPress = () =>{
console.log("button pressed")
this.setState({authenticating:true})
}
contentBoolRender = () =>{
if(this.state.authenticating===true){
return(
<View>
<ActivityIndicator size="large"/>
</View>
)
}
return(
<View>
<Input
placeholder="Enter your Email..."
label = "Email"
onChangeText = {email => this.setState({email})}
value = {this.state.email}
/>
<Input
placeholder="Enter your Password..."
label = "Password"
onChangeText = {password => this.setState({password})}
value = {this.state.password}
SecureTextEntry
/>
<Button title="login" onpress={()=>this.onButtonPress()}></Button>
</View>
)
}
render() {
return(
<View>
{this.contentBoolRender()}
</View>
);
}
}
const styles = StyleSheet.create({
login:{
padding: 20,
backgroundColor: 'white'
},
});
App.js
import React, {Component} from 'react';
import {
SafeAreaView,
StyleSheet,
ScrollView,
View,
Text,
StatusBar,
Button,
} from 'react-native';
import * as firebase from 'firebase';
import auth from '#react-native-firebase/auth';
import {
Header,
LearnMoreLinks,
Colors,
DebugInstructions,
ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';
import Notes from "./notes.js";
import CreateNote from "./createnote.js";
import Login from "./login.js";
class App extends Component {
state = {
loggedin: false,
notes: [
{
id: 1,
text: "mow the lawn",
author: "dean",
time: "10am"
},
{
id: 2,
text: "feed the dog",
author: "sam",
time: "2pm"
}
]
};
componentDidMount(){
const firebaseConfig = {
apiKey: 'AIzaSyABmRXh2nlBt4FtjfOWNaoz7q5Wy5pGFlc',
authDomain: 'familytodo-28018.firebaseapp.com'
}
firebase.initializeApp(firebaseConfig)
}
confirmLogin=()=>{
this.setState({loggedin:true})
}
updateNotes = (title, name, eventTime) => {
console.log("making new note");
let newNote = {
text: title,
author: name,
time: eventTime
};
this.setState(state => {
const list = state.notes.concat(newNote);
console.log(list);
return {
notes: list
};
});
};
deleteNotes = note => {
console.log("note deleted", note.id);
this.setState(state => {
var list = this.state.notes;
for (let i = 0; i < this.state.notes.length; i++) {
if (this.state.notes[i].id === note.id) {
list.pop(i);
}
}
return {
notes: list
};
});
};
conditionalRender=()=>{
if(this.state.loggedin===false){
return (
<View>
<Login confirmlogin = {this.confirmLogin} />
</View>
)
}
return(
<View>
<CreateNote
handleName={this.handleName}
handleEvent={this.handleEvent}
handleTime={this.handleTime}
updateNotes={this.updateNotes}
/>
<Notes style={styles.notes} notes={this.state.notes} deleteNotes={this.deleteNotes} />
</View>
);
}
render() {
return(
<View>
{this.conditionalRender()}
</View>
);
}
}
const styles = StyleSheet.create({
app: {
marginHorizontal: "auto",
maxWidth: 500
},
logo: {
height: 80
},
header: {
padding: 20
},
title: {
fontWeight: "bold",
fontSize: 15,
marginVertical: 10,
textAlign: "center"
},
notes:{
marginHorizontal: '50%'
},
text: {
lineHeight: 15,
fontSize: 11,
marginVertical: 11,
textAlign: "center"
},
link: {
color: "#1B95E0"
},
code: {
fontFamily: "monospace, monospace"
}
});
export default App;
any help would be appreciate greatly please help me provide better info if possible :)
I think this is happening because react-native has no exported member named Input. I think you are looking for either TextInput (from react-native) or Input (from react-native-elements which has a label prop)
For the TextInput, try changing the login component to this:
import React, {Component} from 'react';
import {ActivityIndicator, Button, TextInput, View} from 'react-native';
import * as firebase from 'firebase';
export default class Login extends Component {
state = {
email: '',
password: '',
authenticating: false
};
componentDidMount() {
const firebaseConfig = {
apiKey: 'garbagekey',
authDomain: 'garbage auth domain'
};
firebase.initializeApp(firebaseConfig);
}
onButtonPress = () => {
console.log("button pressed");
this.setState({authenticating: true});
};
contentBoolRender = () => {
if (this.state.authenticating === true) {
return (
<View>
<ActivityIndicator size="large"/>
</View>
);
}
return (
<View>
<TextInput
placeholder="Enter your Email..."
onChangeText={email => this.setState({email})}
value={this.state.email}
/>
<TextInput
placeholder="Enter your Password..."
onChangeText={password => this.setState({password})}
value={this.state.password}
secureTextEntry
/>
<Button title="login" onPress={() => this.onButtonPress()}/>
</View>
);
};
render() {
return (
<View>
{this.contentBoolRender()}
</View>
);
}
}
or for Input try using this:
import React, {Component} from 'react';
import {ActivityIndicator, Button, View} from 'react-native';
import { Input } from 'react-native-elements';
import * as firebase from 'firebase';
export default class Login extends Component {
state = {
email: '',
password: '',
authenticating: false
};
componentDidMount() {
const firebaseConfig = {
apiKey: 'garbagekey',
authDomain: 'garbage auth domain'
};
firebase.initializeApp(firebaseConfig);
}
onButtonPress = () => {
console.log("button pressed");
this.setState({authenticating: true});
};
contentBoolRender = () => {
if (this.state.authenticating === true) {
return (
<View>
<ActivityIndicator size="large"/>
</View>
);
}
return (
<View>
<Input
placeholder="Enter your Email..."
label = "Email"
onChangeText={email => this.setState({email})}
value={this.state.email}
/>
<Input
placeholder="Enter your Password..."
label = "Password"
onChangeText={password => this.setState({password})}
value={this.state.password}
secureTextEntry
/>
<Button title="login" onPress={() => this.onButtonPress()}/>
</View>
);
};
render() {
return (
<View>
{this.contentBoolRender()}
</View>
);
}
}
Additionally, have a look through the code above as your Login component had a few other typos including: secureTextEntry and onPress

React Redux is not updating the state

I am searching for a couple of days and I can't find out why redux is not updating the state I don't see any problem in my code please help me to find the problem.
it is a simple login project. I can see that data changes inside the reducer when debugging but it's not being mapped to props and state is not changing.
this is my code:
actions.js
import {
LOGIN_SUCCESS,
LOGIN_FAILURE,
} from './types'
export const loginSuccess = (user) => ({
type: LOGIN_SUCCESS,
payload: user
})
export const loginFailure = (error) => ({
type: LOGIN_FAILURE,
payload: error
})
loginReducer.js
import {
LOGIN_SUCCESS,
LOGIN_FAILURE,
} from './types'
const initialState = {
user: null,
errorMessage: null
}
export const loginReducer = (state = initialState, action) => {
switch (action.type) {
case LOGIN_SUCCESS:
return {...state,user: action.payload, errorMessage: null }
case LOGIN_FAILURE:
return {...state,errorMessage: action.payload }
default:
return state;
}
}
loginScreen.js
import React from 'react'
import {
Text,
View,
ImageBackground,
Button,
StyleSheet
} from 'react-native'
import { TextField } from 'react-native-material-textfield';
import { loginSuccess, loginFailure } from './reduxx/actions'
import { connect } from "react-redux";
class LoginScreen extends React.Component {
constructor(props) {
super(props)
this.state = {
email: "",
password: "",
}
}
_handlePress() {
this.props.login(this.state.email, this.state.password)
// user in propa is undifined
this.props.user
}
render() {
let { email, password } = this.state
return (
<ImageBackground source={require('./images/loginBackground.jpg')} style={styles.imageBackground}>
<View style={styles.mainContainer}>
<View style={styles.box}>
<TextField label="Email" value={email} onChangeText={email => this.setState({ email })} />
<TextField label="Password" textContentType="password" value={password} onChangeText={password => this.setState({ password })} />
<Button onPress={() => {
this._handlePress()
}} color="green" title="Sign in" style={styles.button} />
</View>
<View style={styles.bottomTextContainer}>
<Text style={{ color: "white" }}>Don't have an account?</Text>
<Text style={{ color: "lightgreen" }} onPress={() => this.props.navigation.navigate("Register")}> Sign up</Text>
</View>
</View>
</ImageBackground>
)
}
}
function mapStateToProps(state) {
return {
user: state.user,
errorMessage: state.errorMessage
}
}
function mapDispatchToProps(dispatch) {
return {
login: (email, password) => {
try {
let user = //get user from database
dispatch(loginSuccess(user))
} catch (error) {
dispatch(loginFailure(error))
}
},
}
}
export default connect(mapStateToProps, mapDispatchToProps)(LoginScreen);
const styles = StyleSheet.create({
imageBackground: {
width: '100%',
height: '100%'
},
mainContainer: {
flex: 1,
justifyContent: "center"
},
box: {
backgroundColor: 'white',
padding: 8,
margin: 8
},
button: {
margin: 10
},
bottomTextContainer: {
position: "absolute",
bottom: 8,
alignSelf: "center",
flexDirection: "row"
}
});
app.js
import React from 'react';
import { Provider } from "react-redux"
import store from "./reduxx/store";
import AppContainer from './Navigator'
export default class App extends React.Component {
render() {
return (
<Provider store={store} >
<AppContainer />
</Provider>
);
}
}
store.js
import {createStore} from "redux";
import { combineReducers } from 'redux';
import {loginReducer} from './loginReducer'
const rootReducer = combineReducers({
loginReducer,
});
export default store = createStore(rootReducer)
The state which comes into mapStateToProps contains all the combined reducers, so we need to access the reducer name first from the state (like state.loginReducer.user), before trying to access the data of that reducer.
PFB code which should work:
function mapStateToProps(state) {
return {
user: state.loginReducer.user,
errorMessage: state.loginReducer.errorMessage
}
}
Change here :
function mapStateToProps(state) {
return {
user: state.user,
errorMessage: state.errorMessage
}
}
TO
function mapStateToProps(state) {
return {
login: state.loginReducer
}
}
And then this.props.user to this.props.login.user and this.props.errorMessage to this.props.login.errorMessage in all the occurrence.

The component for route '' must be a React Component

I'm new to react-native and am trying to use the Redux framework, I've encountered this problem: (The component for route ' ' must be a React component):
Thank you in advance.
Screen Error
Index.js
import React from 'react'
import { Provider } from 'react-redux'
import { AppRegistry } from 'react-native';
import { name as appName } from './app.json';
import Menu from './app/componnts/Menu'
import storeConfig from './app/store/storeConfig'
const store = storeConfig()
const Redux = () => {
return (
<Provider store={store}>
<Menu />
</Provider>
)
}
AppRegistry.registerComponent(appName, () => Redux);
Menu.js
import React, { Component } from 'react'
import {
StyleSheet,
SafeAreaView,
ScrollView,
Dimensions,
View,
Image,
Text
} from 'react-native'
import { createDrawerNavigator, DrawerItems, } from 'react-navigation'
import Login from './Login/Login'
import Viatura from './Viatura/Viatura'
export default class App extends Component {
render() {
return (
<AppDrawerNavigator />
)
}
}
const CustomDrawerComponent = (props) => (
<SafeAreaView style={{ flex: 1 }}>
<ScrollView>
<DrawerItems {...props} />
</ScrollView>
</SafeAreaView>
)
const AppDrawerNavigator = createDrawerNavigator({
Login,
Viatura,
}, {
contentComponent: CustomDrawerComponent,
drawerWidth: 300,
contentOptions: {
activeTintColor: 'orange'
}
})
Login.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { loginStore } from '../../store/actions/user.js';
import { Text, View, ImageBackground, Dimensions, Image, TextInput, TouchableOpacity, Alert } from 'react-native'
import Icon from 'react-native-vector-icons/FontAwesome'
import axios from 'axios'
import { createIconSetFromIcoMoon } from 'react-native-vector-icons'
import { API_MOTORISTAS_LOGIN } from '../../config'
import icoMoonConfig from '../../resources/fonts/selection.json'
import styles from './styles'
import bgImage from '../../images/fundo-vertical-preto.jpg'
import logo from '../../images/logo-tg-v-01.png'
const MyIcon = createIconSetFromIcoMoon(icoMoonConfig, 'icomoon', 'icomoon.ttf');
class Login extends Component {
constructor(props) {
super(props)
this.state = {
user: '',
password: '',
}
this.loginUser = this.loginUser.bind(this)
}
loginUser = () => {
try {
const { user, password } = this.state
this.setState({ error: '', })
axios.post(`${API_MOTORISTAS_LOGIN}`,
{
'param1': this.state.user,
'param2': this.state.password
}, {
"headers": {
'Content-Type': 'application/json',
}
}).then((response) => {
if (response.data.error) {
Alert.alert((response.data.error));
} else {
this.props.onLogin({ ...this.state })
const api_token = response.data.api_token
this.props.navigation.navigate('Viatura', {
api_token: api_token,
})
}
})
.catch((error) => {
console.log("axios error:", error);
});
} catch (err) {
Alert.alert((err))
}
}
static navigationOptions = {
drawerIcon: ({ tintColor }) => (
<MyIcon name="fonte-tg-33" style={{ fontSize: 24, color: tintColor }} />
)
}
render() {
return (
<ImageBackground
source={bgImage}
style={styles.backgroundContainer}>
<View
style={{
height: (Dimensions.get('window').height / 10) * 10,
alignItems: 'center',
justifyContent: 'center',
}}>
<View style={styles.logoContainer}>
<Image source={logo} style={styles.logo} />
</View>
<View style={styles.inputText}>
<View style={styles.iconContainer}>
<MyIcon style={styles.icon} name="fonte-tg-39" size={25} />
</View>
<TextInput
style={styles.input}
placeholder={'User'}
placeholderTextColor={'rgba(0, 0, 0, 0.6)'}
underlineColorAndroid='transparent'
value={this.state.user}
onChangeText={user => this.setState({ user })}
/>
</View>
<View style={styles.inputText}>
<View style={styles.iconContainer}>
<MyIcon style={styles.icon} name="fonte-tg-73" size={25} />
</View>
<TextInput
style={styles.input}
placeholder={'Password'}
secureTextEntry={true}
placeholderTextColor={'rgba(0, 0, 0, 0.6)'}
underlineColorAndroid='transparent'
value={this.state.password}
onChangeText={password => this.setState({ password })}
/>
</View>
<View style={styles.buttonsLogin}>
<TouchableOpacity style={styles.btnSair}>
<Text style={styles.text}> Sair </Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.loginUser} style={styles.btnEntrar}>
<Text style={styles.text}> Entrar </Text>
</TouchableOpacity>
</View>
</View>
</ImageBackground>
)
}
}
const mapDispatchToProps = dispatch => {
return {
onLogin: user => dispatch(loginStore(user))
}
}
export default connect(null, mapDispatchToProps)(Login);
actionTypes.js
export const USER_LOGGED_IN = 'USER_LOGGED_IN';
export const USER_LOGGED_OUT = 'USER_LOGGED_OUT';
user.js (inside actions folder)
import { USER_LOGGED_IN, USER_LOGGED_OUT } from './actionTypes'
export const login = user => {
return {
type: USER_LOGGED_IN,
payload: user
}
}
export const logout = () => {
return {
type: USER_LOGGED_OUT,
}
}
user.js (inside reducers folder)
import { USER_LOGGED_IN, USER_LOGGED_OUT } from '../actions/actionTypes'
const initalState = {
user: null,
password: null
}
const reducer = (state = initalState, action) => {
switch (action.type) {
case USER_LOGGED_IN:
return {
...state,
user: action.paypload.user,
password: action.paypload.password
}
case USER_LOGGED_OUT:
return {
...state,
user: null,
password: null,
}
default:
return state
}
}
export default reducer
storeConfig.js
import { createStore, combineReducers } from 'redux'
import userReducer from './reducers/user'
const reducers = combineReducers({
user: userReducer,
})
const storeConfig = () => {
return createStore(reducers)
}
export default storeConfig
As I said at the beginning of the post, I'm still a beginner at react-native, so I'm posting several snippets of code so that I can be as clear as possible about how my situation is.
I don't think your screen is properly setting export default.
can you change this code?
import React, { Component } from 'react'
import {
StyleSheet,
SafeAreaView,
ScrollView,
Dimensions,
View,
Image,
Text
} from 'react-native'
import { createDrawerNavigator, DrawerItems, } from 'react-navigation'
import Login from './Login/Login'
import Viatura from './Viatura/Viatura'
export default class App extends Component {
render() {
return (
<AppDrawerNavigator />
)
}
}
const CustomDrawerComponent = (props) => (
<SafeAreaView style={{ flex: 1 }}>
<ScrollView>
<DrawerItems {...props} />
</ScrollView>
</SafeAreaView>
)
const AppDrawerNavigator = createDrawerNavigator(
{
Login : () => <Login />,
Viatura : () => <Viatura />
},
{
contentComponent: CustomDrawerComponent,
drawerWidth: 300,
contentOptions: {
activeTintColor: 'orange'
}
}
)
OR
import { Login } from './Login/Login'
import { Viatura } from './Viatura/Viatura'
...
const AppDrawerNavigator = createDrawerNavigator(
{
Login : { screen: Login },
Viatura : { screen: Viatura }
},
{
contentComponent: CustomDrawerComponent,
drawerWidth: 300,
contentOptions: {
activeTintColor: 'orange'
}
}
)
Try this
const AppDrawerNavigator = createDrawerNavigator({
Login:Login,
Viatura:Login,
}, {
contentComponent: CustomDrawerComponent,
drawerWidth: 300,
contentOptions: {
activeTintColor: 'orange'
}
})

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;