React-redux: Why is the state undefined in my Home component? - react-native

I am having troubles with getting the state in my HomeComponent.js . Every time I try to print it, it return "undefined" .
I've tried different ways to call onPress in my Home component (e.g. onPress={this.printState()}, but none work)
This is my HomeComponent.js
//import statements
const mapStateToProps = state => {
return {
jobTitles: state.jobTitles
}
}
const mapDispatchToProps = dispatch => ({
fetchJobTitles: () => dispatch(fetchJobTitles())
});
class Home extends Component {
constructor(props) {
super(props);
this.state = {
jobInputValue: '',
addressInputValue: ''
};
}
componentDidMount() {
this.props.fetchJobTitles();
}
printState = () => {
console.log('State is: ' +
JSON.stringify(this.state.jobTitles));
}
render() {
return (
<ImageBackground style={styles.bkgImage} source={require('../assets/homepage_background.jpg')}>
//JSX goes here
<Button
title="CAUTÄ‚"
type="outline"
underlayColor={colors.red}
titleStyle={styles.buttonTitleStyle}
color={colors.red}
style={styles.buttonStyle}
onPress={this.printState}
/>
</ImageBackground>
);
}
}
//some styles
export default connect(mapStateToProps, mapDispatchToProps)(Home);
This is my reducer (jobTitles.js):
import * as ActionTypes from '../ActionTypes';
export const jobTitles = (state = { errMess: null,
jobTitles:[]}, action) => {
switch (action.type) {
case ActionTypes.GET_JOB_TITLES:
return {...state, errMess: null, jobTitles: action.payload};
case ActionTypes.JOB_TITLES_FAILED:
return {...state, errMess: action.payload};
default:
return state;
}
};
And this is my Action Creator:
import * as ActionTypes from './ActionTypes';
import { baseUrl } from '../shared/baseUrl';
export const fetchJobTitles = () => (dispatch) => {
return fetch(baseUrl + 'api/jobs/job_keywords')
.then(response => {
if (response.ok) {
return response;
} else {
var error = new Error('Error ' + response.status + ': ' +
response.statusText);
error.response = response;
throw error;
}
},
error => {
var errmess = new Error(error.message);
throw errmess;
})
.then(response => response.json())
.then(jobTitles => dispatch(addJobTitles(jobTitles)))
.catch(error => dispatch(jobTitlesFailed(error.message)));
};
export const jobTitlesFailed = (errmess) => ({
type: ActionTypes.JOB_TITLES_FAILED,
payload: errmess
});
export const addJobTitles = (jobTitles) => ({
type: ActionTypes.GET_JOB_TITLES,
payload: jobTitles
});
This is how the response from the API looks like:
"jobTitles": Object {
"results": Array [
"Engineer",
"Software",
"Software Architect",
"Software Consultant",
"Solution Architect",
"System Architect"
]
}
I expected the console.log() statement from the print() function in the HomeComponent.js to print the JSON response from the API, but instead it returns "undefined". Any ideas why?
Any help will be greatly appreaciated!

In your code :
this.state = {
jobInputValue: '',
addressInputValue: ''
};
What you try to print :
this.state.jobTitles
Of course it's undefined ! Either log this.props.jobTitles or set state jobTitles to print what you want.

You should use this.props.jobTitles
The mapStateToProps puts data from the redux state into the props of the component. this.state only holds the local state of the component. So jobInputValue and addressInputValue in this case. Everything from mapStateToProps and mapDispatchToProps will end up in the props. (As the name of the function indicates)

Related

Update points Redux React Native

