I am new to React/Redux and I am stuck in a problem. My fetched data from API is not being passed to props. It's always an empty object.
I see that there might be some issues that I am not even aware of but I don't have a clue where to look for.
Please check my codes below:
RegisterPage.jsx
import { connect } from 'react-redux';
import { userActions } from '../_actions';
class RegisterPage extends React.Component {
constructor(props) {
super(props);
this.state = {
user: {
first_name: '',
last_name: '',
properties_id: '',
email: '',
phone_number: '',
password: ''
},
submitted: false,
checked: false,
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
componentDidMount() {
this.props.dispatch(userActions.getAll());
}
handleChange(event) {
const { name, value } = event.target;
const { user } = this.state;
this.setState({
user: {
...user,
[name]: value
},
checked: !this.state.checked
});
}
handleSubmit(event) {
event.preventDefault();
this.setState({ submitted: true });
const { user } = this.state;
const { dispatch } = this.props;
if(this.state.checked) {
if (user.first_name && user.last_name && user.properties_id &&
user.email && user.phone_number && user.password) {
dispatch(userActions.register(user));
}
} else {
alert("Please tick the checkbox to agree to Terms and Conditions");
}
}
render() {
const { registering, properties } = this.props;
const { user, submitted } = this.state;
return (......)
}
function mapStateToProps(state) {
const { registering } = state.registration;
const { properties } = state;
return {
properties,
registering
};
}
const connectedRegisterPage = connect(mapStateToProps)(RegisterPage);
export { connectedRegisterPage as RegisterPage };
users.reducers.js
export function users(state = {}, action) {
switch (action.type) {
case userConstants.GETALL_REQUEST:
return {
loading: true
};
case userConstants.GETALL_SUCCESS:
return {
items: action.properties
//action.users
};
case userConstants.GETALL_FAILURE:
return {
error: action.error
};
default:
return state
}
}
user.actions.js
export const userActions = {
login,
logout,
register,
getAll,
delete: _delete
};
function getAll() {
return dispatch => {
dispatch(request());
userService.getAll()
.then(
properties => dispatch(success(properties)),
error => dispatch(failure(error.toString()))
);
};
function request() { return { type: userConstants.GETALL_REQUEST } }
function success(properties) { return { type: userConstants.GETALL_SUCCESS, properties } }
function failure(error) { return { type: userConstants.GETALL_FAILURE, error } }
}
user.service.js
// Get All Properties
function getAll() {
const requestOptions = {
method: 'GET'
};
return fetch(`${config.apiUrl}/api/properties`, requestOptions).then(handleResponse).then(
properties => {
return properties;
}
);
}
Here's the screenshot of the console:
It is clear that properties array is not empty. But when I am going to use properties, it is empty. I don't know what's wrong. If anyone could help figure out what's wrong with my code or something that I missed, your help will be greatly appreciated. I just need to fix this so I could move forward. Thanks in advance!
I thinking that your state tree might not contain state.properties but instead state.items. Unless if you did something in combineReducers() that changes the shape of it again.
case userConstants.GETALL_SUCCESS:
return {
items: action.properties
//action.users
};
This part would probably cause action.properties to be stored in state.items instead of state.properties
I'd recommend using ReduxDevTools to make your life with state easier
Related
I'm having a React Error #31 which describes as "Objects are not valid as a React child."
The problem is I'm trying to do Object Destructuring for Form Validations as follows, so I'm trying to figure out the best way to solve this (unless refactoring everything to arrays?).
import { useReducer } from 'react';
const initialInputState = {
value: '',
isTouched: false,
};
const inputStateReducer = (state, action) => {
if (action.type === 'INPUT') {
return { value: action.value, isTouched: state.isTouched };
}
if (action.type === 'BLUR') {
return { isTouched: true, value: state.value };
}
if (action.type === 'RESET') {
return { isTouched: false, value: '' };
}
return inputStateReducer;
};
const useInput = (validateValue) => {
const [inputState, dispatch] = useReducer(inputStateReducer, initialInputState);
const valueIsValid = true;
const hasError = !valueIsValid && inputState.isTouched;
const valueChangeHandler = (event) => {
dispatch({ type: 'INPUT', value: event.target.value });
}
const inputBlurHandler = (event) => {
dispatch({ type: 'BLUR' });
}
const reset = () => {
dispatch({ type: 'RESET' });
}
return {
value: inputState.value,
isValid: valueIsValid,
hasError,
valueChangeHandler,
inputBlurHandler,
reset,
}
}
export default useInput;
import useInput from "../hooks/use-input";
import { useState } from 'react';
export default function InterestForms({ photoSet, onInterestLightboxChange, interestLightboxContent }) {
const alphabetOnly = (value) => /^[a-zA-Z() ]+$/.test(value);
const [isFormSubmitted, setFormSubmitted] = useState(false);
// Calling useInput and expect values in object destructuring.
// However, it seems this is causing React Error #31 since it expect values to be in different format
const {
value: nameValue,
isValid: nameIsValid,
hasError: nameHasError,
valueChangeHandler: nameChangeHandler,
inputBlurHandler: nameBlurHandler,
reset: resetName,
} = useInput(alphabetOnly);
...
Refactoring this to arrays might solve the problem. But I want to see if there's a better solution (or if I just missed something simple).
I had a fully functioning Android application and now I am getting this error:
null is not an object (evaluating '_this.state.displayErrors') which is referencing this line of code here:
_getErrors = () => {
if (this.state.displayErrors) {
return {
...this.state.validationErrors,
...this.props.validationErrors
};
}
return {};
};
This is the full file:
import React, { Component } from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import DetailsConfirmationForm from "auth/components/DetailsConfirmationForm";
import {
firstNameChanged,
lastNameChanged,
prefixChanged,
suffixChanged,
stateChanged
} from "auth/registrationActions";
import regex from "utils/helpers/regex";
import { prefixes, suffixes } from "enums/dropdownOptions";
export class DetailsConfirmation extends Component {
static propTypes = {
firstName: PropTypes.string,
firstNameChanged: PropTypes.func.isRequired,
lastName: PropTypes.string,
lastNameChanged: PropTypes.func.isRequired,
navigation: PropTypes.object,
prefix: PropTypes.string,
prefixChanged: PropTypes.func.isRequired,
registeredUser: PropTypes.object,
state: PropTypes.string,
stateChanged: PropTypes.func.isRequired,
suffix: PropTypes.string,
suffixChanged: PropTypes.func.isRequired,
validationErrors: PropTypes.object
};
constructor(props) {
super(props);
}
componentDidMount() {
const { personalDetails, personalAddress } = this.props.registeredUser;
console.log(this.props.registeredUser);
if (personalDetails) {
this.props.firstNameChanged(personalDetails.firstName);
this.props.lastNameChanged(personalDetails.lastName);
this.props.suffixChanged(personalDetails.suffix);
this.props.prefixChanged(personalDetails.prefix);
}
if (personalAddress && personalAddress.stateCode) {
this.props.stateChanged(personalAddress.stateCode);
}
const { params = {} } = this.props.navigation.state;
const { displayAlert = true } = params;
this.state = {
validationErrors: {},
displayErrors: false,
titleName: personalDetails && personalDetails.firstName,
displayAlert
};
}
componentWillReceiveProps(nextProps) {
if (this.state.displayErrors) {
this._validate(nextProps);
}
}
_validate = props => {
const { prefix, state, firstName, lastName, suffix } = props;
const validPrefixes = prefixes.map(p => p.value);
const validSuffixes = suffixes.map(p => p.value);
const validationErrors = {
prefix:
prefix && prefix.trim() && validPrefixes.includes(prefix)
? ""
: "Is Required",
state: state && state.trim() ? "" : "Is Required",
firstName: firstName && firstName.trim() ? "" : "Is Required",
lastName: lastName && lastName.trim() ? "" : "Is Required",
suffix:
!suffix || validSuffixes.includes(suffix) ? "" : "Select an option"
};
const nameRegexErrorMessage =
"Only letters, hyphens and periods are allowed.";
if (validationErrors.firstName === "" && !regex.userName.test(firstName)) {
validationErrors.firstName = nameRegexErrorMessage;
}
if (validationErrors.lastName === "" && !regex.userName.test(lastName)) {
validationErrors.lastName = nameRegexErrorMessage;
}
const fullErrors = {
...validationErrors,
...this.props.validationErrors
};
const isValid = Object.keys(fullErrors).reduce((acc, curr) => {
if (fullErrors[curr] !== "") {
return false;
}
return acc;
}, true);
if (isValid) {
this.setState({ validationErrors: {} });
//register
} else {
this.setState({ validationErrors, displayErrors: true });
}
return isValid;
};
_navigate = () => {
const isValid = this._validate(this.props);
if (isValid) {
if (this.props.registeredUser.organization) {
this.props.navigation.navigate("CompleteAccount");
} else {
this.props.navigation.navigate("AskForMembership");
}
}
};
_getErrors = () => {
if (this.state.displayErrors) {
return {
...this.state.validationErrors,
...this.props.validationErrors
};
}
return {};
};
render() {
return (
<DetailsConfirmationForm
{...this.state}
{...this.props}
navigate={this._navigate}
validationErrors={this._getErrors()}
/>
);
}
}
const mapsStateToProps = ({ registrations }) => {
return {
...registrations.accountData,
validationErrors: registrations.validationErrors,
registeredUser: registrations.registeredUser
};
};
export default connect(
mapsStateToProps,
{
firstNameChanged,
lastNameChanged,
prefixChanged,
suffixChanged,
stateChanged
}
)(DetailsConfirmation);
Is this a scoping issue? The if statement does not have access to displayErrors outside of _getErrors function? If so, how in the world did this work for several weeks before?
I tried placing:
this.state = {
validationErrors: {},
displayErrors: false,
titleName: personalDetails && personalDetails.firstName,
displayAlert
};
inside the constructor(props) function where I believe this belongs, but then I get a ton of other problems regarding the variables in it such as personalDetails and displayAlert not being defined as variables. The biggest pain being that displayAlert.
For setting this.state in your constructor you will need to destructure props to get the values you need similar to what is happening in componentDidMount. I would remove the setting of initial state values from componentDidMount altogether.
constructor(props) {
super(props);
const { personalDetails, personalAddress } = props.registeredUser;
const { params = {} } = props.navigation.state;
const { displayAlert = true } = params;
this.state = {
validationErrors: {},
displayErrors: false,
titleName: personalDetails && personalDetails.firstName,
displayAlert
};
}
componentDidMount() {
const { personalDetails, personalAddress } = this.props.registeredUser;
console.log(this.props.registeredUser);
if (personalDetails) {
this.props.firstNameChanged(personalDetails.firstName);
this.props.lastNameChanged(personalDetails.lastName);
this.props.suffixChanged(personalDetails.suffix);
this.props.prefixChanged(personalDetails.prefix);
}
if (personalAddress && personalAddress.stateCode) {
this.props.stateChanged(personalAddress.stateCode);
}
}
I'm not sure if this will fix your problem or how this.state is getting set to null.
I think the problem is with this line in DetailsConfirmationForm component
validationErrors={this._getErrors()}
You are calling function instead of passing .
Try this
validationErrors={this._getErrors}
I am new to the react-redux. Now here what I am doing is ,
In this, On click of login , and on the success of this ,
history.push('/');
I am redirecting user on this page.
Now,
import React from 'react';
import { fetchUserJd } from '../action/index';
import { connect } from 'react-redux';
import { Redirect } from 'react-router-dom';
import JobList from '../../JobList/container/JobList';
class LandingPage extends React.Component {
componentDidMount() {
this.props.fetchJobDescription();
}
render() {
if (this.props.jobs && this.props.jobs.content && this.props.jobs.content.length > 0) {
return <JobList />;
}
else {
return <Redirect to="/create-job" />
}
}
}
const mapDispatchToProps = (dispatch) => {
return {
fetchJobDescription: () => dispatch(fetchUserJd())
}
}
const mapStateToProps = (state) => {
return {
jobs: state.UserJobs.response
}
}
export default connect(mapStateToProps, mapDispatchToProps)(LandingPage);
Here this is my landing page.
Now, Here what I do is I call an api in the componentDidMount and get some data. If data is avaliable then I redirect user to some another page or to some diff route.
const initialState = {
response: [],
error: false
}
case FETCHING_JOBDESCRIPTION_SUCCESS:
return {
...state,
response: action.data,
error: false,
}
This is my reducer where I used the initial state as an empty array.
Now Here what is happening, it is directly going to the else condition to the create-job but the data is avaliable so it should have rendered that component. But, Somehow it is not happening.
Response is like =>
{
"content": [{
"id": "5b7d4a566c5fd00507501051",
"hrmsJdId": null,
"companyId": null,
"jdName": "Senior/ Lead UI Developer",
"jobDescription": null,
}]
}
render() {
if (this.props.isFetching) {
return null;
}
else if (this.props.jobs && this.props.jobs.content && this.props.jobs.content.length > 0) {
return <JobList />;
}
else {
return <Redirect to="/create-job" />
}
}
}
The solution that I used is like this ,
export function fetchUserJd() {
return (dispatch) => {
let url = FETCH_JD_ROOT_URL + page + "&" + size;
dispatch({
type: REQUEST_INITIATED
})
return get(url)
.then((response) => {
if (response.status === 200) {
dispatch(
{
type: FETCHING_JOBDESCRIPTION_SUCCESS,
data: response.payload
}
)
dispatch({
type: REQUEST_SUCCESSED
})
} else {
dispatch({
type: REQUEST_SUCCESSED
})
}
})
}
return Promise.resolve();
};
So, Here I used Promise.resolve Now, in the componentDidMount,
in constructure,
constructor(props) {
super(props);
this.state = {
isloading: true
}
}
componentDidMount() {
this.props.fetchJobDescription().then(() => {
this.setState({
isloading: false
})
});
}
SO, can any one please help me with this issue?
I'm trying to get a simple API call working, where the component calls the API as its mounting and sets the state to be rendered. But when I try to get the state to change an object in it, it says that the state is undefined.
TypeError: Cannot read property 'state' of undefined
class SpellGrid extends React.Component {
constructor(props) {
super(props);
this.state = {
value: '',
spacing: '16',
username: 'admin',
password: 'notpassword',
description: '',
remember: false,
spell: {
name: '',
school: '',
},
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.mapApiToState = this.mapApiToState.bind(this);
}
mapApiToState() {
// I've tried with all of the axios code in here.
}
componentDidMount() {
axios
.get("http://localhost:8000/api/spells/1")
.then(function(response) {
console.log('response', response);
let fields = response.data[0].fields;
// THIS IS THE LINE THAT IS ERRORING
let spell = Object.assign({}, this.state.spell);
spell.name = fields.Name;
spell.school = fields.School;
console.log('spell', spell);
this.setState({spell});
console.log('state.spell', this.state.spell);
//console.log('state', this.state);
})
.catch(function(error) {
console.log(error);
});
console.log('state', this.state);
}
handleChange = name => event => {
this.setState({
[name]: event.target.value,
});
};
onSubmit = (event) => {
event.preventDefault();
this.props.onSubmit(this.state.username, this.state.password)
};
handleSubmit(e) {
console.log('Form state: ', this.state);
e.preventDefault();
}
render() {
const {classes, theme} = this.props;
const { spacing } = this.state;
return (
<div>{this.state.spell.name}</div>
);
}
} export default withStyles(styles, { withTheme: true })(SpellGrid);
If you are using this, you will need to be carefull in which function scope you're in:
axios
.get("http://localhost:8000/api/spells/1")
.then(response => {
// Since the `response` is now an arrow function, we still
// get access to the original `this`
let fields = response.data[0].fields;
let spell = Object.assign({}, this.state.spell);
spell.name = fields.Name;
spell.school = fields.School;
this.setState({
spell
});
})
.catch(error => {
console.log(error);
});
This is the code. No idea as to why there is a problem.
class TeacherForm extends Component {
constructor({ data }) {
super();
this.isUpdatingForm = !! data;
this.state = Object.assign({ ... });
this.handleSubmit = this.handleSubmit.bind(this);
this.removeTeacher = this.removeTeacher.bind(this);
}
handleChange(value, field) {
this.setState({ shouldUpdate: true, [field]: value });
}
handleSubmit(e) {
e.preventDefault();
const { name, subjects, parttime, timing } = this.state;
if (this.isUpdatingForm) {
return update.call({
_id: this.props.data._id,
transaction: { name, subjects, parttime, timing },
}, () => this.setState({ shouldUpdate: false }));
}
return add.call();
}
removeTeacher() {
return remove.call(this.props.data._id);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
...
</form>
);
}
}
The error gets thrown at the handleSubmit method in the callback of update.call. This normally shows up when I call removeTeacher and a list updates and this component unmounts.
It sounds like the callback () => this.setState({ shouldUpdate: false }) is executed after that the component is unmounted. Is that possible? If so, one way to get around that is to replace this part by
return update.call({
_id: this.props.data._id,
transaction: { name, subjects, parttime, timing },
}, () => { !this.unmounted && this.setState({ shouldUpdate: false }); });
and to add
componentWillUnmount() {
this.unmounted = true;
}