why useReducer is returning undefined? - javascript

I am using useReducer to mange my states in a form and everything is working perfectly fine until I submit the fine on form submission I am logging all my state values in the console but it returns all values as undefined as null except for the last one
Console Output:
{product_id: undefined, product_name: undefined, produuct_price: '55'}
product_id: undefined
product_name: undefined
produuct_price: "55"
[[Prototype]]: Object
I dont know why its happening because all state binding is working perfectly fine and if there is some problem why it is returning last value correctly:
import "./ProductForm.css";
import { useReducer } from "react";
const ProductForm = () => {
const initialState = {
product_id: "",
product_name: "",
product_quantity: 0,
product_description: "",
product_type: "",
product_valid: "false",
product_price: 0,
product_title: "",
product_image: "",
};
const reducer = (state, action) => {
if (action.type === "id") {
return { product_id: action.value };
} else if (action.type === "name") {
return { product_name: action.value };
} else if (action.type === "title") {
return { product_title: action.value };
} else if (action.type === "price") {
return { product_price: action.value };
} else if (action.type === "image") {
return { product_image: action.value };
} else if (action.type === "description") {
return { product_discription: action.value };
} else if (action.type === "type") {
return { product_type: action.value };
}
};
const [state, dispatch] = useReducer(reducer, initialState);
const submitHandler = (e) => {
e.preventDefault();
const obj = {
product_id: state.product_id,
product_name: state.product_name,
produuct_price: state.product_price,
};
};
return (
<form onSubmit={submitHandler}>
<div className="form-group">
<label>Product id</label>
<input
type="text"
className="form-control"
value={state.product_id}
onChange={(e) => dispatch({ type: "id", value: e.target.value })}
/>
</div>
<div className="form-group">
<label>Product Name</label>
<input
type="text"
className="form-control"
value={state.product_name}
onChange={(e) => dispatch({ type: "name", value: e.target.value })}
/>
</div>
<div className="form-group">
<label>Product Price</label>
<input
type="text"
className="form-control"
value={state.product_price}
onChange={(e) => dispatch({ type: "price", value: e.target.value })}
/>
</div>
<button type="submit" className="btn btn-primary mt-4">
Submit
</button>
</form>
);
};
export default ProductForm;

you have to return other field values too from the state, each time as below
const reducer = (state, action) => {
if (action.type === "id") {
return { ...state, product_id: action.value };
} else if (action.type === "name") {
return { ...state, product_name: action.value };
}
// likewise for all
};
so it returns all the fields in the state with the updated field value, other wise the state will always be the last updated value which in your case is the produuct_price...
you can check by logging the state from your current code and will have is just this below, as it is the last updated value ...
{produuct_price: "55"}

Related

useReducer state change is one character delay at onChange

