React Native: null is not an object (evaluating '_this.state.displayErrors') - javascript

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}

Related

"Objects are not valid as a React child" in Object destructuring

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).

React Input onSelect Component shows Previous Selection

I've create an autocomplete search input bar using AWS Cloudscape. When I attempt to use the onSelect to save state, it does not save state. When I select a second item from the search bar, the first item then shows up. I assume this is a state async issue. I need the most recent selection, how do I get that?
import React from "react";
import Regions from "./trailforks_regions.json"
import RegionNames from "./region_names.json"
import * as dfd from "danfojs";
import { Autosuggest } from '#cloudscape-design/components';
export class AutoCompleteInputComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
df: new dfd.DataFrame(Regions),
value: null,
final_value: "",
options: [],
status: "loading",
regionContext: [],
uri_value: "",
loaded: false
};
}
lookupRegionUriName() {
console.log("Checking for uri region name on:" + this.state.final_value)
let region_name_split = this.state.final_value.split(",")[0]
let query_df = this.state.df.query(this.state.df["name"].eq(region_name_split))
let uri_name = query_df["region_uri_name"].values
console.log("found:" + uri_name)
return uri_name
}
handleChange = event => {
this.setState({ value: event.detail.value });
};
handleSelect = event => {
console.log(event) // This shows the correct data! But the set state does not set this data.
this.setState({ final_value: event.detail.value });
this.lookupRegionUriName(this.state.final_value)
};
handleLoadItems = ({ detail: { filteringText, firstPage, samePage } }) => {
this.filteringText = filteringText;
var my_options = RegionNames.filter(regions => regions.toLowerCase().startsWith(this.filteringText.toLowerCase()))
var region_values = []
for (var i = 0; i <= my_options.length; i++) {
region_values.push({ value: my_options[i] })
}
this.setState({
options: region_values.slice(0, 25),
status: 'loading',
});
};
enteredTextLabel = value => `Use: "${value}"`;
renderInput() {
if (this.state.df != null) {
const { status, value, options, final_value, uri_value } = this.state;
return (
<>
<Autosuggest
onChange={this.handleChange}
onSelect={this.handleSelect}
onLoadItems={this.handleLoadItems}
value={value}
options={options}
enteredTextLabel={this.enteredTextLabel}
ariaLabel="Autosuggest example with suggestions"
placeholder="Enter Trailforks Region Name"
empty="No matches found"
finishedText={this.filteringText ? `End of "${this.filteringText}" results` : 'End of all results'}
//status={status}
loadingText="searching"
filteringType="manual"
/>
</>
)
} else {
return (<></>)
}
}
render() {
return (
<>
{this.renderInput()}
</>
)
}
}
Figured it out. It was a state issue, here are the changes for future people:
import React from "react";
import Regions from "./regions.json"
import RegionNames from "./region_names.json"
import * as dfd from "danfojs";
import { Autosuggest } from '#cloudscape-design/components';
export class AutoCompleteInputComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
df: new dfd.DataFrame(Regions),
value: null,
final_value: "",
options: [],
status: "loading",
regionContext: [],
uri_value: "",
loaded: false
};
}
updateRegion(state_object) {
this.lookupRegionUriName()
return state_object.final_value
}
lookupRegionUriName() {
console.log("Checking for uri region name on:" + this.state.final_value)
let region_name_split = this.state.final_value.split(",")[0]
let query_df = this.state.df.query(this.state.df["name"].eq(region_name_split))
let uri_name = query_df["region_uri_name"].values
this.setState({uri_value: uri_name})
console.log("found:" + uri_name)
}
handleChange = event => {
this.setState({ value: event.detail.value });
};
handleSelect = event => {
console.log(event) // This shows the correct data! But the set state does not set this data.
this.setState({ final_value: event.detail.value });
this.lookupRegionUriName(this.state.final_value)
};
handleLoadItems = ({ detail: { filteringText, firstPage, samePage } }) => {
this.filteringText = filteringText;
var my_options = RegionNames.filter(regions => regions.toLowerCase().startsWith(this.filteringText.toLowerCase()))
var region_values = []
for (var i = 0; i <= my_options.length; i++) {
region_values.push({ value: my_options[i] })
}
this.setState({
options: region_values.slice(0, 25),
status: 'loading',
});
};
enteredTextLabel = value => `Use: "${value}"`;
renderInput() {
if (this.state.df != null) {
const { status, value, options, final_value, uri_value } = this.state;
return (
<>
<Autosuggest
onChange={this.handleChange}
onSelect={event => {this.setState({final_value: event.detail.value}, () => {this.updateRegion(this.state)})}}
//onSelect={this.handleSelect}
onLoadItems={this.handleLoadItems}
value={value}
options={options}
enteredTextLabel={this.enteredTextLabel}
ariaLabel="Autosuggest example with suggestions"
placeholder="Enter Trailforks Region Name"
empty="No matches found"
finishedText={this.filteringText ? `End of "${this.filteringText}" results` : 'End of all results'}
status={status}
loadingText="searching"
filteringType="manual"
/>
</>
)
} else {
return (<></>)
}
}
render() {
return (
<>
{this.renderInput()}
</>
)
}
}