I'm trying to load points from Firebase in order to display it on the screen
I'm using Redux, because the points number can be updated but I can not put this.props.Points.updatePoint inside Firebase request
How can I update it?
Home.js :
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
};
}
componentDidMount = async () => {
const pointsRef=firebase.database().ref("Users").child(firebase.auth().currentUser.uid).orderByChild("Points").once('value',function(snapshot){
const Points=snapshot.val().Points
});
this.props.Points.updatePoints(Points)
render(){
return(
<Text>{this.props.Points}</Text>
)}
}
const mapStateToProps = (state) => {
return {
Points:state.Points};
};
const mapDispatchToProps = (dispatch) => {
return {
updatePoints:(Points)=>dispatch({ type: "UPDATE_POINTS", payload: Points }),
};
};
PointReducer.js :
const initialState = {
Points: 0,
};
const Points = (state = initialState, action) => {
switch (action.type) {
case "UPDATE_POINTS":
return {
...state,
Points: action.payload,
};
default:
return state;
}
};
export default Points;
Your method is correct. The problem is actually with the way you're trying to access to updatePoints function in mapDispatchToProps & the place you're run the statement.
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount = async () => {
const pointsRef = firebase
.database()
.ref("Users")
.child(firebase.auth().currentUser.uid)
.orderByChild("Points")
.once("value", (snapshot) => {
const Points = snapshot.val().Points;
this.props.updatePoints(Points); // You can directly access to `updatePoints` using prop object.
}); // convert this function to an arrow function. It will fix `this` related issues.
};
render() {
return <Text>{this.props.Points}</Text>;
}
}
const mapStateToProps = (state) => {
return {
Points: state.Points,
};
};
const mapDispatchToProps = (dispatch) => {
return {
updatePoints: (Points) =>
dispatch({ type: "UPDATE_POINTS", payload: Points }),
};
};
Let me know if you need further support.

Api call happens only the first time in react-native redux