When I try to use useReducer as state management but the problem arises when I use onChange with the reducer it is one character delay. There is a useEffect that depends on the character changes to turn the validity value true or false.
But it works fine with the onBlur.
What should be the cause of this delay?
How can I solve this issue?
import React, { useState, useEffect, useReducer, useContext } from "react";
import AuthContext from "../store/auth-context";
import Button from "../UI/Button";
import Card from "../UI/Card";
import Input from "../UI/Input";
const reducerFn = (state, action) => {
if (action.type === "Email") {
return {
...state,
emailState: action.value,
};
} else if (action.type === "Email_IsValid") {
return {
...state,
isEmailValid: true,
};
} else if (action.type === "Email_IsInValid") {
return {
...state,
isEmailValid: false,
};
} else if (action.type === "Password") {
return {
...state,
passwordState: action.value,
};
} else if (action.type === "Password_IsValid") {
return {
...state,
isPasswordIsValid: true,
};
} else if (action.type === "Password_IsInValid") {
return {
...state,
isPasswordIsValid: false,
};
}
return { emailState: "", isEmailValid: false, passwordState: "", isPasswordIsValid: false };
};
const Login = () => {
const [formState, dispatchFn] = useReducer(reducerFn, { emailState: "", isEmailValid: false, passwordState: "", isPasswordIsValid: false });
const { emailState, isEmailValid, passwordState, isPasswordIsValid } = formState;
const [isEmailValidated, setIsEmailValidated] = useState(null);
const [isPasswordIsValidated, setIsPasswordIsValidated] = useState(null);
const authCtx = useContext(AuthContext);
useEffect(() => {
if (isEmailValid) setIsEmailValidated(true);
if (isPasswordIsValid) setIsPasswordIsValidated(true);
}, [isEmailValid, isPasswordIsValid]);
const onEmailChangeHandler = ({ target }) => {
console.log(isEmailValid, isEmailValidated);
dispatchFn({ type: "Email", value: target.value });
if (emailState.includes("#")) {
dispatchFn({ type: "Email_IsValid" });
} else {
dispatchFn({ type: "Email_IsInValid" });
setIsEmailValidated(false);
}
};
const onEmailBlurHandler = () => {
if (emailState.includes("#")) {
dispatchFn({ type: "Email_IsValid" });
} else {
dispatchFn({ type: "Email_IsInValid" });
setIsEmailValidated(false);
}
};
const onPasswordChangeHandler = ({ target }) => {
dispatchFn({ type: "Password", value: target.value });
if (passwordState.length > 7) {
dispatchFn({ type: "Password_IsValid" });
} else {
dispatchFn({ type: "Password_IsInValid" });
setIsPasswordIsValidated(false);
}
console.log(isPasswordIsValid);
};
const onPasswordBlurHandler = () => {
if (passwordState.length > 7) {
dispatchFn({ type: "Password_IsValid" });
} else {
dispatchFn({ type: "Password_IsInValid" });
setIsPasswordIsValidated(false);
}
};
const onFormSubmit = (e) => {
e.preventDefault();
if (isEmailValid === false) setIsEmailValidated(false);
else if (isPasswordIsValid === false) setIsPasswordIsValidated(false);
else if (isEmailValid && isPasswordIsValid) authCtx.onLogin(emailState, passwordState);
};
return (
<Card>
<form>
<Input
id="email"
label="E-Mail"
type="email"
onChange={onEmailChangeHandler}
onBlur={onEmailBlurHandler}
value={emailState}
isValidated={isEmailValidated}
warningText="Please enter a valid email; must contains '#'"
/>
<Input
id="password"
label="Password"
type="password"
onChange={onPasswordChangeHandler}
onBlur={onPasswordBlurHandler}
value={passwordState}
isValidated={isPasswordIsValidated}
warningText="Password must have 8 characters long"
/>
<Button label="Login" onClick={onFormSubmit} classes={`bgRed bgWider`}></Button>
</form>
</Card>
);
};
export default Login;
You'd have a much simpler time not using reducers here at all, like so:
import React from "react";
const Login = () => {
const [email, setEmail] = React.useState("");
const [password, setPassword] = React.useState("");
const [shouldValidateEmail, setShouldValidateEmail] = React.useState(false);
const [shouldValidatePassword, setShouldValidatePassword] = React.useState(false);
const emailIsValid = email.includes("#");
const passwordIsValid = password.length > 7;
const errors = [
shouldValidateEmail && !emailIsValid ? "Email invalid" : null,
shouldValidatePassword && !passwordIsValid ? "Password invalid" : null,
]
.filter(Boolean)
.join(", ");
return (
<form>
Email
<input
id="email"
type="email"
onChange={(e) => setEmail(e.target.value)}
onBlur={() => setShouldValidateEmail(true)}
value={email}
/>
<br />
Password
<input
id="password"
type="password"
onChange={(e) => setPassword(e.target.value)}
onBlur={(e) => setShouldValidatePassword(true)}
value={password}
/>
<br />
{errors ? <>Errors: {errors}</> : null}
</form>
);
};
export default function App() {
const [resetKey, setResetKey] = React.useState(0);
return (
<div className="App">
<Login key={resetKey} />
<hr />
<button onClick={() => setResetKey((k) => k + 1)}>Reset</button>
</div>
);
}
However, if you really do want to use a reducer, just couple the validation state to when the state is changed:
function validateEmail(email) {
return email.includes("#");
}
function validatePassword(password) {
return password.length > 7;
}
const initialState = {
emailState: "",
isEmailValid: false,
passwordState: "",
isPasswordValid: false,
};
const reducerFn = (state, action) => {
if (action.type === "Email") {
return {
...state,
emailState: action.value,
isEmailValid: validateEmail(action.value),
};
} else if (action.type === "Password") {
return {
...state,
passwordState: action.value,
isPasswordValid: validatePassword(action.value),
};
}
return initialState;
};
const Login = () => {
const [{ emailState, isEmailValid, passwordState, isPasswordValid }, dispatchFn] = React.useReducer(
reducerFn,
initialState,
);
const errors = [
emailState && !isEmailValid ? "Email invalid" : null,
passwordState && !isPasswordValid ? "Password invalid" : null,
]
.filter(Boolean)
.join(", ");
return (
<form>
Email
<input
id="email"
type="email"
onChange={(e) => dispatchFn({ type: "Email", value: e.target.value })}
value={emailState}
/>
<br />
Password
<input
id="password"
type="password"
onChange={(e) => dispatchFn({ type: "Password", value: e.target.value })}
value={passwordState}
/>
<br />
{errors ? <>Errors: {errors}</> : null}
</form>
);
};

