Can't Update Store in Redux - reactjs

Unable to figure out the issue as to why my store is not updating. My end goal is to have the "here is the store first name" and "here is the store last name" in the Component.js file display the first and last name of a data block I enter in the reducer (Joe and Smith respectively). Whenever I click the login button after entering proper credentials, the console logs "USERNAME AND PASS MATCH" which tells me that it recognizes that there is a match between what I'm supplying and what is in the data. But, it refuses to update the first_name and last_name of the store for some reason...
In my Component.js file:
import React from "react"
import { connect } from "react-redux"
import { setUsername, setPassword, handleLogin } from "../actions/userActions"
#connect((store) => {
return {
loginShow: store.loginShow,
loggedIn: store.loggedIn,
username: store.username,
password: store.password,
first_name: store.first_name,
last_name: store.last_name,
id: store.id,
invalidAttempt: store.invalidAttempt,
};
})
export default class Layout extends React.Component {
render() {
let loginView = null;
if (this.props.loginShow && this.props.invalidAttempt === false) {
loginView = (
<div className="loginBox">
<h3>Enter username and password</h3>
<input type="text" placeholder="Please enter username..."
onChange={(e) => this.props.dispatch(setUsername(e.target.value))} />
<br />
<input type="password" placeholder="Please enter password..."
onChange={(e) => this.props.dispatch(setPassword(e.target.value))} />
<p>Here is the store first name: {this.props.first_name}</p>
<p>Here is the store last name: {this.props.last_name}</p>
<br />
<button onClick={() => this.props.dispatch(handleLogin(this.props.username, this.props.password))}>Login</button>
</div>
)
}
return (
<div>
{loginView}
</div>
)
}
}
Here is the action file:
export function setUsername(name) {
return {
type: "SET_USERNAME",
payload: name,
}
}
export function setPassword(pword) {
return {
type: "SET_PASSWORD",
payload: pword,
}
}
export function handleLogin(uname, pword) {
return {
type: "HANDLE_LOGIN",
payload: {user: uname, pass: pword}
}
}
Now in my reducer file, I'm pretty sure this is where the issue is:
let data = [
{
"username": "imJoe",
"first_name": "Joe",
"last_name": "Smith",
"password": "password123",
"id": 1
}
]
export default function userReducer(state = {
loginShow: true,
loggedIn: false,
username: '',
password: '',
first_name: '',
last_name: '',
id: '',
invalidAttempt: false,
check: false,
}, action) {
switch (action.type) {
case "SET_USERNAME": {
console.log("SET USERNAME REUCER")
return { ...state, username: action.payload }
}
case "SET_PASSWORD": {
console.log("SET PASS REUCER")
return { ...state, password: action.payload }
}
case "HANDLE_LOGIN": {
console.log("HANDLE LOGIN REDUCER")
data.map(xyz => {
if (xyz.username === action.payload.user && xyz.password === action.payload.pass){
console.log("USERNAME AND PASS MATCH")
return {
...state,
first_name: xyz.first_name,
last_name: xyz.last_name,
}
}
})
}
}
return state
}

Your store not update because in your case "HANDLE LOGIN", you return nothing. From redux docs: reducer always should return new state, but you only map and return nothing.
Here is how "HANDLE_LOGIN" should look like:
case "HANDLE_LOGIN": {
console.log("HANDLE LOGIN REDUCER")
let firstName = null;
let lastName = null;
data.map(xyz => {
if (xyz.username === action.payload.user && xyz.password === action.payload.pass){
console.log("USERNAME AND PASS MATCH")
firstName = xyz.first_name;
lastName = xyz.last_name;
}
});
return {
...state,
first_name: firstName,
last_name: lastName,
}
}
So you reducer reŠµurns data, even data items is null your reducer returns items with null.

Related

MERN Authentication: React - Redux - Formik signup/login form does not wait for dispatch return to update and show server error after submit