React native : How can I turn the class component code into a function component with hooks?

How can I turn the following code into a function component with hooks?
In my example I use a class component
And I want to change the code to a function component form
export default class Modal2 extends Component {
state = {
placeName: "",
errorMsg: null
};
placeNameChangedHandler = val => {
this.setState({
placeName: val,
errorMsg: null
});
};
onConfirm = () => {
const { placeName } = this.state;
const { onConfirm, onHideModal } = this.props;
if (placeName.trim().length > 5) {
onConfirm("Cancel", placeName);
onHideModal();
this.setState({ placeName: "", errorMsg: null })
} else {
this.setState({ errorMsg: "must 5 letters" });
}
};
}
That's how it should look after converting it.
import React, { useState } from 'react';
function Modal2(props) {
const [desiredStateName, setDesiredStateName] = useState({
placeName: "",
errorMsg: null
});
placeNameChangedHandler = val => {
setDesiredStateName({
placeName: val,
errorMsg: null
});
};
onConfirm = () => {
const { placeName } = desiredStateName.placeName;
const { onConfirm, onHideModal } = props;
if (placeName.trim().length > 5) {
onConfirm("Cancel", placeName);
onHideModal();
setDesiredStateName({ placeName: "", errorMsg: null })
} else {
setDesiredStateName((prevState)=>{
return{ ...prevState,errorMsg: "must 5 letters" }
})
}
};
}
export default Modal2;
Also, a quick guide of how you are able to do it by yourself
Have a try by replacing your code with the below code:
import React, { useState } from 'react';
export default Modal2 = props => {
const [placeName, setPlaceName] = useState("")
const [errorMsg, setErrorMsg] = useState(null)
placeNameChangedHandler = val => {
setPlaceName(val)
setErrorMsg(null)
};
onConfirm = () => {
const { onConfirm, onHideModal } = props;
if (placeName.trim().length > 5) {
onConfirm("Cancel", placeName);
onHideModal();
setPlaceName("")
setErrorMsg(null)
} else {
setErrorMsg("must 5 letters")
}
};
}

React Redux Data not being passed to props

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

React Enzyme Jest error jest.fn() should be called