React - using nested objects as state with hooks to fill form data

I have a nested object as state like below -
const [userInfo, setUserInfo] = useState({
author:"",
user: {
name: 'rahul',
email: 'rahul#gmail.com',
phone: [{ primary: '8888888810' }, { alternate: '7777777716' }]
}
});
I want to have 5 input fields - author, name, email, primary, and alternate and want to use only one handleChange() method to change the fields.
You can find the code I wrote on the link - https://stackblitz.com/edit/react-ngpx7q
Here, I am not able to figure out how to update the state correctly. Any help would be greatly appreciated.
Since this was an interview question then I'd avoid 3rd-party libraries. You can use a switch statement to handle the differently nested state, namely the name and email in the second level and primary and alternate in the third level.
const handleChange = (e) => {
const { name, value } = e.target;
switch (name) {
case "name":
case "email":
setUserInfo((userInfo) => ({
user: {
...userInfo.user,
[name]: value
}
}));
break;
case "primary":
case "alternate":
setUserInfo((userInfo) => ({
user: {
...userInfo.user,
phone: userInfo.user.phone.map((el) =>
el.hasOwnProperty(name)
? {
[name]: value
}
: el
)
}
}));
break;
default:
// ignore
}
};
Demo
you can use lodash set to assign the value for the deeply nested object. You need to pass the path to the name prop of your input .
import set from 'lodash/set'
const App = () => {
const [userInfo, setUserInfo] = useState({
author:"",
user: {
name: 'rahul',
email: 'rahul#gmail.com',
phone: [{ primary: '8888888810' }, { alternate: '7777777716' }]
}
});
const handleChange = (e) => {
// clone the state
const userInfoCopy = JSON.parse(JSON.stringify(userInfo));
set(userInfoCopy, e.target.name, e.target.value)
setUserInfo(userInfoCopy)
}
console.log(userInfo)
return (
<div>
<input
name="user.name"
onChange={handleChange}
/>
<input
name="user.phone.[0].primary"
onChange={handleChange}
/>
</div>
);
};
Now you can use a single handleChange method for updating all your keys in the state .
Instead of treating phone as object of array, which i don't think is a good idea, treat it as single object with primary and alternate as key value pairs
import React, { useState } from 'react';
import './style.css';
export default function App() {
const [userInfo, setUserInfo] = useState({
user: {
name: 'ravi',
email: 'ravi#gmail.com',
phone: {
primary: 345345345345,
alternate: 234234234234
}
}
});
const handleChange = e => {
console.log(e.target.name);
setUserInfo(prevState => {
return {
user: {
...prevState.user,
[e.target.name]: e.target.value,
phone: {
...prevState.user.phone,
...{ [e.target.name]: e.target.value }
}
}
};
});
};
const {
name,
email,
phone: { primary, alternate }
} = userInfo.user;
console.log(userInfo);
return (
<div className="App">
Name: <input name="name" value={name} onChange={e => handleChange(e)} />
<br />
Email:{' '}
<input name="email" value={email} onChange={e => handleChange(e)} />
<br />
Primary:{' '}
<input name="primary" value={primary} onChange={e => handleChange(e)} />
<br />
Alternate:{' '}
<input
name="alternate"
value={alternate}
onChange={e => handleChange(e)}
/>
<br />
</div>
);
}
This works based on your original data (where phone is an array of objects):
const handleChange = e => {
let name = e.target.name;
if (['name', 'email'].includes(name)) {
setUserInfo(prevState => {
return {
user: {
...prevState.user,
[name]: e.target.value,
}
};
});
} else {
setUserInfo(prevState => {
return {
user: {
...prevState.user,
phone: name === 'primary' ?
[prevState.user.phone.find(e => Object.keys(e).includes('alternate')), {[name]: e.target.value}] :
[prevState.user.phone.find(e => Object.keys(e).includes('primary')), {[name]: e.target.value}]
}
};
});
}
};
I copy paste your code and only edit your handleChange
import React, { useState } from 'react';
import './style.css';
export default function App() {
const [userInfo, setUserInfo] = useState({
user: {
name: 'ravi',
email: 'ravi#gmail.com',
phone: [{ primary: '9999999990' }, { alternate: '9999998880' }]
}
});
const handleChange = e => {
console.log(e.target.name);
let arrPhone = userInfo.user.phone;
(e.target.name == 'primary' || e.target.name == 'alternate' )
&& arrPhone.map(x => (x.hasOwnProperty(e.target.name)) && (x[e.target.name] = e.target.value))
console.log(arrPhone)
setUserInfo(prevState => {
return {
user: {
...prevState.user,
[e.target.name]: e.target.value,
phone: arrPhone
}
};
});
};
const {
name,
email,
phone: [{ primary }, { alternate }]
} = userInfo.user;
console.log(userInfo);
return (
<div className="App">
Name: <input name="name" value={name} onChange={handleChange} />
<br />
Email: <input name="email" value={email} onChange={handleChange} />
<br />
Primary: <input name="primary" value={primary} onChange={handleChange} />
<br />
Alternate:{' '}
<input name="alternate" value={alternate} onChange={handleChange} />
<br />
</div>
);
}