I am new to using react native redux and I am facing an issue that the api call is made only once, what if i click on another button which should render a different response based on the params and display it on the component which is a flatlist in my case. Please have a look at my code.
RecordListAction:
import { FETCH_RECORD_LIST, FETCH_RECORD_SUCCESS, FETCH_RECORD_FAILURE } from './types.js'
export const fetchRecordList = () => ({
type: FETCH_RECORD_LIST
})
export const fetchRecordSuccess = json => ({
type: FETCH_RECORD_SUCCESS,
payload: json
})
export const fetchRecordFailure = error => ({
type: FETCH_RECORD_FAILURE,
payload: error
})
export const fetchRecordListApi = () => {
console.log("Now I'm here!")
return async dispatch => {
dispatch(fetchRecordList());
let response = await
fetch(url, {
method: 'POST',
headers: {
'tenantid': '1',
'Content-Type': 'application/json',
'language': '1',
'userid': '11'
},
body: JSON.stringify(global.recordListBody)
}).then((response) => response.json())
.then((responseJson) => {
console.log("RecordList Action Value" + responseJson)
dispatch(fetchRecordSuccess(responseJson.records));
}).catch(error => {
dispatch(fetchRecordFailure(error))
}) }}
recordListReducer.js:
import {FETCH_RECORD_REQUEST,FETCH_RECORD_SUCCESS,FETCH_RECORD_FAILURE}
from "../actions/types"
const initialState = {
isFetching: false,
errorMessage : '',
record :[]
};
const recordListReducer = (state = initialState,action) => {
switch(action.type){
case FETCH_RECORD_REQUEST:
return { ...state, isFetching: true }
case FETCH_RECORD_FAILURE:
return { ...state, isFetching: false, errorMessage: action.payload };
case FETCH_RECORD_SUCCESS:
return{...state, isFetching:false, record:action.payload}
default:
return state
}};
export default recordListReducer;
RecordListContainer.js
import React, { Component } from 'react'
import { Text, View, StyleSheet, ActivityIndicator, Button } from 'react-native'
import PropTypes from 'prop-types';
import {fetchRecordListApi} from "../redux/actions/recordListAction"
import {connect} from "react-redux";
import DetailsViewMode from '../Enums/DetailsViewMode'
import RecordList from '../Components/RecordListComponents/RecordList';
import { Icon, Divider } from 'react-native-elements';
class RecordListContainer extends Component {
constructor(props) {
super(props);
}
componentDidMount(){
this.props.dispatch(fetchRecordListApi());
}
render(){
let content = <RecordList record = {this.props.recordList.record}/>
if(this.props.recordList.isFetching){
content= <ActivityIndicator size="large" />
}
}}
RecordListContainer.propTypes = {
fetchRecordListApi : PropTypes.func.isRequired,
recordList : PropTypes.object.isRequired}
const mapStateToProps = state =>{
return{
recordList: state.posts
};
}
export default connect(mapStateToProps)(RecordListContainer);
rootReducer.js :
import recordListReducer from './recordListReducers';'
import { combineReducers } from 'redux';
const rootReducer = combineReducers({
posts : recordListReducer,
});
export default rootReducer;
You could make recordListBody part of redux state or react context. Or you could make recordListBody observable and respond to changes. Here is an example of making recordListBody observable:
//object combined with global.recordListBody to add listeners
// and notify them of changes
const recordListBodyObserver = ((observers) => {
const removeObserver = (observer) => () => {
observers = observers.filter((o) => o !== observer);
};
return {
notify: (value) =>
observers.forEach((observer) => observer(value)),
add: (observer) => {
observers.push(observer);
return removeObserver(observer);
},
};
})([]);
let recordListBodyValue;
//your global object with recordListBody that will notify
// listeners if a value for recordListBody is set
const global = {
set recordListBody(value) {
//notify all listeners;
recordListBodyObserver.notify(value);
//set the new value
return (recordListBodyValue = value);
},
get recordListBody() {
return recordListBodyValue;
},
};
//function to create increasing id
const id = ((id) => () => id++)(1);
class App extends React.PureComponent {
componentDidMount() {
this.removeListener = recordListBodyObserver.add(
(value) => {
//you can dispatch your action here using value
// do not use global.recordListBody here becasue
// that has the old valuee
console.log(
'recordListBody changed from:',
global.recordListBody,
'to value:',
value
);
}
);
}
componentWillUnmount() {
//clean up listener when component unmounts
this.removeListener();
}
render() {
return (
<button
onClick={() => (global.recordListBody = id())}
>
Change recordListBody
</button>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
I am using componentDidUpdate and check if props value is changed, the api is again called when the body coming in props is changed.

React native mapDispatchToProps not working

I can't get my mapDispatchToProps to work properly.
I export a combineReducers:
export default combineReducers({
auth: AuthReducer,
tenants: TenantsReducer
});
The tenants reducer:
const INITIAL_STATE = {
error: false,
data: [],
tenantData: {},
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case GET_TENANTS_DATA:
return { ...state, error: false, data: action.payload };
case GET_TENANT_DATA:
return { ...state, error: false, tenantData: action.payload };
default:
return state;
}
};
Then I have getTenantByID method in my action
export const getTenantByID = ({ tenantID }) => {
return (dispatch) => {
const getTenant = {
FirstName: 'Jonh', LastName: 'Doe', Email: 'jonh#test.com', Phone: 'xxx-xxx-xxxx',
Unit: '101', MiddleName: '',
};
dispatch({
type: GET_TENANT_DATA,
payload: getTenant
});
};
};
Finally, I tried to use it in my component.
import { connect } from 'react-redux';
import { getTenantByID } from '../actions';
...
componentDidMount() {
const { navigation } = this.props;
const tenantID = navigation.getParam('tenantID', '0');
this.props.getTenantByID(tenantID);
console.log(this.props);
this.state = {
tenantData: this.props.tenantData
};
}
const mapStateToProps = ({ tenants }) => {
return {
error: tenants.error,
tenantData: tenants.tenantData
};
};
const mapDispatchToProps = () => {
return {
getTenantByID
};
};
export default connect(mapStateToProps, mapDispatchToProps)(TenantDetails);
In my componentDidMount, the console.log(this.props) is returning a empty object for tenantData. What am I doing wrong?
Initial state is showing as the component already mounted, which is empty object {}
this.props.getTenantByID(tenantId);
this action triggers actually, but the data is not available in componentDidMount lifecycle.
try putting log in render like this
componentDidMount(){
this.props.getTenantByID(2);
}
render() {
console.log(this.props.tenantData); // 1st render => {}, 2nd render=> desired data
return (
<div/>
);
}
use componentDidUpdate to check if value is changed,
componentDidUpdate(prevProps){
if(prevProps.tenantData !== this.props.tenantData){ console.log(prevProps.tenantData, this.props.tenantData) }
}
remember to receive the dispatch parameter in your mapDispatchToProps method
const mapDispatchToProps = (dispatch) => {
return {
getTenantByID: (tenantID ) => {
dispatch(getTenantByID({tenantID }));
};
};
};
call for
this.props.getTenantByID({ tenantID: 10 })

"Redux is not working properly with login"

I have implemented redux with my login form which is not working properly
it shows ERROR 'Cannot read property 'isAuthenticated' of undefined. which i think it means that redux is not implemented properly.
Please Help!
if you need any other file,tell me i will share it to you.
LoginForm.js
import React, {Component} from 'react';
import {
//StyleSheet,
View,
Text,
TextInput,
Button
} from 'react-native';
import {reduxForm,Field} from 'redux-form';
import {connect} from 'react-redux';
import {login} from './authActions';
const validate = values =>{
const errors = {};
if(!values.email){
errors.email="Please fill the email"
return errors;
}
if(!values.password){
errors.password="Please fill the password"
return errors;
}
}
const myFields = ({label,meta:{error,touched}, input:{onChange}}) =>{
return(
<View>
<Text>{label}</Text>
<TextInput style={{borderWidth:1,width:300,marginBottom:10}}
onChangeText={onChange}/>
{touched && (error && (<Text style={{color:'red'}}>{error}</Text>))}
</View>
);
}
const passFields = ({label,meta:{error,touched}, input:{onChange}}) =>{
return(
<View>
<Text>{label}</Text>
<TextInput style={{borderWidth:1,width:300,marginBottom:10}}
secureTextEntry={true}
onChangeText={onChange}/>
{touched && (error && (<Text style={{color:'red'}}>{error}</Text>))}
</View>
);
}
const submitbtn = values =>{
//alert(`here are the values ${JSON.stringify(values)}`);
//console.log(input.value);
this.props.login(values);
}
const myLoginForm = props => {
const {handleSubmit} = props;
return(
<View>
<Field
name="email"
component={myFields}
label="Email"/>
<Field
name="password"
component={passFields}
label="Password"
/>
<Button title="Submit"
onPress={handleSubmit(submitbtn)}/>
</View>
);
}
const LoginForm = reduxForm({
form:'loginform',
validate
})(myLoginForm);
const mapStateToProps =(state) =>({
isAuthenticated: state.auth.isAuthenticated
});
export default connect(mapStateToProps,{login})(LoginForm);
authActions.js
import axios from 'axios';
//import { returnErrors } from './errorActions';
export const register = ({username, name, email, password}) => {
return (dispatch, getState) => {
const config = {
headers : {
'Content-type' : 'Application/json'
}
}
const body = JSON.stringify({
username,
name,
email,
password
})
axios.post('http://localhost:5000/users/register', body , config )
.then(res => dispatch({
type : 'REGISTER_SUCCESS',
payload : res.data
}))
.catch(err => {
dispatch(returnErrors(err.response.data, err.response.status, 'REGISTER_FAIL'));
dispatch({
type : 'REGISTER_FAIL'
})
});
};
};
export const login = ({username, password}) => {
return (dispatch, getState) => {
const config = {
headers : {
'Content-type' : 'Application/json'
}
}
const body = JSON.stringify({
username,
password
})
axios.post('http://localhost:5000/users/login', body , config )
.then(res => dispatch({
type : 'LOGIN_SUCCESS',
payload : res.data
}))
.catch(err => {
dispatch(returnErrors(err.response.data, err.response.status, 'LOGIN_FAIL'));
dispatch({
type : 'LOGIN_FAIL'
})
});
};
}
export const logout = () => {
return {
type : 'LOGOUT_SUCCESS'
}
}
export const loadUser = () => {
return (dispatch, getState) => {
dispatch({
type: 'USER_LOADING',
});
axios.get('http://localhost:5000/users/auth' , tokenConfig(getState))
.then(res => dispatch({
type: 'USER_LOADED',
payload : res.data
}))
.catch(err => {
dispatch(returnErrors(err.response.data.message, err.response.status));
dispatch({
type : 'AUTH_ERROR'
});
})
}
}
//
export const tokenConfig = (getState) => {
const token = getState().auth.token;
const config = {
headers : {
'content-type' : 'Application/json',
}
}
if(token) {
config.headers['auth'] = token;
}
return config;
}
authReducer.js
const initState = {
toke: localStorage.getItem('token'),
isAuthenticated: null,
isLoading: null,
user: null
};
const authReducer = (state = initState, action) => {
switch(action.type){
case 'USER_LOADING':
return{
...state,
isLoading: true
}
case 'USER_LOADED':
return{
...state,
isLoading: false,
isAuthenticated:true,
user:action.payload
}
case 'REGISTER_SUCCESS':
case 'LOGIN_SUCCESS':
localStorage.setItem('token', action.payload.token)
return{
...state,
...action.payload,
isLoading: false,
isAuthenticated:true,
}
case 'AUTH_ERROR':
case 'LOGOUT_SUCCESS':
case 'LOGIN_FAIL':
case 'REGISTER_FAIL':
localStorage.removeItem('token')
return{
token: null,
user: null,
isLoading: false,
isAuthenticated:false,
}
default:
return state;
}
}
export default authReducer
Please check file where you combine your reducers, there's possibility that you put your reducer as something other than "auth" or even you forgotten to put the reducer here.
You can install "redux-devtools-extension" package and it's chrome extension to see which reducers are connected to your redux state and debug it more easily. Also, redux-form requires you to pass the form reducer you create along with all the other reducers you have in 'combineReducers'. Follow the instructions on their docs https://redux-form.com/8.2.2/docs/gettingstarted.md/
hope this helps :)

store.getstate() evaluate to undefined

in a react-native project,I keep hitting in this error:
state is undefined, evaluating store.getstate()
//store.js
const composeEnhancers =
typeof window === "object" && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
: compose;
const enhancer = composeEnhancers(applyMiddleware(thunk));
const Store = createStore(
combineReducers(
{
form: formReducer,
appointmentsReducer
},
enhancer
)
);
console.log(Store.getState());
export default Store;`
//reducer.js
import {
FETCH_APPOINTMENTS_BEGIN,
FETCH_APPOINTMENTS_SUCCESS,
FETCH_APPOINTMENTS_FAILURE
} from '../actions/appointmentsAction';
const initialState = {
data: [],
loading: false,
error: null
};
export default function appointmentsReducer(state = initialState, action) {
switch(action.type) {
case FETCH_APPOINTMENTS_BEGIN:
return {
...state,
loading: true,
error: null
};
case FETCH_APPOINTMENTS_SUCCESS:
return {
...state,
loading: false,
data: action.payload.appointments
};
case FETCH_APPOINTMENTS_FAILURE:
return {
...state,
loading: false,
error: action.payload.error,
data: []
};
default:
return state;
}
}
//actions.js
import { listUrl } from "../cst";
export const FETCH_APPOINTMENTS_BEGIN = "FETCH_APPOINTMENTS_BEGIN";
export const FETCH_APPOINTMENTS_SUCCESS = "FETCH_APPOINTMENTS_SUCCESS";
export const FETCH_APPOINTMENTS_FAILURE = "FETCH_PRODUCTS_FAILURE";
export const fetchAppointmentsBegin = () => ({
type: FETCH_APPOINTMENTS_BEGIN
});
export const fetchAppointmentsSuccess = appointments => ({
type: FETCH_APPOINTMENTS_SUCCESS,
payload: { appointments }
});
export const fetchAppointmentsFailure = error => ({
type: FETCH_APPOINTMENTS_FAILURE,
payload: { error }
});
export function fetchAppointments() {
return dispatch => {
dispatch(fetchAppointmentsBegin());
return fetch(listUrl)
.then(handleErrors)
.then(res => res.json())
.then(json => {
dispatch(fetchApointmentsSuccess(json.appointment));
return json.appointment;
})
.catch(error => dispatch(fetchAppointmentsFailure(error)));
};
}
// Handle HTTP errors since fetch won't.
function handleErrors(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}
// app.js
export default function App() {
return (
<Provider store={Store}>
<Navigation />
</Provider>
);
}
//list rendrer component :
const mapStateToProps = state => ({
data: state.appointments.data,
loading: state.loading,
error: state.error
});
the console.log of store.getstate() gives :
Object {
"appointmentsReducer": Object {
"data": Array [],
"error": null,
"loading": false,
},
"form": Object {},
I'm not sure where the problem is.
Is it due to the asynchronous call not being handled properly?
If I use saga to handle the fetch, will it resolve the problem?
Any help would be appreciated .