I have this Formik form for authentication which can switch from login (by default) to signup form using switch button. Using Formik and yup validation schema, the form first check the fields' values on change and before submitting. On submit, I am trying to have another value check from the server side and to show error messages on top of each field (at the same place as the client side error messages provided by Formik and Yup). When the 'submit' button is clicked and in case of a server error after validation, the state of my Redux 'authReducer' is updated with 'errors'' object inside 'data' property. Through local state, then I create a variable 'errorServer' which is an object created to store for each input (as key), the related server error's message (as value). Then, using this object, I'm trying to show the error message(s) on top of field(s) concerned (I tried both Formik methods 'setErrors' and 'setStatus' to do so). But, in the best case, this error message is only appearing after a second click on the 'submit' button and I don't know how to solve this problem... Can anyone provide me some help? Many thanks!
Server side
auth.controller.js
const { findUserPerUsername, findUserPerEmail} = require('../queries/users.queries');
const { createUser } = require('../queries/users.queries');
const { check, validationResult } = require('express-validator');
// Field value validation before signup
exports.validate = (method) => {
switch (method) {
case 'signup': {
return [
check('name')
.exists()
.withMessage('Enter your name.'),
check('username')
.exists()
.withMessage('Enter a username.')
.isAlphanumeric()
.withMessage('Username must contain only letters and numbers.')
.custom(value => {
return findUserPerUsername(value).then(user => {
if (user) {
return Promise.reject('Username already in use.');
}
})
}),
check('email')
.exists()
.withMessage('Enter an email address.')
.isEmail()
.withMessage('A valid email address is required.')
.custom(value => {
return findUserPerEmail(value).then(user => {
if (user) {
return Promise.reject('Email address already in use.');
}
})
}),
check('password')
.exists()
.withMessage('Enter a password.')
.custom(value => {
const pswRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[##$!%*?&-])[A-Za-z\d##$!%*?&-]{8,}$/;
if (pswRegex.test(value) === false) {
return Promise.reject('Password must contain 8 Characters, One Uppercase, One Lowercase, One Number and One Special Case Character')
} else {
return Promise.resolve();
}
})
]
}
case 'login': {
return [
check('email')
.exists()
.withMessage('Enter an email address.')
.isEmail()
.withMessage('A valid email address is required'),
check('password')
.exists()
.withMessage('Enter a password.')
.custom(value => {
const pswRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[##$!%*?&-])[A-Za-z\d##$!%*?&-]{8,}$/;
if (pswRegex.test(value) === false) {
return Promise.reject('Password must contain 8 Characters, One Uppercase, One Lowercase, One Number and One Special Case Character')
} else {
return Promise.resolve();
}
})
]
}
}
}
exports.signup = async (req, res, next) => {
try {
const body = req.body;
const errors = validationResult(req);
if (!errors.isEmpty()) {
res.status(400).send({
success: false,
errors: errors.array()
});
next();
} else {
const user = await createUser(body);
req.login(user);
res.status(200).send({
user: user,
success: true,
message: 'User successfully registered.'
})
next();
}
} catch(e) {
next(e);
}
}
exports.login = async (req, res, next) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
res.status(400).send({
success: false,
errors: errors.array()
});
next();
} else {
const { email, password } = req.body;
const user = await findUserPerEmail(email);
if (user) {
const match = await user.comparePassword(password);
if (match) {
req.login(user);
res.status(200).send({
user: user,
success: true,
message: 'User successfully logged in.'
});
next();
} else {
res.status(401).send({
success: false,
errors: {
msg: 'Sorry, that password is not right. Please try again or reset your password.',
param: 'password',
location: 'body'
}
});
next();
}
} else {
res.status(401).send({
success: false,
errors: {
msg: 'Sorry, we could not find an account with that email. Please try again or sign up.',
param: 'email',
location: 'body'
}
});
next();
}
}
} catch(e) {
next(e);
}
}
exports.logout = (req, res, next) => {
req.logout();
res.status(200).send({
success: true,
message: "User successfully logged out."
})
next();
}
Client side
auth.config.js
import axios from 'axios';
const API_URL = 'http://localhost:3001/api/auth/'
const signup = ({ name, username, email, password }) => {
return axios.post(API_URL + 'signup', {
name,
username,
email,
password
});
};
const login = ({ email, password }) => {
return axios
.post(API_URL + 'login', {
email,
password
})
.then((res) => {
return res.data;
});
};
const logout = () => {
return axios.get(API_URL + 'logout').then((res) => {
return res.data;
});
};
const getCurrentUser = () => {
/* return JSON.parse(localStorage.getItem('user')); */
}
const AuthConfig = {
signup,
login,
logout,
getCurrentUser
}
export default AuthConfig;
Redux store:
actions.js
import AuthConfig from '../config/auth.config';
export const SIGNUP_SUBMIT = "signup submit";
export const SIGNUP_SUCCESS = "signup success";
export const SIGNUP_ERROR = "signup error";
export const LOGIN_SUBMIT = "login submit";
export const LOGIN_SUCCESS = "login success";
export const LOGIN_ERROR = "login error";
export const LOGOUT = "logout";
export const signup = (name, username, email, password) => (dispatch) => {
return AuthConfig.signup(name, username, email, password).then(
(response) => {
dispatch({
type: SIGNUP_SUCCESS,
payload: response.data
});
},
(error) => {
dispatch({
type: SIGNUP_ERROR,
error: error.response.data
});
}
);
};
export const login = (email, password) => (dispatch) => {
return AuthConfig.login(email, password).then(
(response) => {
dispatch({
type: LOGIN_SUCCESS,
payload: response.data
});
},
(error) => {
dispatch({
type: LOGIN_ERROR,
error: error.response.data
});
}
);
};
export const logout = () => (dispatch) => {
AuthConfig.logout().then(
(response) => {
dispatch({
type: LOGOUT,
payload: response.message
})
}
);
};
reducers.js
import {
SIGNUP_SUBMIT,
SIGNUP_SUCCESS,
SIGNUP_ERROR,
LOGIN_SUBMIT,
LOGIN_SUCCESS,
LOGIN_ERROR,
LOGOUT
} from "./actions";
const initialState = { isLoggedIn: false, data: {} }
export const authReducer = (state = initialState, action) => {
const { type, payload, error } = action;
switch (type) {
case SIGNUP_SUBMIT:
return {
...state,
isLoggedIn: false,
};
case SIGNUP_SUCCESS:
return {
...state,
isLoggedIn: true,
data: payload
};
case SIGNUP_ERROR:
return {
...state,
isLoggedIn: false,
data: error
};
case LOGIN_SUBMIT:
return {
...state,
isLoggedIn: false,
};
case LOGIN_SUCCESS:
return {
...state,
isLoggedIn: true,
data: payload
};
case LOGIN_ERROR:
return {
...state,
isLoggedIn: false,
data: error
};
case LOGOUT:
return {
...state,
isLoggedIn: false,
data: null
};
default: {
return state;
}
}
};
export default authReducer;
Form.js
import React, { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import { Formik, useFormikContext } from 'formik';
import * as Yup from 'yup';
/* import { SignupSchema, LoginSchema /* YupSchema * } from './YupSchema'; */
import Input from './Input';
import { signup, login } from '../../store/actions';
import { Eye, EyeSlash, Lock, MedalStar, Personalcard, Sms, User } from 'iconsax-react';
import './Form.css';
const Logger = () => {
const formik = useFormikContext();
useEffect(() => {
console.group("Formik State");
console.log("status", formik.status);
console.log("values", formik.values);
console.log("errors", formik.errors);
console.log("touched", formik.touched);
console.log("isSubmitting", formik.isSubmitting);
console.log("isValidating", formik.isValidating);
console.log("submitCount", formik.submitCount);
console.groupEnd();
}, [
formik.status,
formik.values,
formik.errors,
formik.touched,
formik.isSubmitting,
formik.isValidating,
formik.submitCount
]);
return null;
};
const ServerValidation = () => {
const { values, /* submitForm, */ } = useFormikContext();
useEffect(() => {
if (values.username === 'JDope99' || values.email === 'jdoe#gmail.com') {
/* submitForm(); */
/* console.log('Username or email already in use.'); */
/* alert(JSON.stringify('Username or email already in use.')); */
}
}, [values]);
return null;
};
const Form = () => {
const authServerErrors = useSelector(state => state.authReducer.data.errors);
console.log(authServerErrors);
const [errorServer, setErrorServer] = useState('');
console.log(errorServer);
const [isSignupForm, setIsSignupForm] = useState(false);
const [showPassword, setShowPassword] = useState(false);
const handleShowPassword = () => setShowPassword(!showPassword);
useEffect(() => {
if (authServerErrors === undefined) {
return
} else {
console.log(authServerErrors);
if (Array.isArray(authServerErrors)) {
let fields = authServerErrors.map(el => el.param);
let msgs = authServerErrors.map(el => el.msg);
if (fields !== [] && msgs !== []) {
let errorObj;
fields.reduce(function(obj, key, index) {
obj[key] = msgs[index]
return errorObj = obj
}, {});
setErrorServer(errorObj);
};
} else {
let field = authServerErrors.param;
let msg = authServerErrors.msg;
let error = {};
error[field] = msg;
setErrorServer(error);
}
}
}, [authServerErrors]);
const switchForm = () => {
setIsSignupForm(!isSignupForm);
setShowPassword(false);
}
const initialValues = {
name: '',
username: '',
email: '',
password: ''
}
const dispatch = useDispatch();
const navigate = useNavigate(); // Will be used to redirect user in case of no server error to /Home
const YupSchema = Yup.object().shape({
name: Yup.string().when('isSignupForm', {
is: true,
then: Yup
.string()
.min(2, 'Too short')
.max(20, 'Too long')
.required('Required'),
otherwise: Yup.string().min(2, 'Too short').max(20, 'Too long')
}),
username: Yup.string().when('isSignupForm', {
is: true,
then: Yup
.string()
.min(2, 'Too short')
.max(20, 'Too long')
.required('Required'),
otherwise: Yup.string().min(2, 'Too short').max(20, 'Too long')
}),
email: Yup
.string()
.email('Valid email required')
.required('Required'),
password: Yup
.string()
.matches(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!##\$%\^&\*])(?=.{8,})/,
"Must Contain 8 Characters, One Uppercase, One Lowercase, One Number and One Special Case Character"
)
.required('Required')
})
return (
<div className="container">
<Formik
initialValues={ initialValues }
validationSchema= { YupSchema }
onSubmit={async (values, actions) => {
try {
if (isSignupForm) {
await dispatch(signup(values));
/* errors.setErrors({
name: errorServer.name,
username: errorServer.username,
email: errorServer.email,
password: errorServer.password
})
console.log(errors); */
} else {
await dispatch(login(values));
}
actions.setStatus({
name: errorServer.name || '',
username: errorServer.username || '',
email: errorServer.email || '',
password: errorServer.password || ''
});
} catch (error) {
console.log(error);
}
}}
>
{({ handleChange, handleBlur, handleReset, handleSubmit, values, errors, status, isValid, dirty }) => (
<form
id="auth-form"
className="d-flex flex-column justify-content-start align-items-start py-3"
onChange={ handleChange }
onBlur={ handleBlur }
onReset={ handleReset }
onSubmit= { handleSubmit }
>
<Logger />
{ isSignupForm ? (
<>
<h2>Sign Up</h2>
<p className="text-lg">Welcome to our website! We hope you will enjoy being part of our community.</p>
<div className="input-box mb-3 w-100">
<Input
label="Name"
type="text"
name="name"
className="form-control"
helpText="Please enter your full name."
icon={<Personalcard size="20" color="#666664" variant="Bold" /* className="input-icon" */ />}
errors={errors.name}
value={values.name}
status={ status !== undefined ? status.name : ''}
/>
</div>
<div className="input-box mb-3 w-100">
<Input
label="Username"
type="text"
name="username"
className="form-control"
helpText="Must contain only letters and numbers"
icon={<User size="20" color="#666664" variant="Bold" /* className="input-icon" */ />}
errors={errors.username}
value={values.username}
status={ status !== undefined ? status.username : ''}
/>
</div>
</>
) : (
<>
<h2>Log In</h2>
<p className="text-lg">We are pleased to have you back.</p>
</>
)}
<div className="input-box mb-3 w-100">
<Input
label="Email"
type="text"
name="email"
className="form-control"
helpText="example#email.com"
icon={<Sms size="20" color="#666664" variant="Bold" /* className="input-icon" */ />}
errors={errors.email}
value={values.email}
status={ status !== undefined ? status.email : ''}
/>
</div>
<div className="input-box mb-3 w-100">
<Input
label="Password"
type={ showPassword ? "text" : "password"}
name="password"
/* className="form-control" */
helpText="Must Contain 8 Characters, One Uppercase, One Lowercase, One Number and One Special Case Character"
icon={<Lock size="20" color="#666664" variant="Bold" /* className="input-icon" */ />}
toggle={ showPassword ? <Eye size="20" color="#000000" className="toggle-icon" onClick={ handleShowPassword } /> : <EyeSlash size="20" color="#000000" className="toggle-icon" onClick={ handleShowPassword } /> }
errors={errors.password}
value={values.password}
status={ status !== undefined ? status.password : ''}
/>
</div>
{ isSignupForm ? (
<div className="mt-1">
<p>By clicking "Sign Up" you agree to our Customer Terms and Privacy Policy.</p>
</div>
) : null}
<div className="d-flex flex-row mt-1">
<button type="submit" className="btn btn-green me-3" disabled={isSignupForm ? !(isValid && dirty) : null }>
{ isSignupForm ? 'Sign Up' : 'Log In' }
</button>
<button type="reset" className="btn btn-outline-black">
Reset
</button>
</div>
<div className="mt-2">
{ isSignupForm ? (
<p>Already have an account? <button type="button" className="link" onClick={() => { switchForm(); handleReset() } }>Log In</button></p>
)
:
(
<p>New to InkIt? <button type="button" className="link" onClick={() => { switchForm(); handleReset() } }>Sign Up</button></p>
)
}
</div>
<ServerValidation />
</form>
)}
</Formik>
</div>
);
};
export default Form;
Input.js
import React, { useState } from 'react';
import { useField } from 'formik';
import { TickSquare } from 'iconsax-react';
const Input = ({ label, helpText, icon, toggle, errors, status, ...props}) => {
const [field, meta, helpers] = useField(props);
console.log({ field, meta, helpers });
// Show inline feedback if EITHER
// - the input is focused AND value is longer than 2 characters
// - or, the has been visited (touched === true)
const [didFocus, setDidFocus] = useState(false);
const handleFocus = () => setDidFocus(true);
const showInputFeedback = (!!didFocus && field.value.trim().length > 2) || meta.touched;
return (
<div className={`form-control px-3 py-2 ${showInputFeedback ? (meta.error ? 'invalid' : 'valid') : ""}`}>
<div className="label-box d-flex flex-row justify-content-between align-items-center">
<label htmlFor={props.name}>{label}</label>
{showInputFeedback ? (
<div className="feedback text-sm">
{ meta.error ? meta.error : status ? status : <TickSquare size="20" color="#06d6a0" variant="Bold" />}
</div>
)
: null}
</div>
<div className="d-flex flex-row flex-nowrap">
<span className="input-icon icon-left">
{icon}
</span>
<input className={ toggle ? "form-control input-password" : "form-control" } autoComplete="off" onFocus={ handleFocus } {...field} {...props} />
{toggle ? (
<span className="input-icon icon-right">
{toggle}
</span>
)
: null
}
</div>
<div className="help-text text-xs">
{helpText}
</div>
</div>
);
}
export default Input;
Chrome console.log:
enter image description here

How to cause a component to re render in react when using a hook which holds some state

I am trying to re render a component when a prop changes. My component uses a custom hook which holds the state of a form. In useEffect whenever the prop called refresh changes I want to re render the page. I have tried countless solutions including making a forceUpdate function and calling it when the prop changes, I tried changing state so that the component should re render, but the information in my for does not clear. Below is my code.
Component:
const CustomerInformationForm = (props) => {
const [triggerRefresh, setTriggerRefresh] = useState();
const initialState = {
name: {
value: "",
isValid: false,
},
phone: {
value: "",
isValid: false,
},
address: {
value: "",
isValid: true,
},
};
const initialValidity = false;
useEffect(() => {
console.log("customerinfo refreshing");
setTriggerRefresh(props.refresh);
}, [props.refresh]);
let [formState, inputHandler] = useForm(
initialState,
initialValidity
);
return (
<div>
<h3>Customer Information</h3>
<form className="customer-information_form">
<Input
id="name"
label="Name"
type="text"
element="input"
validators={[VALIDATOR_REQUIRE()]}
errorText="Please enter a valid name."
onInput={inputHandler}
onChange={props.customerInfo(formState)}
/>
<Input
type="text"
element="input"
id="phone"
label="Phone"
validators={[VALIDATOR_MINLENGTH(8)]}
errorText="Please enter a valid phone number."
onInput={inputHandler}
onChange={props.customerInfo(formState)}
/>
<Input
type="text"
element="input"
id="address"
label="Address"
validators={[]}
onInput={inputHandler}
onChange={props.customerInfo(formState)}
/>
</form>
</div>
);
};
export default CustomerInformationForm;
Custom hook:
import { useCallback, useReducer } from "react";
const formReducer = (state, action) => {
switch (action.type) {
case "INPUT_CHANGE":
let formIsValid = true;
for (const inputId in state.inputs) {
if (!state.inputs[inputId]) {
continue;
}
if (inputId === action.inputId) {
formIsValid = formIsValid && action.isValid;
} else {
formIsValid = formIsValid && state.inputs[inputId].isValid;
}
}
return {
...state,
inputs: {
...state.inputs,
[action.inputId]: { value: action.value, isValid: action.isValid },
},
isValid: formIsValid,
};
case "SET_DATA":
return {
inputs: action.inputs,
isValid: action.formIsValid,
};
default:
return state;
}
};
export const useForm = (initialInputs, initialValidity) => {
const [formState, dispatch] = useReducer(formReducer, {
inputs: initialInputs,
isValid: initialValidity,
});
const inputHandler = useCallback((id, value, isValid) => {
dispatch({
type: "INPUT_CHANGE",
value: value,
isValid: isValid,
inputId: id,
});
}, []);
const setFormData = useCallback((inputData, formValidity) => {
dispatch({
type: "SET_DATA",
inputs: inputData,
formIsValid: formValidity,
});
}, []);
return [formState, inputHandler, setFormData,];
};
Any solutions as to what I could do to get the form to re render empty??
I think the best way is to use the setFormData method returned by your hook (you have omitted it in your CustomerInformationForm component).
Then you can call it in the form, when some condition is met in an effect:
const initialState = useRef({
name: {
value: "",
isValid: false,
},
phone: {
value: "",
isValid: false,
},
address: {
value: "",
isValid: true,
},
});
let [formState, inputHandler, setFormData] = useForm(
initialState,
initialValidity
);
useEffect(() => {
console.log("customerinfo refreshing");
setTriggerRefresh(props.refresh);
setFormData(initialState.current);
}, [props.refresh]);
You can also store the initialState value with useRef, to avoid re-running the effect unnecessarily.

Filter Search in React JS

Im having trouble filtering data based on my input search... my goal is to search for 'M' and to see a list of all payload data containing 'M' and as I get closer to the exact string 'Matt' only show the payload data for 'Matt' and bacisally be able to search by first_name, last_name or number. Thank you in advance, ANY feedback is appreciated! Ive been stuck on this >_<
Im using a Custom component library and trying to do a basic search & filter, the issue is not with the custom library but with the filter function. It doesnt seem to retrieve the v.first_name value. Im also open to any other sorts of filter libraries / approaches.
I typed in the letter 'M' into the searchbox UI component and got the following outputs for each console log statement
import React, { Component } from 'react';
import { IS_FETCHING_DBUSERS, FETCH_DBUSERS_SUCCESS } from '../../actions/keys';
import { users } from '../../actions/URI';
import { fetchComponent } from '../../actions/index';
import { connect } from 'react-redux';
import _ from 'lodash';
import {
LumosTheme,
Grid, Form, Icon, Container, Loader
} from '#CustomLibrary/core';
class SearchBar extends Component {
state = {
responseData: " ",
handle: true,
query: "",
filterValues: []
};
searchString = this.state.query;
responseData = this.props.Users.data;
componentDidMount() {
this.props.fetchComponent([IS_FETCHING_DBUSERS, FETCH_DBUSERS_SUCCESS], users).then(() => {
return this.setState({
handle: false
})
})
}
handleInputChange = (value) => {
console.log("In HandleInputFunction, Value= ", value) // Output of value is 'M'
this.setState({query:value}, () => {
console.log("In HandleInputFunction, query= ", this.state.query) // Output of query is 'M'
this.filterArray();
}
)
}
filterArray = () => {
console.log('In Filter fxn')
let searchString = this.state.query;
let responseData = this.props.Users.data;
console.log('This is the responseData in Filter: ', responseData); // output of responseData is '(6)[{...},{...},{...},{...},{...},{...}]'
console.log('This is my filterValues in Filter: ', this.state.filterValues); //output of filterValues is '[]'
console.log('This is my searchString in Filter: ', searchString) //output of searchString is 'M'
if(searchString.length > 0){
const filterData = _.filter(this.state.responseData, (v) => v.first_name === searchString);
console.log('This is my filterData in loop: ',filterData) //output of filterData is '[]'
this.setState({
filterValues : filterData
})
console.log('This is my filterValues in loop: ', this.state.filterValues) //output of filterValues is '[]'
}
}
// for now this drop down 'searchByOptions' is hard coded and just here for UI visual purposes, what I want to do later is depending on the option the user choses I pass the correct payload.
searchByOptions = [
{ label: 'Name or number', value: 'NAME/number' },
{ label: 'Distribution List', value: 'DL' },
{ label: 'Database Schema or Table', value: 'DB' },
{ label: 'Role', value: 'Role' }
];
render() {
if (this.state.handle) {
return <Loader />
}
else {
return (
<LumosTheme>
<Container width='1379px'>
</Container>
<Container width='1379px'>
<Grid paddingTop='10px'>
<Form.Item width='380px'>
<Form.Dropdown
options={this.searchByOptions}
defaultOption={this.searchByOptions[0]}
/>
</Form.Item>
</Grid>
<Grid flexWrap="wrap" width='1000px'>
< Form.SearchBox placeholder='Search' icon={<Icon.SearchRounded />}
userSelectOnClick
openOnClick
onSearch={this.handleInputChange}
value={this.state.query}
>
<Form.SearchList >
{this.state.responseData ?
this.state.filterValues.map(item => (
<Form.SearchOption
value={item.first_name}
/>
)):'null'}
</Form.SearchList>
</ Form.SearchBox>
</Grid>
</Container>
</LumosTheme>
)
}
}
}
const mapStateToProps = state => {
return {
Users: state.users
}
}
export default connect(mapStateToProps, { fetchComponent })(SearchBar);
my payload is being fetched correctly and looks like so
0: {id:1, first_name: "Matt", last_name: "Jones", number:"123",}
1: {id:2, first_name: "Alex", last_name: "Lee", number:"675",}
2: {id:3, first_name: "Adam", last_name: "Khan", number:"733",}
3: {id:4, first_name: "Sue", last_name: "Kid", number:"248",}
4: {id:5, first_name: "Jade", last_name: "Smith", number:"907",}
5: {id:6, first_name: "Luca", last_name: "James", number:"125",}
It looks like you are doing an exact match with that filter condition.
You can use _.filter(this.state.responseData, (v) => v.first_name.includes(searchString));.
Pro tip: once you go lodash, you never go back... That didn't rhyme, but you get the point.

React-native TypeError: Cannot read property 'data' of undefined

I have propblems in my project
enter image description here
D:\React-Native\Expo\mobile\node_modules\expo\build\logs\RemoteConsole.js:80 Possible Unhandled Promise Rejection (id: 0):
TypeError: Cannot read property 'data' of undefined
TypeError: Cannot read property 'data' of undefined
SignUp.js
import React from 'react';
import {ScrollView, KeyboardAvoidingView, CheckBox, View, Text, Platform,} from 'react-native';
import styles from './styles/authStyles';
import ScreenTitle from '../components/ScreenTitle';
import Input from '../components/input';
import Button from '../components/Button';
import axios from '../config/axios';
import { SINGNUP_URL } from '../config/urls';
import Loader from '../components/Loader';
import Alert from '../components/Alert';
export default class SignUpScreen extends React.Component
{
constructor(props)
{
super(props);
this.state =
{
name: "",
email: "",
password: "",
specialization: "",
phone: "",
address: "",
workingHours: "",
userType: false,
location: null,
isLoading: false,
alert:
{
messages: null,
type: "",
}
}
}
componentDidUpdate()
{
if(this.state.alert.messages)
{
setTimeout(() =>
{
this.setState({alert: {messages: null}})
}, 3000)
}
}
componentWillUnMount()
{
clearTimeout();
}
changeNameHandler = (value) =>
{
this.setState({name: value})
};
changeEmailHandler = (value) =>
{
this.setState({email: value})
};
changePasswordHandler= (value) =>
{
this.setState({password: value})
};
changeSpecializationHandler = (value) =>
{
this.setState({specialization: value})
};
changePhoneHandler = (value) =>
{
this.setState({phone: value})
};
changeAddressHandler = (value) =>
{
this.setState({address: value })
};
changeWorkingHoursHandler = (value) =>
{
this.setState({workingHours: value})
};
changeUserTypeHandler = () =>
{
this.setState({ userType: !this.state.userType})
}
validate()
{
const {name, email, password, specialization, address, phone, workingHours, userType} = this.state;
let validationErrors = [];
let passed = true;
if(!name)
{
validationErrors.push(" Please put your name");
passed= false;
}
if(!email)
{
validationErrors.push(" Please put your email");
passed= false;
}
if(!password)
{
validationErrors.push(" Please put your password");
passed= false;
}
if(userType)
{
if(!specialization)
{
validationErrors.push(" Please put your specialization");
passed= false;
}
if(!address)
{
validationErrors.push(" Please put your address");
passed= false;
}
if(!phone)
{
validationErrors.push(" Please put your phone nuber");
passed= false;
}
if(!workingHours)
{
validationErrors.push(" Please put your working hours");
passed= false;
}
}
if(validationErrors.length > 0)
{
this.setState({alert: {messages: validationErrors, type: "danger"}})
}
return passed
}
_SignUp = async () =>
{
if(!this.validate()) return;
this.setState({isLoading: true})
const { name, email, password, specialization, address, phone, workingHours, userType } = this.state;
const body =
{
name,
email,
password,
specialization,
address,
phone,
workingHours,
userType: userType ? "doctor" : 'normal',
location:
{
latitude: 1,
longitude: 2
},
isLoading: false
}
try
{
const response = await axios.post(SINGNUP_URL, body);
this.setState({
name:'',
email: '',
password: '',
specialization: '',
address: '',
phone: '',
workingHours: '',
userType: false,
location: null
});
this.props.navigation.navigate("SignIn",
{
alert:{messages:"Your account has been successfully registered", type:"success"}
});
}
catch(e){
this.setState({
alert: { messages: e.response.data.message, type: "danger"},
isLoading: false
});
}
}
render()
{
const { name, email, password, specialization, address, phone, workingHours, userType, isLoading, alert } = this.state;
return(
<ScrollView contentContainerStyle={{paddingVertical: 40}}>
<Loader title="A new account is creating" loading={isLoading} />
<Alert messages={alert.messages} type={alert.type} />
<View style={styles.container}>
<ScreenTitle
title="Create new account"
icon="md-person-add"
/>
<KeyboardAvoidingView behavior="padding" enabled >
<Input
placeholder="Name"
onChangeText={this.changeNameHandler}
value={name} />
<Input
placeholder="Email"
onChangeText={this.changeEmailHandler}
value={email} />
<Input
placeholder="Password"
secureTextEntry
onChangeText={this.changePasswordHandler}
value={password} />
<View
style={styles.checkBoxContainer}
>
<CheckBox
style={styles.checkBoxLabel}
value={userType}
onChange={this.changeUserTypeHandler} />
<Text style={styles.checkBoxLabel} >Doctors</Text>
</View>
{userType && (
<React.Fragment>
<Input
onChangeText={this.changeSpecializationHandler}
placeholder="Specialization" value = {specialization} />
<Input
onChangeText={this.changeWorkingHoursHandler}
placeholder="Hours work" value={workingHours} />
<Input
onChangeText={this.changeAddressHandler}
placeholder="Address" value={address} />
<Input
onChangeText={this.changePhoneHandler}
placeholder="Phone Number" value={phone} />
</React.Fragment>
)}
<Button
text="New Account"
onPress={this._SignUp}
/>
<View style={{height: 30}} />
{/* <Button text="Home" onPress={() => this.props.navigation.navigate("Main")} /> */}
</KeyboardAvoidingView>
</View>
</ScrollView>
)
}
}
SignIn.js
import React, { Component } from 'react'
import { Text, View, KeyboardAvoidingView, AsyncStorage } from 'react-native'
import styles from './styles/authStyles';
import ScreenTitle from '../components/ScreenTitle';
import Input from '../components/input';
import Button from '../components/Button';
import axios from '../config/axios';
import { SIGNIN_URL } from '../config/urls';
import Loader from '../components/Loader';
import Alert from '../components/Alert';
import Container from '../components/Container';
import { ScrollView } from 'react-native-gesture-handler';
export default class SignIn extends Component {
constructor(props)
{
super(props);
this.state =
{
email: "",
password: "",
isLoading: false,
alert:
{
messages: null,
type: ""
}
}
}
componentDidMount()
{
const alert = this.props.navigation.getParam('alert');
if(alert)
{
this.setState({alert});
}
}
componentDidUpdate()
{
if(this.state.alert.messages)
{
setTimeout(() =>
{
this.setState({alert: {messages: null}})
}, 3000)
}
}
componentWillUnMount()
{
clearTimeout();
}
changeEmailHandler = (value) =>
{
this.setState({email: value})
};
changePasswordHandler= (value) =>
{
this.setState({password: value})
};
validate()
{
const { email, password } = this.state;
let validationErrors = [];
let passed = true;
if(!email)
{
validationErrors.push(" Please put your email");
passed= false;
}
if(!password)
{
validationErrors.push(" Please put your password");
passed= false;
}
if(validationErrors.length > 0)
{
this.setState({alert: {messages: validationErrors, type: "danger"}})
}
return passed
}
_SignIn= async () => {
if(!this.validate()) return;
this.setState({ isLoading: true });
const body = {
email: this.state.email,
password: this.state.password
};
try {
const response = await axios.post(SIGNIN_URL, body);
this.setState({ email: "", password: "", isLoading: false });
AsyncStorage.setItem("accessToken", response.data.accessToken);
} catch (e) {
this.setState({
alert: { messages: e.response.data.message, type: "danger" },
isLoading: false
});
}
}
render() {
const {email, password, isLoading, alert} = this.state;
return (
<Container>
<Alert messages={alert.messages} type={alert.type} />
<Loader title="Login" loading={isLoading} />
<ScrollView
keyboardShouldPersistTaps="handled"
contentContainerStyle={styles.container} >
<ScreenTitle title="Login" icon="md-log-in" />
<KeyboardAvoidingView behavior="padding" enabled>
<Input
onChangeText={this.changeEmailHandler}
value={email}
placeholder="Email" />
<Input
onChangeText={this.changePasswordHandler}
value={password}
secureTextEntry
placeholder="Password" />
</KeyboardAvoidingView>
<Button
text="Login"
onPress={this._SignIn}
/>
<View style={{height: 30}} />
{/* <Button text="Home" onPress={() => this.props.navigation.navigate("Main")} /> */}
</ScrollView>
</Container>
)
}
}
Try changing
catch (e) {
this.setState({
alert: { messages: e.response.data.message, type: "danger" },
isLoading: false
});
}
To
catch (e) {
let {response: {data: {message = ''} = {}} = {}} = e
this.setState({
alert: { messages: message, type: "danger" },
isLoading: false
});
}
And before even doing that please check the value of e using console.log and make sure message exist in the nested object.

reset uncontrolled component content after submitting the form + react + jsx

I have a already built React form, which works fine expect reset functionality. It has used uncontrolled components bit different way. I am looking for what I need to follow, change the code to achieve my goal.
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
import * as React from "react";
import WidgetComponent from './../../lib/WidgetComponent';
import jQuery from 'jquery';
import InputListComponent from './components/InputListComponent';
import FormInputComponent from './components/FormInputComponent';
import formTextInputComponent from './components/formTextInputComponent';
import FormInputPasswordComponent from './components/FormInputPasswordComponent';
import FormSelectComponent from './components/FormSelectComponent';
import SubmitBtnListComponent from './components/SubmitBtnListComponent';
import SubmitBtnComponent from './components/SubmitBtnComponent';
import ErrorListBox from './components/ErrorListBox';
import EmailPromptModal from './components/EmailPromptModal';
import formHandlerInterfaceFactory from './lib/formHandlerInterface';
import formValidator from './lib/formValidators';
import {generateJQueryAjaxConfigFromPlaceholders} from './../../lib/GenerateJQueryAjaxConfig';
import './styles/registration-form.scss';
export default class ControlPanelCreateAccounts extends WidgetComponent {
constructor(props) {
super(props);
this.state = {
form: {
firstName: "",
lastName: "",
username: "",
password: "",
confirmPassword: "",
email: "",
phone: "",
accessLevel: -1,
},
userRoles: [],
validInputs: {
firstName: false,
lastName: false,
username: false,
password: false,
confirmPassword: false,
email: false,
phone: false,
accessLevel: false,
},
defaultValue: {
firstName: "",
lastName: "",
username: "",
password: "",
confirmPassword: "",
email: "",
phone: "",
accessLevel: -1,
},
errors: [],
submitService: null,
userRoleService: null,
newUserId: null,
};
this.onFormInputChange = this.onFormInputChange.bind(this);
this.onServicesCalled = this.onServicesCalled.bind(this);
this.onFormSubmit = this.onFormSubmit.bind(this);
this.formElementInvalid = this.formElementInvalid.bind(this);
this.formElementValid = this.formElementValid.bind(this);
this.postWidgetLoad = this.postWidgetLoad.bind(this);
this.createUserRequest = this.createUserRequest.bind(this);
this.createUserRoleRequest = this.createUserRoleRequest.bind(this);
this.onResetReceived = this.onResetReceived.bind(this);
this.onFormInputReset = this.onFormInputReset.bind(this);
}
onResetReceived(){
console.log('onResetReceived');
let newState = Object.assign(this.state.form, {
firstName : "",
});
this.setState(newState);
console.log(this.state.form);
}
postWidgetLoad(widget) {
EmailPromptModal.service = widget.services[2];//??
let submitService = widget.services[1];//1
let userRoleService = widget.services[2];//2
//this.storeState({ submitService });
let newState = Object.assign(this.state, {
submitService: submitService,
userRoleService: userRoleService
});
this.setState(newState);
}
validate() {
console.log('validating pre-submit');
let errors = Object.keys(this.state.validInputs).reduce((accum, current) => {
console.log('state of accumulator is: ');
console.log(accum);
console.log('current iteration is: ' + current);
//console.log('password and reType passwords are : ');
//console.log(this.state.form.password + " :: " + this.state.confirmPassword );
if (this.state.validInputs[current] === false) {
console.log('current input is invalid');
this.validate = this.validate.bind(this);
if (current === 'firstName') {
accum.push('The first name field is either empty or contains invalid characters');
}
if (current === 'lastName') {
accum.push('The last name field is either empty or contains invalid characters');
}
if (current === 'email') {
accum.push('The email field is empty or is not a valid email address');
}
if (current === 'phone') {
accum.push('The phone field is empty or is not a valid phone number');
}
if (current === 'password') {
accum.push('The password field is empty or contain less than 5 characters or contain invalid character');
}
if (current === 'confirmPassword') {
accum.push('The passwords mismatched');
}
if (current === 'accessLevel') {
accum.push('You must select a accessLevel from the drop-down menu.');
}
}
return accum;
}, []);
console.log('final error list looks like: ');
console.log(errors);
this.storeState({
errors: errors
});
return errors.length === 0;
}
onServicesCalled() {
let response = arguments[0][0].data;
let roleList = [];
if (response) {
response.map(element => {
roleList.push({val: element.id, text: element.role_name});
});
}
let newState = Object.assign(this.state, {
userRoles: roleList
});
this.setState(newState);
}
onFormInputChange(ev, legend) {
let value = ev.target.value;
let form = this.state.form;
form[legend] = value;
this.storeState({
form
});
formElementInvalid(elem) {
let name = elem.name;
let stateUpdateObj = this.state.validInputs;
stateUpdateObj[name] = false;
this.storeState({validInputs: stateUpdateObj});
if (name !== 'status') {
elem.classList.add('input-error');
} else {
document.getElementById('status-error-field').classList.add('visible');
}
}
formElementValid(elem) {
let name = elem.name;
let stateUpdateObj = this.state.validInputs;
stateUpdateObj[name] = true;
this.storeState({validInputs: stateUpdateObj});
if (name !== 'status') {
elem.classList.remove('input-error');
} else {
document.getElementById('status-error-field').classList.remove('visible');
}
}
onFormSubmit() {
if (this.validate()) {
console.log("validate got passed");
console.log(this.state.form);
var id;
// match password fields
if (this.state.form.password !== this.state.form.confirmPassword) {
let stateUpdateObj = this.state.validInputs;
stateUpdateObj['confirmPassword'] = false;
this.storeState({validInputs: stateUpdateObj});
this.onFormSubmit();
} else {
jQuery.when(this.createUserRequest()).done((r) => {
console.log("submit for createUser is success reload!");
console.log(r);
id = r.data.id;
jQuery.when(this.createUserRoleRequest(id)).done((r) => {
console.log("submit for createUserRole is success reload!");
console.log(r);
if(r.data.wasSuccessful){
console.log("create UserRole is successful, So reload the table ");
// do page refresh here
window.pubsub.publish('isp.reload-control-users', {});
//reset the form
console.log("reset the form contents");
this.onResetReceived();
}
}).fail((xhr, status, error) => {
console.log(status);
console.error(error);
alert('An error occured while submitting this request, if this issue persists please contact support.');
});
let newState = Object.assign(this.state, {
newUserId: r.data.id
});
this.setState(newState);
}).fail((xhr, status, error) => {
console.log(status);
console.error(error);
alert('An error occured while submitting this request, if this issue persists please contact support.');
});
}
}
}
//create a record in is_auth_roles table
createUserRoleRequest(id) {
let config = generateJQueryAjaxConfigFromPlaceholders(this.state.userRoleService, {'userId': id});
config.headers['Content-Type'] = 'application/json';
config.headers['Accept'] = '*/*';
config.data = JSON.stringify({
'roleId': this.state.form.accessLevel,
'type': 'add'
});
console.log("submit config of createUserRoleRequest() is: ");
console.log(config);
return jQuery.ajax(config);
}
createUserRequest() {
let config = generateJQueryAjaxConfigFromPlaceholders(this.state.submitService, {});
config.headers['Content-Type'] = 'application/json';
config.headers['Accept'] = '*/*';
config.data = JSON.stringify({
firstName: this.state.form.firstName,
lastName: this.state.form.lastName,
username: this.state.form.username,
phone: this.state.form.phone,
password: this.state.form.password,
confirmPassword: this.state.form.confirmPassword,
email: this.state.form.email,
accessLevel: this.state.accessLevel
});
console.log("submit config is: ");
console.log(config);
return jQuery.ajax(config);
}
render() {
return (
<div className="registration-form">
<h3 className="component-title"><i className="fa fa-table" aria-hidden="true"></i> User <span> Registration</span></h3><br/>
<InputListComponent>
{/*onValid={this.formElementValid} />*/}
<formTextInputComponent legend="firstName" label="First Name" placeholder="First Name" onChange={this.onFormInputChange} validator={formValidator.nameValidator} onInvalid={this.formElementInvalid} default = {this.state.defaultValue.firstName} value={ this.state.value}
onValid={this.formElementValid} />
<FormInputComponent legend="lastName" label="Last Name" placeholder="Last Name" onChange={this.onFormInputChange} validator={formValidator.nameValidator} onInvalid={this.formElementInvalid} default = {this.state.defaultValue.lastName}
onValid={this.formElementValid}/>
<FormInputComponent legend="username" label="User Name" placeholder="User Name" onChange={this.onFormInputChange} validator={formValidator.nameValidator} onInvalid={this.formElementInvalid} default = {this.state.defaultValue.username}
onValid={this.formElementValid}/>
<FormInputPasswordComponent legend="password" label="Password" placeholder="Password" onChange={this.onFormInputChange} validator={formValidator.passwordValidator} onInvalid={this.formElementInvalid} default = {this.state.defaultValue.password}
onValid={this.formElementValid}/>
<FormInputPasswordComponent legend="confirmPassword" label="Confirm Password" placeholder="Confirm Password" onChange={this.onFormInputChange} validator={formValidator.passwordValidator} default = {this.state.defaultValue.confirmPassword}
onInvalid={this.formElementInvalid} onValid={this.formElementValid}/>
<FormInputComponent legend="email" label="Email Address" placeholder="Email" onChange={this.onFormInputChange} validator={formValidator.emailValidator} onInvalid={this.formElementInvalid} default = {this.state.defaultValue.email}
onValid={this.formElementValid}/>
<FormInputComponent legend="phone" label="Phone Number" placeholder="Phone Number" onChange={this.onFormInputChange} validator={formValidator.phoneValidator} onInvalid={this.formElementInvalid} default = {this.state.defaultValue.phone}
onValid={this.formElementValid}/>
<FormSelectComponent legend="accessLevel" data={this.state.userRoles} onChange={this.onFormInputChange} validator={formValidator.statusValidator} onInvalid={this.formElementInvalid} default = {this.state.defaultValue.accessLevel}
onValid={this.formElementValid}/>
</InputListComponent>
<ErrorListBox errorList={this.state.errors}/>
<button className="dataTable-modal-btn click-btn" onClick={this.onFormSubmit}>CREATE USER</button>
</div>
);
}
}
window.ControlPanelCreateAccounts = ControlPanelCreateAccounts;
window.CurrentWidget = window.ControlPanelCreateAccounts;
this is formInputComponent code
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
const FormInputComponent = (props) => {
const onChangeHandler = (ev) => {
if(props.validator(ev.target.value)) {
props.onChange(ev, props.legend);
props.onValid(ev.target);
} else {
props.onInvalid(ev.target);
}
};
console.log('props.defaultValue');
console.log(props.legend);
console.log(props.default);
console.log(props.value);
return (
<div className="form-row">
<label>{props.label}</label><br />
<input name={props.legend} type="text" placeholder={props.placeholder} onChange={onChangeHandler} defaultValue={props.default || ''} value={props.value}/>
</div>
);
};
FormInputComponent.propTypes = {
legend: React.PropTypes.string,
label: React.PropTypes.string,
placeholder: React.PropTypes.string,
onChange: React.PropTypes.func,
validator: React.PropTypes.func,
onInvalid: React.PropTypes.func,
onValid: React.PropTypes.func,
default: React.PropTypes.string,
};
export default FormInputComponent;
If you want setState inside of onResetReceived to reset the form, you'll need to set the value prop of each form field. The default prop gets sent to FormInputComponent as the defaultValue of the input, but you can also see it accepts a value={props.value} as well (even though value is not defined in its PropTypes. From the parent set
<FormInputComponent value={this.state.form.firstName} ... />
This will ensure changing this.state.form via setState will update each form field.

Resources