My component is as below
import React from 'react';
import { connect } from 'react-redux';
import { Button } from 'react-bootstrap';
import UserActions from '../../../actions/sampleUserAction';
import UserForm from '../../../views/sample/userForm';
import UsersList from '../../../views/sample/usersList';
#connect(store => ({
users: store.sampleUserReducer.users,
}))
export default class UserComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
displayForm: false,
user: { id: '', fName: '', lName: '' },
isCreationMode: true,
};
this.addNewUser = this.addNewUser.bind(this);
this.handleChange = this.handleChange.bind(this);
this.submitForm = this.submitForm.bind(this);
this.editUser = this.editUser.bind(this);
this.deleteUser = this.deleteUser.bind(this);
}
addNewUser() {
this.setState({
displayForm: !this.state.displayForm,
isCreationMode: true,
user: { id: '', fName: '', lName: '' },
});
}
createUser(users) {
users.push({
id: users.length + 1,
fName: this.state.user.fName,
lName: this.state.user.lName,
});
return users;
}
updateUser(users) {
users.forEach((user) => {
if (user.id === this.state.user.id) {
user.fName = this.state.user.fName;
user.lName = this.state.user.lName;
}
});
return users;
}
submitForm(e) {
e.preventDefault();
let { users } = this.props;
if (this.state.isCreationMode) {
users = this.createUser(users);
} else if (!this.state.isCreationMode) {
users = this.updateUser(users);
}
this.addNewUser();
this.props.dispatch(UserActions.listUsers(users));
}
handleChange(e) {
const { id } = this.state.user;
let { fName, lName } = this.state.user;
if (e.target.name === 'fName') {
fName = e.target.value;
}
if (e.target.name === 'lName') {
lName = e.target.value;
}
this.setState({ user: { id, fName, lName } });
}
editUser(e, id) {
const { users } = this.props;
let user = users.filter(obj => obj.id === id);
user = user.length > 0 ? user[0] : null;
if (user != null) {
this.setState({
displayForm: true,
isCreationMode: false,
user: { id: user.id, fName: user.fName, lName: user.lName },
});
}
}
deleteUser(e, id) {
let { users } = this.props;
users = users.filter(user => user.id !== id);
this.props.dispatch(UserActions.listUsers(users));
}
render() {
console.log(this.state.displayForm);
return (
<div className="container-fluid">
<div className="well">
Sample Users App With Redux
</div>
<UserForm
displayForm={this.state.displayForm}
isCreationMode={this.state.isCreationMode}
submitForm={this.submitForm}
handleChange={this.handleChange}
user={this.state.user}
addNewUser={this.addNewUser}
/>
<UsersList
users={this.props.users}
editUser={this.editUser}
deleteUser={this.deleteUser}
/>
<div className="clearfix">
<Button bsStyle="primary" onClick={this.addNewUser}>Add User</Button>
</div>
</div>
);
}
}
and test file is as below
import React from 'react';
import { createMockStore } from 'redux-test-utils';
import { shallowWithStore } from 'enzyme-redux';
import { Button } from 'react-bootstrap';
import UserComponent from '../../../../src/components/containers/sample/userComponent';
import UserForm from '../../../../src/views/sample/userForm';
import UsersList from '../../../../src/views/sample/usersList';
describe('UsersComponent', () => {
let store;
let container;
const props = {
submitForm: jest.fn(),
addNewUser: jest.fn(),
};
beforeEach(() => {
const defaultState = { sampleUserReducer: { users: [] } };
store = createMockStore(defaultState);
container = shallowWithStore(<UserComponent />, store);
});
it('should work', () => {
expect(true).toEqual(true);
});
it('container should have UserForm component', () => {
expect(container.dive().find(UserForm)).toHaveLength(1);
});
it('container should have UsersList component', () => {
expect(container.dive().find(UsersList)).toHaveLength(1);
});
it('should have add new user button', () => {
expect(container.dive().find(Button)).toHaveLength(1);
expect(container.dive().find(Button).dive().text()).toEqual('Add User');
});
it('On click add user button', () => {
container.dive().find(Button).simulate('click');
expect(props.addNewUser).toHaveBeenCalled();
});
});
I'm using jest, enzyme, enzyme-redux. I'm new to react unit testing. Last test case is giving error as below. React version is 16.x. In last test case I'm trying to call mocked jest function on button click. For button using react-bootstrap inbuilt Button component
expect(jest.fn()).toHaveBeenCalled()
Expected mock function to have been called.
You are likely to need to add container.update(); which forces a re-render after external inputs like clicking.
http://airbnb.io/enzyme/docs/api/ShallowWrapper/update.html
Sometimes container.update() does not work and in such cases, try container.instance().forceUpdate() in your tests after the click which updates the component after the state changes.
Another option would be to use jest's spy to assert that addNewUser was called.
const spy = jest.spyOn(container.instance(), 'addNewUser');
container.dive().find(Button).simulate('click');
expect(spy).toBeCalled();

Categories