Mahmoud Abd AL Kareem
Mahmoud Abd AL Kareem

Reputation: 645

connect redux function is not working with react-native

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;
      }
    };

This is the error returned

Upvotes: 8

Views: 6962

Answers (6)

Chanaka
Chanaka

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

P. ter
P. ter

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

Mahmoud Abd AL Kareem
Mahmoud Abd AL Kareem

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

yousef najem
yousef najem

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

basudz
basudz

Reputation: 1037

Try just passing emailChanged directly.

return bindActionCreators(emailChanged, dispatch);

Upvotes: 1

bpsourav21
bpsourav21

Reputation: 873

try to use dispatch calling from any component. like this

Form.js

this.props.dispatch(emailChanged(text));

Upvotes: 0

Related Questions