how to connect react with redux? - react-native

so I am trying to learn about react-redux using react-native and I want to make a page where I can input a number and press login. when I press login, the page will alert me the number I input and saved into the store I created with redux.
can anyone please tell me what i'm doing wrong and what should I add or do to make it work?
below is my testing page
import React, {Component} from 'react';
import {View, TextInput, TouchableOpacity, Text} from 'react-native';
import {connect} from 'react-redux';
import actions from '../Redux/Action';
class tes extends Component{
constructor(props){
super(props)
}
render(){
return(
<View>
<TextInput placeholder="phone number"
keyboardType="number-pad"/>
<TouchableOpacity onPress={this.props.onLogin}>
<Text>login</Text>
</TouchableOpacity>
</View>
)
}
}
mapStateToProps = state => {
return {
number: state.phoneNumber
}
}
mapDispatchToProps = dispatch => {
return {
onLogin: (number) => {
dispatch(actions.setLoginNumber(number))
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(tes);
this is my store class
import {createStore} from 'redux';
import reducer from './Reducer';
export default createStore(reducer)
here is my reducer class
const reducer = (state = {
phoneNumber: '',
},action) => {
switch(action.type) {
case "LOGIN":
state = {
phoneNumber: action.payload
}
break;
}
return state;
}
export default reducer;
{/* and this one my action class */}
export default function setLoginNumber(number) {
return{
type: "LOGIN",
payload: number
};
}
thanks in advance..

I think your not passing parameter number to onLogin function and you will need local state variable to hold the value. The code should be like this
import React, {Component} from 'react';
import {View, TextInput, TouchableOpacity, Text} from 'react-native';
import {connect} from 'react-redux';
import actions from '../Redux/Action';
class tes extends Component{
constructor(props){
super(props)
this.state = {
number: 0,
};
}
render(){
return(
<View>
<TextInput placeholder="phone number"
onChangeText={inputNumber => {
this.setState({ number: inputNumber })
}}
keyboardType="number-pad"/>
<TouchableOpacity onPress={() => {this.props.onLogin(this.state.number) }}>
<Text>login</Text>
</TouchableOpacity>
</View>
)
}
mapStateToProps = state => {
return {
number: state.phoneNumber
}
}
mapDispatchToProps = dispatch => {
return {
onLogin: (number) => {
dispatch(actions.setLoginNumber(number))
}
}
}
Answer for your second question -
You haven't passed created store to provider component of react-redux like below example
import { Provider } from 'react-redux';
import App from './App';
import store from './store';
export default class Root extends Component {
constructor() {
super();
}
render() {
return (
<Provider store={store}>
<App />
</Provider>
);
}
}
Hope it helps.

Related

React-Native TextInput one step behind in Child Component

TextInput in Child Component on logging is one step behind in my Input
Parent Component:
import React from 'react';
import {
View,
Text,
} from 'react-native';
import NumberInput from '../../Components/NumberInput'; //child component
class Login extends React.Component {
constructor(props) {
super(props);
this.state = {
number: '',
};
}
onChange = value => {
this.setState({number: value});
console.log(this.state.number);
};
render() {
return (
<View>
<Text>Login</Text>
<NumberInput onChange={this.onChange} />
</View>
);
}
}
Child Component
import React from 'react';
import {View, Text, TextInput} from 'react-native';
import {withNavigation} from 'react-navigation';
const NumberInput = ({onChange}) => {
return (
<View>
<Text>Enter mobile number to continue</Text>
<TextInput
style={styles.input}
keyboardType={'phone-pad'}
placeholder="999-999-9999"
underlineColorAndroid="transparent"
autoCompleteType="tel"
maxLength={10}
onChangeText={value => onChange(value)}
/>
</View>
);
};
export default withNavigation(NumberInput);
Pass your console.warn() as a callback to this.setState()
this.setState({
number: value
},() => {
console.warn(this.state.number);
});

Cannot read property 'navigate' of undefined in react-navigation

I have used import { StackNavigator } from 'react-navigation'; in my Router.js
import { StackNavigator } from 'react-navigation';
import LoginForm from './components/LoginForm';
import EmployeeList from './components/EmployeeList';
import EmployeeCreate from './components/EmployeeCreate';
const RouterComponent = StackNavigator(
{
LoginForm: {
screen: LoginForm,
navigationOptions: {
title: 'Please Login'
}
},
EmployeeList: {
screen: EmployeeList,
},
EmployeeCreate: {
screen: EmployeeCreate,
navigationOptions: {
title: 'Create Employee'
}
}
},
{
initialRouteName: 'LoginForm',
}
);
export default RouterComponent;
Of course i use it in my App.js
import React from 'react';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import ReduxThunk from 'redux-thunk';
import reducers from './src/reducers';
import Router from './src/Router';
export default class App extends React.Component {
render() {
const store = createStore(reducers, {}, applyMiddleware(ReduxThunk));
return (
<Provider store={store}>
<Router />
</Provider>
);
}
}
And i can use this.props.navigation in my LoginForm.js like this function:
onButtonPress() {
const { email, password, navigation } = this.props;
this.props.loginUser({ email, password, navigation });
}
I pass navigation to my Action file , i can use it to navigate another screen , like this:
const loginUserSuccess = (dispatch, user, navigation) => {
dispatch({
type: LOGIN_USER_SUCCESS,
payload: user
});
//navigation is from LoginForm.js , navigate to EmployeeList is working
navigation.navigate('EmployeeList');
};
Now i try to use this.props.navigation.navigate in my ListItem.js
My ListItem is under EmployeeList.js
Here is my EmployeeList.js
import _ from 'lodash';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { View, Text, Button, FlatList } from 'react-native';
import { employeesFetch } from '../actions';
import ListItem from './ListItem';
class EmployeeList extends Component {
static navigationOptions = ({ navigation }) => ({
title: 'EmployeeList',
headerLeft: null,
headerRight: <Button title="Add" onPress={() => navigation.navigate('EmployeeCreate')} />,
});
componentWillMount() {
this.props.employeesFetch();
}
// Using ListItem over here
renderRow(employee) {
return <ListItem employee={employee} />;
}
render() {
console.log(this.props);
return (
<FlatList
data={this.props.employees}
renderItem={this.renderRow}
keyExtractor={employee => employee.uid}
/>
);
}
}
const mapStateToProps = state => {
const employees = _.map(state.employees, (val, uid) => {
return { ...val, uid };
});
return { employees };
};
export default connect(mapStateToProps, { employeesFetch })(EmployeeList);
Here is my problem use this.props.navigation.navigate in ListItem.js
import React, { Component } from 'react';
import { Text, View, TouchableWithoutFeedback } from 'react-native';
import { CardSection } from './common';
class ListItem extends Component {
onRowPress() {
this.props.navigation.navigate('EmployeeCreate');
}
render() {
const { item } = this.props.employee;
return (
<TouchableWithoutFeedback onPress={this.onRowPress.bind(this)}>
<View>
<CardSection>
<Text style={styles.titleSytle}>
{item.name}
</Text>
</CardSection>
</View>
</TouchableWithoutFeedback>
);
}
}
const styles = {
titleSytle: {
fontSize: 18,
paddingLeft: 15
}
};
export default ListItem;
I can use this.props.navigation in my LoginForm.js , i can't figure it out why i use it in ListItem.js navigate is undefined ?
Any help would be appreciated. Thanks in advance.
in file EmployeeList.js pass navigation as prop to ListItem.
renderRow(employee) {
return <ListItem employee={employee} navigation={this.props.navigation} />;
}
Now you should be able to access navigation using this.props.navigation inside ListItem.js.
Just an observation, never bind methods to context inside the render
function as it is called repeatedly and a new instance will be created
each time. Change your ListItem.js as below.
class ListItem extends Component {
constructor(props) {
super(props);
this.onRowPress = this.onRowPress.bind(this); // here we bind it
}
onRowPress() {
this.props.navigation && this.props.navigation.navigate('EmployeeCreate');
}
render() {
const { item } = this.props.employee;
return (
<TouchableWithoutFeedback onPress={this.onRowPress}>
<View>
<CardSection>
<Text style={styles.titleSytle}>
{item.name}
</Text>
</CardSection>
</View>
</TouchableWithoutFeedback>
);
}
}
Use withNavigation. In your ListItem.js file add import { withNavigation } from ‘react-navigation’; and replace export default ListItem; with export default withNavigation(ListItem);
Here is what I did (using latest react native with ES6):
Refactored the class code from this:
export default class MyComponent extends React.Component {
// content & code
}
To look like this:
import { withNavigation } from 'react-navigation';
class MyComponent extends React.Component {
// content & code
}
export default withNavigation(MyComponent);
According to the docs (withNavigation):
"withNavigation is a higher order component which passes the navigation prop into a wrapped component."

Create a login form with TextInput in child component

I am new to React Native and I am trying to implement a simple Login form.
I tried the following first, which works:
import React, { Component } from 'react';
import {
View,
Text,
TextInput,
StyleSheet,
} from 'react-native';
import TitledInput from './login-form';
export default class LoginForm extends Component {
constructor(props) {
super(props);
this.state = { email: '', password: ''};
}
render() {
return (
<View style={styles.container}>
<TextInput
label='Email Adress'
placeholder='you#domain.com'
value={this.state.email}
onChangeText={(email) => this.setState({email})}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
Then I wanted to split the input in another independent component called TitledInput and tried this (which is not working):
LoginForm
import React, { Component } from 'react';
import {
View,
Text,
StyleSheet,
} from 'react-native';
import TitledInput from './login-form';
export default class LoginForm extends Component {
constructor(props) {
super(props);
this.state = { email: '', password: ''};
}
render() {
return (
<View style={styles.container}>
<TitledInput
label='Email Adress'
placeholder='you#domain.com'
value={this.state.email}
onChangeText={(email) => this.setState({email})}
/>
</View>
);
}
}
TitledInput
import React, { Component } from 'react';
import { View, Text, TextInput, StyleSheet } from 'react-native';
export default class TitledInput extends Component {
const { inputStyle, labelStyle, containerStyle } = styles;
render() {
return (
<View style={container}>
<Text style={label}>{props.label.toUpperCase()}</Text>
<TextInput
autoCorrect={false}
placeholder={props.placeholder}
secureTextEntry={props.secureTextEntry}
value={props.value}
onChangeText={props.onChangeText}
style={input}
/>
</View>
);
}
}
I get a 'maximum call stack exceeded' error.
I could read on the internet that this error can occur when calling setState in a re-render function... but I don't know how to handle this case where I want my login form to know the value a its input child component.
I want to know it because I will use it when clicking the submit button or is it the whole purpose of the state?
while you are changing email input value in TiledInput email value will propagate to LoginForm. In LoginForm because of state "email" change the login form will rerender. In this case you have to use ComponentWillReceiveProps inorder to get the new email value.
Instead of this approach you can have another state value inside TitledInput to keep email value.
import React, { Component } from 'react';
import {
View,
Text,
StyleSheet,
} from 'react-native';
import TitledInput from './login-form';
export default class LoginForm extends Component {
constructor(props) {
super(props);
this.state = { email: '', password: ''};
}
render() {
return (
<View style={styles.container}>
<TitledInput
label='Email Adress'
placeholder='you#domain.com'
onChangeText={(email) => this.setState({email})}
/>
</View>
);
}
}
// TitledInput
import React, { Component } from 'react';
import { View, Text, TextInput, StyleSheet } from 'react-native';
export default class TitledInput extends Component {
constructor(props) {
super(props);
this.state = { text: ''};
}
const { inputStyle, labelStyle, containerStyle } = styles;
handleTextChange(value){
this.setState({text:value});
this.props.onChangeText(value);
}
render() {
return (
<View style={container}>
<Text style={label}>{props.label.toUpperCase()}</Text>
<TextInput
autoCorrect={false}
placeholder={props.placeholder}
secureTextEntry={props.secureTextEntry}
value={this.state.text}
onChangeText={this.handleTextChange()}
style={input}
/>
</View>
);
}
}
[update] By using onBlur instead of onChangeText you can reduce the number of function call

Cannot read property 'navigate' of Undefined in React Navigation

i'm React Native newbie. What i'm trying to do is added react navigation to my login page where user can click a button and navigate to the sign up page but i'm getting an error Cannot read property 'navigate' of Undefined. I've already searched the solution over an internet but no luck. This So does not help me - React Navigation - cannot read property 'navigate' of undefined and same with others .
Here is my code
index.ios.js
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
import {StackNavigator} from 'react-navigation';
import Login from './src/screens/Login';
import Signup from './src/screens/Signup';
export default class tapak extends Component {
constructor(props) {
super(props);
this.buttonPress = this.buttonPress.bind(this);
}
render() {
return (
<View style={styles.container}>
<Text style={{color: 'blue'}} onPress={this.buttonPress}>sign up</Text>
</View>
);
}
buttonPress() {
console.log('called');
this.props.navigation.navigate('Signup');
}
}
const Stacks = StackNavigator({
Login: {
screen: Login
},
Signup:{
screen: Signup
}
});
Render the StackNavigator in your index.ios.js and move the button to the Login component:
const Stacks = StackNavigator({
Login: {
screen: Login
},
Signup:{
screen: Signup
}
});
class tapak extends Component {
render() {
return (
<Stacks />
);
}
}
Login.js :
export default class Login extends Component {
constructor(props) {
super(props);
this.buttonPress = this.buttonPress.bind(this);
}
buttonPress() {
console.log('called');
this.props.navigation.navigate('Signup');
}
render() {
return (
<View style={styles.container}>
<Text style={{color: 'blue'}} onPress={this.buttonPress}>sign up</Text>
</View>
);
}
}
Working example
here.
Write this code to index.ios.js
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
import {StackNavigator} from 'react-navigation';
import Login from './src/screens/Login';
import Signup from './src/screens/Signup';
const Stacks = StackNavigator({
Login: {
screen: Login
},
Signup:{
screen: Signup
}
});
Login.js
import React ,{Component} from 'react';
import {
Text, View , Button,Image,
} from 'react-native';
export default class HomeScreen extends Component {
render() {
const { navigate } = this.props.navigation;
return (
<View>
<Text
onPress={() => navigate('Signup')}
> SignUp</Text>
</View>
);
}
}
Hope this help you.
I think you need to include navigationOptions, for example:
class MyComponent extends React.Component {
static navigationOptions = {
title: 'Great',
// other configurations
}
render() {
return (
// your view
)
}
}
Also yu need to make sure you use AppRegistry.registerComponent('glfm', () => Stacks); rather than AppRegistry.registerComponent('glfm', () => tapak);
The only answer to this question is to just put const { navigate } = this.props.navigation in your render() function and then you can use it in any component that you need
For Example
render() {
const { navigate } = this.props.navigation;
return (
<View>
<Text>This is the home screen of the app</Text>
<Button
onPress={() => navigate('Profile', { name: 'Brent' })}
title="Go to Brent's profile"
/>
</View>
);
}
Please read this doc for https://reactnavigation.org/docs/en/navigation-prop.html

react-native redux dispatch action from nested components

I am wondering how I can dispatch an action from n-th level nested components. This is wha I have got:
BodyContainer (contains connect, mapProps, mapDispatch, etc.) => Body
=Body (Component) where the actions are dispatched)
==Grid (Component)- state is passed as props from Body and some elements parts of the state are further passed on to the next component as props.
===Square (Component) - receives some part of the state as props.
Now, I'd like to dispatch an action from the Square component to change the state. I thought I'll just do a SquareContainer first but then how would it get the parts of the state from Grid?
See below the components (let me know if you need more information):
BodyContainer:
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { listNumbers, pickNumber } from '../actions/numberActions';
import { populateRow, populateGrid } from '../actions/gridActions';
import Body from '../components/Body';
const mapStateToProps = state => ({
numbers: state.numbers,
grid: state.grid
});
const mapDispatchToProps = dispatch => (
bindActionCreators({
listNumbers,
pickNumber,
populateRow,
populateGrid
}, dispatch)
);
export default connect(
mapStateToProps,
mapDispatchToProps
)(Body);
Body Component
import React, { Component, PropTypes } from 'react';
import { View, StyleSheet, Button } from 'react-native';
import Grid from './Grid';
import * as globalStyles from '../styles/global';
export default class Body extends Component {
componentWillMount() {
this.refresh();
}
refresh() {
this.props.populateGrid();
}
render() {
return (
<View style={styles.body}>
<Grid inGrid={this.props.grid} />
<View style={styles.buttonContainer}>
<Button
onPress={this.refresh.bind(this)}
title={'Regenerate the Grid'}
/>
</View>
</View>
);
}
}
Grid component
import React, { Component, PropTypes } from 'react';
import { View, StyleSheet } from 'react-native';
import Square from './Square';
import * as globalStyles from '../styles/global';
export default class Grid extends Component {
render() {
const row = [];
let i = 0;
this.props.inGrid.forEach((r) => {
r.forEach((c) => {
i++;
row.push(
<Square key={i} sqValue={c} />
);
});
});
const { grid } = styles;
return (
<View style={grid}>
{row}
</View>
);
}
}
Square Component
import React, { PropTypes } from 'react';
import { View, Text, StyleSheet, TouchableHighlight } from 'react-native';
import * as globalStyles from '../styles/global';
const Square = ({ sqValue }) => {
const { square, textStyle, squareActive } = styles;
return (
<TouchableHighlight
style={[square, sqValue[1] && squareActive]}
onPress={() => console.log(sqValue[0])}
>
<View>
<Text style={textStyle}>{sqValue[0]},{sqValue[1]}</Text>
</View>
</TouchableHighlight>
);
};
Edit: I've changed the Square component to a stateful one:
import React, { Component, PropTypes } from 'react';
import { View, Text, StyleSheet, TouchableHighlight } from 'react-native';
import * as globalStyles from '../styles/global';
export default class Square extends Component {
render() {
const { square, textStyle, squareActive } = styles;
const { sqValue } = this.props;
return (
<TouchableHighlight
style={[square, sqValue[1] && squareActive]}
onPress={() => console.log(sqValue[0])}
>
<View>
<Text style={textStyle}>{sqValue[0]},{sqValue[1]}</Text>
</View>
</TouchableHighlight>
);
}
}
I'd like to dispatch an action from onPress={}. Thank you