Unable to get the updated state in ReactJS login page

I am new to React JS and have stuck on a problem. I am building a login page where I want to display some error when the user enters invalid credentials. When I enter the correct credentials I am able to login but when I enter the invalid credentials then also I am able to login. On debugging I have found that in mapPropsToState although I get isLoggedIn parameter as false but it is not mapped to props. props still get true here.
My Login Page:
const required = (value) => {
if (!value) {
return (
<div className="alert alert-danger" role="alert">
This field is required!
</div>
);
}
};
const Login = (props) => {
const form = useRef();
const checkBtn = useRef();
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [loading, setLoading] = useState(false);
const { isLoggedIn } = useSelector(state => state.auth);
const { message } = useSelector(state => state.message);
const dispatch = useDispatch();
const onChangeUsername = (e) => {
const username = e.target.value;
setUsername(username);
};
const onChangePassword = (e) => {
const password = e.target.value;
setPassword(password);
};
const handleLogin = (e) => {
e.preventDefault();
setLoading(true);
form.current.validateAll();
if (checkBtn.current.context._errors.length === 0) {
dispatch(login(username, password))
.then(() => {
if (props !=null && props.isAuthenticated) {
props.history.push("/home");
}
})
.catch(() => {
setLoading(false);
});
} else {
setLoading(false);
}
};
if (isLoggedIn) {
// return <Redirect to="/home" />;
}
return (
<div className="col-md-12">
<div className="card card-container">
<img
src="//ssl.gstatic.com/accounts/ui/avatar_2x.png"
alt="profile-img"
className="profile-img-card"
/>
<Form onSubmit={handleLogin} ref={form}>
<div className="form-group">
<label htmlFor="username">Username</label>
<Input
type="text"
className="form-control"
name="username"
value={username}
onChange={onChangeUsername}
validations={[required]}
/>
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<Input
type="password"
className="form-control"
name="password"
value={password}
onChange={onChangePassword}
validations={[required]}
/>
</div>
<div className="form-group">
<button className="btn btn-primary btn-block" disabled={loading}>
{loading && (
<span className="spinner-border spinner-border-sm"></span>
)}
<span>Login</span>
</button>
</div>
{message && (
<div className="form-group">
<div className="alert alert-danger" role="alert">
{message}
</div>
</div>
)}
<CheckButton style={{ display: "none" }} ref={checkBtn} />
</Form>
</div>
</div>
);
};
Login.propTypes = {
login: PropTypes.func.isRequired,
isAuthenticated: PropTypes.bool.isRequired
}
const mapStateToProps = (state) => ({
isAuthenticated: state.auth.isLoggedIn
})
export default connect(mapStateToProps, { login })(Login);
my login and logout function in action:
export const login = (username, password) => (dispatch) => {
return AuthServicee.login(username, password).then(
(data) => {
if(data.success) {
userService.getUserDetails(username).then((data) => {
localStorage.setItem("user", JSON.stringify(data.data));
dispatch({
type: LOGIN_SUCCESS,
payload: { user: data },
});
return Promise.resolve();
},(error) => {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
dispatch({
type: LOGIN_FAIL,
});
dispatch({
type: SET_MESSAGE,
payload: message,
});
return Promise.reject();
}).catch(err => {
dispatch({
type: LOGIN_FAIL,
});
});;
} else {
dispatch({
type: LOGIN_FAIL,
});
dispatch({
type: SET_MESSAGE,
payload: data.error,
});
}
},
(error) => {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
dispatch({
type: LOGIN_FAIL,
});
dispatch({
type: SET_MESSAGE,
payload: message,
});
return Promise.reject();
}
);
};
export const logout = () => (dispatch) => {
AuthServicee.logout();
dispatch({
type: LOGOUT,
});
};
reducer class:
const user = JSON.parse(localStorage.getItem("user"));
const initialState = user
? { isLoggedIn: true, user }
: { isLoggedIn: false, user: null };
export default function (state = initialState, action) {
const { type, payload } = action;
switch (type) {
case LOGIN_SUCCESS:
return {
...state,
isLoggedIn: true,
user: payload.user,
};
case LOGIN_FAIL:
return {
...state,
isLoggedIn: false,
user: null,
};
case LOGOUT:
return {
...state,
isLoggedIn: false,
user: null,
};
default:
return state;
}
}
can someone please help me here?
Edit: I get correct values in my state variable in mapStateToProps but somehow when I try to use it in then function of my dispatch call, I still get props.isAuthenticated as true. Although it should have become false as I have updated it in mapStateToProps.
If you're being redirected to the /home I would look into your validation functions first, because as you say the pass invalid values. What does this function do if you have invalid inputs form.current.validateAll();?

