Reputation: 645
I'm trying to configure redux with react-native I face an error called undefined is not a function when try to call an action from my component ,
It seems that action() function is not called correctly , I can't access state as prop inside the form component.
This is my code :
index.android.js
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
// import ReduxThunk from 'redux-thunk';
import reducers from './src/reducers';
import { Form } from './src/components/Form';
import { AppRegistry } from 'react-native';
export class App extends Component {
componentWillMount() {
//here we will handle firebase sutup process for the authntication purpose .
}
render() {
const store = createStore(reducers);
return (
<Provider store={store}>
<Form />
</Provider>
);
}
}
const styles = {
main: {
flex: 1,
flexDirection: 'column',
justifyContent: 'flex-start',
}
};
AppRegistry.registerComponent('test', () => App);
Form.js
import React, { Component } from "react";
import { connect } from "react-redux";
import { bindActionCreators } from "redux";
import { Image } from "react-native";
import { emailChanged } from "../actions";
import {
Container,
ContainerSection,
Button,
Input
} from ".././components/common";
export class Form extends Component {
onEmailChange(text) {
//call the action creator for the email
this.props.emailChanged(text);
}
onPasswordChange(text) {
//call the action creator for the password
}
onButtonPress() {
//do something
}
renderButton() {
return <Button onPress={this.onButtonPress.bind(this)}>Log in</Button>;
}
render() {
return (
<Container>
<ContainerSection>
<Image source={require("../../images/logo.png")} />
</ContainerSection>
<ContainerSection>
{/* email input */}
<Input
label="Email"
placeholder="[email protected]"
value={this.props.email}
onChangeText={this.onEmailChange.bind(this)}
keyboardType="email-address"
/>
</ContainerSection>
<ContainerSection>
{/* password input */}
<Input
label="password"
placeholder="password"
value={this.props.password}
onChangeText={this.onPasswordChange.bind(this)}
secure
/>
</ContainerSection>
<ContainerSection>{this.renderButton()}</ContainerSection>
</Container>
);
}
}
const mapStateToProps = state => {
return {
email: state.auth.email,
password: state.auth.password
};
};
function mapDispatchToProps(dispatch) {
return bindActionCreators({ emailChanged }, dispatch);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Form);
index.js (inside actions folder )
import { EMAIL_CHANGED, PASSWORD_CHANGED } from './types';
//it is an action creator
export const emailChanged = (text) => {
return {
type: EMAIL_CHANGED,
payload: text
};
};
//it is an action creator
export const passwordChanged = (text) => {
return {
type: PASSWORD_CHANGED,
payload: text
};
};
index.js(inside reducers folder)
import { combineReducers } from 'redux';
import AuthReducer from './AuthReducer';
export default combineReducers({
auth: AuthReducer
// employeeForm: EmployeeFormReducer,
// employees: EmployeeReducer
});
AuthReducer.js
import {
EMAIL_CHANGED,
PASSWORD_CHANGED
} from '../actions/types';
const INITIAL_STATE = {
email: '',
password: ''
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case EMAIL_CHANGED:
return { ...state, email: action.payload };
case PASSWORD_CHANGED:
return { ...state, password: action.payload };
default:
return state;
}
};
Upvotes: 8
Views: 6962
Reputation: 778
I also face this problem and spend lot of time.. This issue is not working react-redux connect in this component. Because excute
export class Form extends Component
Therefore not execute the
export default connect(
mapStateToProps,
emailChanged,
)(Form);
Solution :
remove the export keyword eg :
class Form extends Component
and import where this component you must use
import Form from './src/components/Form';
Don't use
import { Form } from './src/components/Form';
in this case make changes as follows,
index.android.js
import Form from './src/components/Form';
Form.js
export class Form extends Component {
...
}
export default connect(
mapStateToProps,
emailChanged ,
)(Form);
Hope this is helpful for you... Happy Coding...!
Upvotes: 10
Reputation: 3
The same thing happened to me yesterday. But when I copied all the code files to the previous project, it worked. So it must be something with the corrupted installation, as the installation has been done the same way using same packages. In one project connect works, in the other, it does not.
Upvotes: 0
Reputation: 645
Finally I solved the error, first of all I changed the structure of the code I put the Provider and the store at index.android.js directly, then after that I changed this function :
function mapDispatchToProps(dispatch) {
return bindActionCreators({ emailChanged }, dispatch);
}
to this :
function mapDispatchToProps (dispatch) {
return {
emailChanged: () => dispatch(emailChanged())
}
}
without using bindActionCreators
then I installed the dependencies using yarn add ,
thanks to Sacha Best notes also you may need to make sure you have the following packages installed via npm :
"babel-plugin-transform-decorators-legacy": "^1.3.4",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react-native": "^3.0.2",
"babel-preset-stage-0": "^6.24.1",
also make sure your .babelrc has:
{
"presets": ["react-native", "es2015", "stage-0"],
"sourceMaps": true,
"plugins": [
["transform-decorators-legacy"],
]
}
important note to consider that : Both the React and Redux teams generally discourage the use of connect() as a decorator, for a variety of reasons. The spec is still unstable, the Babel plugins are still changing, and even when the plugins are working, using connect() as a decorator can lead to unexpected behavior from a developer's point of view.
This worked for me, but if it still doesn't work with you try to use connect() as function not as decorator .
these changes solved the error ! hope that this will help you . Thank you all.
Upvotes: 0
Reputation: 23
Please try something this in in your Form.js
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { Image } from 'react-native';
import { emailChanged } from '../actions';
import { Container, ContainerSection, Button, Input } from '.././components/common';
export class Form extends Component {
constructor(props) {
super(props)
this.onPasswordChange= this.onPasswordChange.bind(this)
}
onEmailChange(text) {
//call the action creator for the email
this.props.emailChanged(text);
}
onPasswordChange(text) {
//call the action creator for the password
}
onButtonPress() {
//do something
}
renderButton() {
return (
<Button onPress={this.onButtonPress.bind(this)}>
Log in
</Button>
);
}
render() {
return (
<Container>
<ContainerSection>
<Image source={require('../../images/logo.png')} />
</ContainerSection>
<ContainerSection>
{/* email input */}
<Input
label="Email"
placeholder="[email protected]"
value={this.props.email}
onChangeText={this.onEmailChange.bind(this)}
keyboardType="email-address"
/>
</ContainerSection>
<ContainerSection>
{/* password input */}
<Input
label="password"
placeholder="password"
value={this.props.password}
onChangeText={this.onPasswordChange}
secure
/>
</ContainerSection>
<ContainerSection>
{this.renderButton()}
</ContainerSection>
</Container>
);
}
}
const mapStateToProps = state => {
return {
email: state.auth.email,
password: state.auth.password
};
};
FlipCardWithImage.propTypes = {
emailChanged : PropTypes.func,
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ emailChanged }, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Form);
Upvotes: 0
Reputation: 1037
Try just passing emailChanged directly.
return bindActionCreators(emailChanged, dispatch);
Upvotes: 1
Reputation: 873
try to use dispatch calling from any component. like this
Form.js
this.props.dispatch(emailChanged(text));
Upvotes: 0