POST request is empty

I try to send a form values to DB (use redux-thunk, express, and MongoDB). My component looks like
const AddPerson = (props) => {
const [person, setPerson] = useState({
name: '',
age: '',
status: ''
})
const handleChange = (event) => {
setPerson({
...person,
[event.target.name]: event.target.value
})
}
const handleSubmit = (event) => {
props.putCustomersData({ person })
// eslint-disable-next-line no-console
console.log(person)
event.preventDefault()
}
return (
<div>
<Head title="This is new person" />
<form onSubmit={
handleSubmit
}
>
<div>
<div>name</div>
<input
name="name"
type="text"
value={person.name}
onChange={handleChange}
/>
<div>age</div>
<input
type="text"
name="age"
value={person.age}
onChange={handleChange}
/>
<div>status</div>
<input
name="status"
type="text"
value={person.status}
onChange={handleChange}
/>
<div>
<button type="submit">Ok</button>
</div>
</div>
</form>
</div>
);
}
AddPerson.propTypes = {}
AddPerson.defaultProps = {
person: { }
}
const mapStateToProps = state => ({
persons: state.persons.data
})
const mapDispatchToProps = dispatch => bindActionCreators({ putCustomersData }, dispatch)
export default connect(mapStateToProps, mapDispatchToProps)(AddPerson)
and redux
const initialState = {
data: []
}
export default (state = initialState, action) => {
switch (action.type) {
case POST_CUSTOMER:
return {
...state,
data: action.data
}
default:
return state
}
}
export function putCustomersData(person) {
return (dispatch) => {
axios.post('someUrl', {
headers: {
'Content-Type': 'application/json',
},
body: { person }
})
.then((responce) => {
dispatch({
type: POST_CUSTOMER,
data: responce.data
})
// eslint-disable-next-line no-console
console.log('ok', responce.data)
})
.catch((error) => {
dispatch({ type: POST_CUSTOMER_ERROR, error })
// eslint-disable-next-line no-console
console.log('err', error)
})
}
}
And my request writes as empty in DB. console.log(person) show a right object in console:
{name: "Anna", age: "14", status: "student"}
But console.log(responce.data) shows only
{_id: "5e888cb9ca6e5518a5bdf0c2", __v: 0}
I check my requests with Postman and they work. I don`t understand where my problem is. Why object does not write in DB?
The parameters of your axios.post(...) call are in the wrong order.
You have:
axios.post('someUrl', {
headers: {
'Content-Type': 'application/json',
},
body: { person }
})
The correct order is:
axios.post('someUrl', {
{ person },
headers: {
'Content-Type': 'application/json',
}
})
From the docs
axios.post(url[, data[, config]])

Uncaught TypeError: Cannot read property 'then' of undefined React/Redux

I am trying to do a put request using axios to update a given Recipe by the authenticated user in the database basic rest API. But i am having an error in my code saying the 'then' is undefined.
`
// My updateRecipe.jsx Action file
import axios from 'axios';
export const RECIPE_UPDATED = "RECIPE_UPDATED"
const recipeUpdated = (recipe) => {
//console.log(recipe)
return {
type: RECIPE_UPDATED,
recipe
}
}
const updateRecipe = (data) => {
return dispatch => {
// console.log(data)
// console.log(dataid)
axios.put(`http://localhost:8009/api/v1/recipes/${data.id}`, data)
.then(() => dispatch(
//console.log(data),
recipeUpdated(data)
)).catch( error => {
//console.log(error.message)
error} )
}
}
export {
updateRecipe,
recipeUpdated,
}`
This is my Reducer file:
import { SET_RECIPES } from '../action/recipeAction.jsx';
import { RECIPE_FETCH } from '../action/RecipeFetch.jsx'
import { RECIPE_UPDATED } from '../action/updateRecipe.jsx'
export default function recipes(state = [], action = {}) {
switch(action.type) {
case RECIPE_UPDATED:
return state.map(item => {
//console.log(item)
if(item.id === action.recipe.id) return action.recipe;
return item;
})
case RECIPE_FETCH:
const index = state.findIndex(item => item.id === action.recipe.id);
if(index > -1){
return state.map(item => {
if(item.id === action.recipe.id) return action.recipe;
return item;
});
}else{
return [
...state,
action.recipe
];
}
case SET_RECIPES: return action.recipes;
default: return state;
}
}
This is my component file this is where i am having the problem, in my if(id) block the then just underneath the this.props.updatedRecipe.
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { connect } from 'react-redux';
import { Redirect } from 'react-router-dom';
import { Alert } from 'reactstrap';
import { saveRecipe, fetchRecipe, updateRecipe } from
'../../action/index.jsx'
import styles from './style.js'
class RecipeForm extends React.Component {
constructor (props) {
super(props)
this.state = {
id: this.props.recipe ? this.props.recipe.id : null,
title: this.props.recipe ? this.props.recipe.title : '',
description: this.props.recipe ? this.props.recipe.description :
'',
imageUrl: this.props.recipe ? this.props.recipe.imageUrl : '',
errors:{},
loading: false,
done: false,
}
}
componentWillReceiveProps (nextProps){
//console.log(nextProps.recipe)
this.setState({
id: nextProps.recipe.id,
title: nextProps.recipe.title,
description: nextProps.recipe.description,
imageUrl: nextProps.recipe.imageUrl,
})
}
componentDidMount () {
if(this.props.match.params.id){
//console.log(this.props.match.params.id);
this.props.fetchRecipe(this.props.match.params.id)
}
}
handleChange(event) {
// this.setState({ [event.target.name] : event.target.value });
// console.log('updatedRecipe: ' + event.target.id + ' == '+
event.target.value )
if (!!this.state.errors[event.target.name]){
let errors = Object.assign({}, this.state.errors);
delete errors[event.target.name];
// console.log(errors)
this.setState({
[event.target.name]: event.target.value,
errors
})
}else{
//console.log(this.state)
this.setState({
[event.target.name]: event.target.value,
// let handleChange = Object.assign({}, this.state);
// handleChange[event.target.id] = event.target.value;
// this.setState({
// recipes: handleChange,
})
}
}
handleSubmit(e){
e.preventDefault();
let errors = {};
if (this.state.title === '') errors.title = "Can't be empty";
if (this.state.description === '') errors.description = "Can't be
empty";
if (this.state.imageUrl === '') errors.imageUrl = "Can't be empty";
this.setState({
errors
})
const isValid = Object.keys(errors).length === 0
if(isValid){
const { id, title, description, imageUrl } = this.state;
//console.log(this.state)
this.setState({ loading: true });
if(id){
this.props.updateRecipe({ id, title, description, imageUrl })
// this is where the BUG is COMING FROM
.then(
()=>{
console.log("see me")
this.setState({ done : true, loading: true})},
(err) => {
err.response.json()
.then(({errors}) => {
console.log(errors)
this.setState({
errors})
}
)
}
)
}else{
this.props.saveRecipe({ title, description, imageUrl, })
.then(
()=>{
this.setState({ done : true, loading: true})},
(err) => {
err.response.json()
.then(({errors}) => {
console.log(errors)
this.setState({
errors})
}
)
}
)
}
}
}
render() {
const form = (
<div className="d-flex justify-content-center align-items-center
container">
<form className={classnames({ loading: this.state.loading })}>
<h1 style={styles.header}>Add New Recipe</h1>
{!!this.state.errors.form && <Alert color="danger">
{this.state.errors.form }</Alert>}
<div className="form-group row">
<div className="form-group col-md-12 col-md-offset-8 text-
right">
<div className={classnames('field', { error:
!!this.state.errors.title})}>
<label htmlFor="title">Recipe Title</label>
<input
name="title"
value={this.state.title}
onChange={this.handleChange.bind(this)}
className="form-control"
id="title"
placeholder="title"/>
<span style={{color: "#ae5856"}} >{this.state.errors.title}
</span>
</div>
<div className={classnames('field', { error:
!!this.state.errors.description})}>
<label htmlFor="title">Recipe Description</label>
<input
name="description"
value={this.state.description}
onChange={this.handleChange.bind(this)}
className="form-control"
id="description"
placeholder="description"/>
<span style={{color: "#ae5856"}}>
{this.state.errors.description}</span>
</div>
<div className={classnames('field', { error:
!!this.state.errors.imageUrl})}>
<label htmlFor="imageUrl">Recipe Image</label>
<input
name="imageUrl"
value={this.state.imageUrl}
onChange={this.handleChange.bind(this)}
className="form-control"
id="imageUrl"
placeholder="Image"/>
<span style={{color: "#ae5856"}}>{this.state.errors.imageUrl}
</span>
</div>
<div>
{this.state.imageUrl !== '' && <img src=
{this.state.imageUrl} alt="" className="img-rounded"/>}
</div>
<button onClick={this.handleSubmit.bind(this)} type="submit"
className="btn btn-primary">Submit</button>
</div>
</div>
</form>
</div>
);
return (
<div>
{this.state.done ? <Redirect to="/recipes"/> : form}
</div>
)
}
}
RecipeForm.propTypes = {
saveRecipe: PropTypes.func.isRequired,
}
function mapStateToProps(state, props){
//console.log(props.match.recipe)
if(props.match.params.id){
//console.log(props.match.params.id)
let recipe = {}
const recipes = state.recipes.filter(item => {
//console.log(item)
//console.log(state.recipes)
if (item.id == props.match.params.id){
recipe = item
}
})
//console.log(recipe)
return {
recipe
}
//console.log(recipe);
}
return { recipe: null };
}
export default connect(mapStateToProps, { saveRecipe, fetchRecipe })
(RecipeForm);
It should just be updateRecipe, not this.props.updateRecipe. You're importing updateRecipe() (as a function) at the top of the <RecipeForm/> component. So it's not a property that's being passed to the component, it's just a function you've imported in that file.

Categories

Resources