values.map is not a function Sequelize - forms

I'm trying to send a form to my db but I get this error :
The problem XD
I have this problem only when I try to do it from the front, because when I try with Insomnia it works. So I'm not sure where the problem is coming from.
I'm using multer for the image.
The model:
const { DataTypes } = require('sequelize');
// Exportamos una funcion que define el modelo
// Luego le injectamos la conexion a sequelize.
module.exports = (sequelize) => {
// defino el modelo
sequelize.define('Recipe', {
id:{
type: DataTypes.UUID(5),
primaryKey:true,
defaultValue:DataTypes.UUIDV4(5)
},
title: {
type: DataTypes.STRING,
allowNull: false,
},
summary:{
type:DataTypes.STRING,
allowNull: false,
},
healthScore:{
type: DataTypes.INTEGER,
},
steps:{
type:DataTypes.ARRAY(DataTypes.STRING)
},
dishTypes:{
type:DataTypes.ARRAY(DataTypes.STRING)
},
readyInMinutes:{
type: DataTypes.INTEGER,
get(){
return "Ready in " + this.getDataValue("readyInMinutes") + " minutes"
}
},
ingredients:{
type: DataTypes.ARRAY(DataTypes.STRING)
},
servings:{
type:DataTypes.STRING
},
image:{
type:DataTypes.STRING,
defaultValue: `https://post.healthline.com/wp-content/uploads/2020/08/food-still-life-salmon-keto-healthy-eating-732x549-thumbnail-732x549.jpg`
}
},{
timestamps: false
});
};
The post route (actually the helper):
const createNewRecipe = require("../helpers/RecipeCont/CreateRecipe/CreateRecipe")
const createNewRecipeRoute = async (req, res) => {
try {
const data = {
title,
summary,
healthScore,
steps,
dishTypes,
readyInMinutes,
ingredients,
servings,
Diet_type,
} = req.body;
const image = req.file.path
let newRecipe = await createNewRecipe({
title,
summary,
healthScore,
steps,
dishTypes,
readyInMinutes,
ingredients,
servings,
image,
});
await newRecipe.addDiet_type(Diet_type)
console.log(req.file)
res.status(200).json("Receta creada con éxito");
} catch (error) {
console.log(error)
res.status(400).json(error.message);
}
}
module.exports = createNewRecipeRoute;
The form
import React, {useState} from "react";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { createRecipe, getDietTypes } from "../../actions/actions";
import styles from "./form.module.css"
export default function Form(){
const [form,setForm] = useState({
title:"",
summary:"",
healthScore:0,
steps:[],
dishTypes:[],
readyInMinutes:0,
ingredients:[],
servings:0,
image:"",
Diet_type:[1]
})
const [steps, setSteps] = useState("")
const [dishTypes, setDishType]=useState("")
const [ingredients, setIngredients]= useState("")
const dispatch=useDispatch()
useEffect(()=>{
dispatch(getDietTypes())
},[])
const diets = useSelector(state=> state.diet)
const changeHandler=(e)=>{
if(e.target.name==="image"){
setForm({...form,[e.target.name]: e.target.file })
}
setForm({...form, [e.target.name]:e.target.value})
}
const stepHandler = (e)=>{
let aux = e.target.name
let auxV = e.target.value
if(e.key==="Enter"){
e.preventDefault()
setForm({...form, [e.target.name]: [...form[aux] , auxV ]})
aux==="steps"? setSteps("") : aux==="ingredients"? setIngredients("") : setDishType("")
}
console.log(form)
}
const deleteHandler = (e)=>{
let help = e.target.name
e.preventDefault()
let aux = form[help].filter(s=> s!==e.target.value)
setForm({...form, [help]: [...aux]})
}
const imageHandler = (e)=>{
setForm({...form, [e.target.name]:e.target.files[0]})
}
const sendHandler = (e)=>{
e.preventDefault();
if(form.image!==""){
const formData = new FormData()
formData.append("image",form.image)
formData.append("title",form.title)
formData.append("summary",form.summary)
formData.append("healthScore",form.healthScore)
formData.append("steps",form.steps)
formData.append("dishTypes",form.dishTypes)
formData.append("readyInMinutes",form.readyInMinutes)
formData.append("Ingredients",form.ingredients)
formData.append("servings",form.servings)
formData.append("Diet_type",form.Diet_type)
for (var key of formData.entries()) {
console.log(key[0] + ', ' + key[1]);
}
dispatch(createRecipe(formData))
} else {
dispatch(createRecipe(form))
}
console.log(form)
}
return(
<>
<div className={styles.div} >
<h2>Create your own recipe!</h2>
<form encType="multipart/form-data" method="POST" onSubmit={sendHandler}>
<div className={styles.divTitle}>
<h2>Title:</h2>
<input type="text" placeholder="Title" name="title" value={form.title} onChange={changeHandler}></input>
</div>
<input type="text" placeholder="summary" name="summary" value={form.summary} onChange={changeHandler}></input>
{form.healthScore}<input type="range" placeholder="healthScore" name="healthScore" min="1" max="100" step="1" value={form.healthScore} onChange={changeHandler} ></input>
<div>
<input type="text" name="steps" value={steps} onChange={(e)=>{setSteps(e.target.value)}} onKeyDown={stepHandler} placeholder="Steps"></input>
<div>
{form.steps.length>0? form.steps.map(e=><li>{e}<button value={e} name="steps" onClick={deleteHandler}>x</button></li>) : "Add the steps of your recipe!"}
</div>
</div>
<div>
<input type="text" name="ingredients" value={ingredients} onChange={(e)=>{setIngredients(e.target.value)}} onKeyDown={stepHandler} placeholder="Ingredients"></input>
<div>
{form.ingredients.length>0? form.ingredients.map(e=><li>{e}<button value={e} name="ingredients" onClick={deleteHandler}>x</button></li>) : "Add the ingredients of your recipe!"}
</div>
</div>
<div>
<input type="text" name="dishTypes" value={dishTypes} onChange={(e)=>{setDishType(e.target.value)}} onKeyDown={stepHandler} placeholder="Dish types"></input>
<div>
{form.dishTypes.length>0? form.dishTypes.map(e=><li>{e}<button value={e} onClick={deleteHandler}>x</button></li>) : "Add the dish types of your recipe!"}
</div>
</div>
<select>
{console.log(diets)}
{diets?.map(d=><option key={d.id} id={d.id}>{d.name}</option>)}
</select>
<input className={styles.number} name="readyInMinutes" value={form.readyInMinutes} onChange={changeHandler} type="number"></input>
<input className={styles.number} name="servings" value={form.servings} onChange={changeHandler} type="number"></input>
<input type="file" name="image" onChange={imageHandler}></input>
<input type="submit"></input>
</form>
</div>
</>
)
}
I'm still working on the form, in Diet_type for example, but even trying to hardcode the state to make the post it doesn't work.
The "for" is because the console.log doesn't work with formData and at the beginning I thought it was that I wasn't sending anything, but actually I do.
I save the image of all the request even for those which can fulfill so the middleware seems its working too.
I hope you can help me to understand what's going on and try to find a solution, c: Thanks for your time!!

The object values does not have the map methode.
You expect that there should be a map methode, but this is not a javascipt array with protype map. So you get the error.
You can use the loadash library.
const {map} require(`lodash`)
map(values, value => {
console.log(value)
// your code coes here
}

Related

How to convert axios API call to vuex action dispatch?

I'm new with Vue3JS and I am developing a todo list built from several components like that :
I need to retrieve data to display all tasks, TaskInfo component is used to display informations.
I try to convert my local component TaskInfo works well with API call (method async loadTask()) into Vuex store action/mutation, but I didn't quite understand how to process in my case, because in component TaskInfo data are always the same.
I try to load data from current taskId with :
mounted() {
this.loadTask(this.taskId);
},
But, only last item loaded, with id 26, display data loaded by getTaskinfo computed variable loaded this.$store.state.taskInfo.
Any suggestion to load component data correctly when is mouted ?
TaskInfo.vue
<template>
<div class="task-container mb-4 ml-2 rounded-lg flex flex-grow flex-col flex-nowrap items-left shadow-test border border-solid border-gray-dark">
<div class="task--labels flex p-4 border-1 border-solid border-b border-gray-dark">
<span class="font-light p-2 text-xs bg-gray-dark rounded-lg m-2"> </span>
<span class="font-light p-2 text-xs bg-gray-dark rounded-lg m-2">label 2</span>
<div class="font-bold absolute right-1">
<button
v-if="this.taskId"
#click="this.$store.dispatch('removeTask', this.taskId)" >
<svg width="24px" height="24px" viewBox="0 0 24 24" role="img" xmlns="http://www.w3.org/2000/svg" aria-labelledby="removeIconTitle" stroke="#EF5350" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none" color="#000000">
<title id="removeIconTitle">Remove</title>
<path d="M17,12 L7,12"/>
<circle cx="12" cy="12" r="10"/>
</svg>
</button>
</div>
</div>
<div
class="task--title p-4 title text-m font-normal"
:class="[ getTaskinfo.finish === true ? 'finished' : '' ]"
>
{{ getTaskinfo.title }}
</div>
<div class="task-time p-4 text-xs text text-gray1 flex items-center">
<span class="pt-1"></span>
<span class="ml-1">11h - 12h</span>
</div>
</div>
</template>
<script>
import axios from 'axios';
import { mapActions } from 'vuex'
axios.defaults.baseURL = 'http://localhost:3000';
export default {
name: 'taskInfo',
props: ['taskId'],
data() {
return {
error: '',
taskinfo: []
};
},
mounted() {
this.loadTask(this.taskId);
},
methods: {
...mapActions([
'loadTask' // map `this.incrementBy(this.taskId)` to `this.$store.dispatch('loadTask', this.taskId);`
]),
// Old API call replaced by mapActions, it's works fine.
async loadTask() {
try {
if(!this.taskId) return;
const taskInfo = await axios.get(`/task/${this.taskId}`);
if (!taskInfo.data) {
console.log('no taskInfo');
}
else {
this.taskinfo = taskInfo.data;
}
} catch (error) {
console.log('no taskInfo error');
}
},
},
computed: {
getTaskinfo() {
return this.$store.state.taskInfo;
},
}
}
</script>
Store
import { createStore } from 'vuex'
import axios from 'axios';
axios.defaults.baseURL = 'http://localhost:3000';
export default createStore( {
state: {
taskInfo: [],
error: '',
},
// Mutations have to be synchronous.
mutations: {
loadTask(state, taskInfo) {
state.taskInfo = taskInfo;
},
updateError(state, error) {
state.error = error;
},
},
actions: {
setError({ commit }, error) {
commit('updateError', error);
},
// LOAD task info.
async loadTask({ commit }, taskId) {
try {
if(!taskId) commit('updateError', 'taskId empty');
const taskInfo = await axios.get(`/task/${taskId}`);
if (!taskInfo.data) {
commit('updateError','Aucune info sur la tache');
// Set an empty task list.
commit('loadTask', []);
}
else {
console.log('task : ' + taskId);
commit('loadTask', taskInfo.data);
}
} catch (error) {
commit('updateError', error);
}
},
},
});

When I do a post request to create a single user, it creates 6

I have a deno fresh boiler plate app with an added view called create. You land on a simple (but ugly) form view where you make a new user by typing in an email and key then pressing submit. This is the screen:
When I hit submit, the screen doesn't change but I get this in my web console.
What's even weirder is that when I check my database I have 6 entries of the same email and key. I have no idea why this is happening. I only pressed the button once:
The three files where pretty much all of the create page logic is happening is create.tsx, createUser.tsx and Creator.tsx
create.tsx:
import Layout from '../components/layouts.tsx';
import Creator from "../islands/Creator.tsx"
export default function User(props: PageProps) {
return (
<Layout>
<Creator />
</Layout>
)
}
createUser.tsx:
import { Handlers, PageProps } from "$fresh/server.ts";
import UserDb from "../../database.ts";
export const handler = {
POST: async (request, ctx) => {
const reqJsn = (await request.json());
const body = reqJsn;
const email = body.email;
const key = body.key;
console.log(email);
console.log(key);
if (!email || !key) {
ctx.status = 422;
ctx.body = { msg: "Incorrect user data. Email and key are required" };
return;
}
const userId = await UserDb.create({
email: email,
key: key,
created_at: new Date()
});
ctx.body = { msg: "User created", userId };
}
}
Creator.tsx:
// import { useState } from "preact/hooks";
import { useState, useEffect } from "preact/hooks";
import { Handlers, PageProps } from "$fresh/server.ts";
import UserDb from "../database.ts";
interface CreatorProps {
email: string,
key: string
}
export default function Creator(props: CreatorProps) {
async function handleSubmit(event) {
event.preventDefault();
const emailInput = event.target.email;
const ageInput = event.target.key;
console.log(emailInput.value);
console.log(ageInput.value);
const resp = await createNewUser(emailInput.value, ageInput.value);
return resp
};
async function createNewUser(email, key) {
const rawPosts = await fetch('http://localhost:8000/api/createUser', {
"method": "POST",
"headers": {
"content-type": "text/plain"
},
"body": JSON.stringify({
email: email,
key: key,
})
});
console.log(rawPosts);
}
return (
<div>
<h1 class="text rounded-lg p-4 my-8"> Search </h1>
<form method="post" onSubmit={async (e) => handleSubmit(e)}>
<input class="center rounded-lg p-4 my-8" id="email" name="email" />
<input class="center rounded-lg p-4 my-8" id="key" name="key" />
<br />
<button
class="px-5 py-2.5 text-sm font-medium bg-blue-600 rounded-md shadow disabled:(bg-gray-800 border border-blue-600 opacity-50 cursor-not-allowed)"
type="submit">Submit
</button>
</form>
<br />
{/* <ul>
{results.map((name) => <li key={name}>{name}</li>)}
</ul> */}
</div>
);
};
Again, I provided a minimal reproducible example. to run it you need a postgres instance running, create a .env file in the root directory of the project and add your postgres variables like this:
.env:
POSTGRES_USER=postgresuserone
POSTGRES_PASSWORD=pass2woR3d
POSTGRES_DB=postgresdbone
go to the root directory of the repo and type deno task start in your terminal and it works. Remember to navigate to localhost:8000/create, fill in the 2 field and press submit. You will now have 6 entries in your db.
I fixed it, the key was that the POST request action was never actually returning. I added:
const res = new Response(null, {
status: 302,
headers: new Headers({
location: 'localhost' + '../../../create',
}),
});
return res;
to the end of createUser.tsx (also renamed createUser.tsx to createUser.ts) and it only posted 1 new user to the database.

MERN stack binary image is just a black square

I'm building a full-stack app with a MERN stack (mongo db, Express, React and Node.js)
I'm currently able to cave my image to the mongo db as a binary file.
The the mongo shell database output is
"img" : BinData(0,"QzpcZmFrZXBhdGhcc2FtcGxlLnBuZw=="),
so I would assume that the upload is fine.
When it comes to converting it back to an image on the front end I am using
src={`data:image/png;`+btoa(`${Buffer.from(img.data).toString('base64')}`)}
which gives me a string that looks like this...
data:image/png;UXpwY1ptRnJaWEJoZEdoY2MyRnRjR3hsTG5CdVp3PT0=
However, on the front-end it shows a broken image icon and when I go to this link it just shows
a teeny tiny black square.
I have tried a lot of combinations of concatenating the string but can't seem to get it to display.
Thanks in advance !
Ok, so after many differnet strategies I managed to combine a few bit of code here and there plus some improvisation to build this...
ImageUpload.js
import React from 'react';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
class ImageUpload extends React.Component {
constructor(props) {
super(props);
this.state = {
image: {
name: "",
img: ""
},
submitted: false
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
const { name, value } = event.target;
const { image } = this.state;
this.setState({
image: {
...image,
[name]: value,
}
});
}
onSelectChange = (e) => {
const values = [...e.target.selectedOptions].map(opt => opt.value);
this.props.onChange(values);
};
handleSubmit(event) {
event.preventDefault();
console.log(this.state);
this.setState({ submitted: true });
const { image } = this.state;
if (image.name) {
this.props.register(image);
}
}
_onChange = (e) => {
const file = this.refs.uploadImg.files[0]
const reader = new FileReader();
reader.onloadend = () => {
this.setState({
image: {
img: reader.result
}
})
}
if (file) {
reader.readAsDataURL(file);
this.setState({
image: {
img: reader.result
}
})
}
else {
this.setState({
image: {
img: ""
}
})
}
}
render() {
const { registering } = this.props;
const { image, submitted } = this.state;
console.log(this.handleChange);
render() {
const { registering } = this.props;
const { image, submitted } = this.state;
console.log(this.handleChange);
return (
<div className="col-md-9 col-md-offset-3">
<form name="form" onSubmit={this.handleSubmit}>
<div className={'form-group' + (submitted && !image.img ? ' has-error' : '')}>
<label htmlFor="img">Image</label>
<input
ref="uploadImg"
type="file"
name="selectedFile"
onChange={this._onChange}>
{/* value={toString(window.form.elements[0].nextElementSibling.src)}> */}
</input>
<img src={this.state.image.img} />
{submitted && !image.img &&
<div className="help-block">Image is required</div>
}
</div>
<div className="form-group">
<button className="btn btn-primary">Register Building</button>
{registering && }
<Link to="/home" className="btn btn-link">Cancel</Link>
</div>
</form>
</div>
The mongoose Schema is just a simple string:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
Apartment = require('../apartments/apartment.model')
const schema = new Schema(
name: { type: String },
img: { type: String }
}
);
schema.set('toJSON', { virtuals: true });
module.exports = mongoose.model('image', schema);
Hope it helps anyone else. Also if anyone wouldn't mind explaining the potential flaws in uploading extrememly looong strings for images (and any solutions to this) that'd be a big help too. Would there be a way to enter this as a Buffer base64 and convert it? would the db read it this way ?
Thanks !

How to prevent repeating code of form validation

I created the form for multiple inputs, where the specific input data shall be validated at the time of data entry and once again for all data just before the submission of the form to the backend.
The conditions to submit: all fields are mandatory and the data is valid.
My program works, but I don't like that I'm repeating the validation code in 2 places: in ErrorOutput and hadleSubmit.
In ErrorOutput I check the data and, if necessary, display an error message.
In handleSubmit I just check the data without displaying of error message and if all data is valid, I confirm submitting.
How can I improve my example to prevent the repetition of this code, but the data validation was also at the time of data entry and before submission?
import React from 'react'
import { render } from 'react-dom'
const ErrorOutput = props => {
let name = props.name
let inputValue = props.case
let submit = props.submit
// Data validation
if (name === 'firstName') {
if (!inputValue.match(/^[a-zA-Z]+$/) && inputValue.length > 0) {
return <span>Letters only</span>
} else if (submit && inputValue.length === 0) {
return <span>Required</span>
}
return <span></span>
}
if (name === 'telNo') {
if(!inputValue.match(/^[0-9]+$/) && inputValue.length > 0) {
return <span>Numbers only</span>
} else if (submit && inputValue.length === 0) {
return <span>Required</span>
}
return <span></span>
}
}
class App extends React.Component {
constructor(props){
super(props)
this.state = {
firstName: '',
telNo: '',
submit: false
}
}
handleSubmit(e){
e.preventDefault()
let submit = true
let error = true
const { firstName, telNo } = this.state
this.setState ({submit: submit})
// Repeat the data validation before submission
if (firstName === '' || !firstName.match(/^[a-zA-Z]+$/)) {
error = true
} else if (telNo === '' || !telNo.match(/^[0-9]+$/)) {
error = true
} else {
error = false
}
// Submited if all data is valid
if (!error) {
// send data
return alert('Success!')
}
}
handleValidation(e) {
this.setState({
[e.target.name]: e.target.value
})
}
render() {
return (
<form onSubmit={this.handleSubmit.bind(this)}>
<div>
<label>
First name:
</label>
<input
type='text'
name ='firstName'
value = {this.state.firstName}
onChange = {this.handleValidation.bind(this)}
/>
<ErrorOutput case={this.state.firstName} name={'firstName'} submit = {this.state.submit} />
</div>
<div>
<label>
Phone number:
</label>
<input
type='tel'
name ='telNo'
value = {this.state.telNo}
onChange = {this.handleValidation.bind(this)}
/>
<ErrorOutput case={this.state.telNo} name={'telNo'} submit = {this.state.submit} />
</div>
<button>
Submit
</button>
</form>
)
}
}
render(
<App />,
document.getElementById('root')
)
You could extract a FormItem component:
class FormItem extends React.Component {
render() {
return (
<div>
<label>
{this.props.label}
</label>
<input
{...this.props.input}
/>
<ErrorOutput
case={this.props.input.value}
name={this.props.input.name}
submit={this.props.onSubmit}
/>
</div>
);
}
}
and use it in your App:
render() {
return (
<form onSubmit={this.handleSubmit.bind(this)}>
<FormItem label='First name:' input={{
type: 'text'
name: 'firstName'
value: this.state.firstName,
onChange: this.handleValidation.bind(this)
}}
onSubmit={this.state.submit}
/>
<FormItem label='Phone number:' input={{
type:'tel'
name :'telNo'
value : {this.state.telNo}
onChange : {this.handleValidation.bind(this)}
}}
onSubmit={this.state.submit}
/>
<button>
Submit
</button>
</form>
)
}
this is where libraries like react-final-form and redux-form become handy.
UPD
ErrorOutput component should not validate anything, it is not a responsibility of a component. Instead, you could validate your values on inputs blur event and before submit:
class App extends React.Component {
constructor(props){
super(props)
this.state = {
firstName: '',
telNo: '',
submit: false,
errors: {},
invalid: false,
}
}
handleSubmit(e){
e.preventDefault()
if (this.validate()) {
// handle error
} else {
// submit
}
}
validate = () => {
const { firstName, telNo } = this.state
const errors = {}
let invalid = false;
if (firstName === '' || !firstName.match(/^[a-zA-Z]+$/)) {
errors.firstName = 'first name is required'
invalid = true;
} else if (telNo === '' || !telNo.match(/^[0-9]+$/)) {
telNo.telNo = 'telNo is required'
invalid = true;
}
this.setState({
invalid,
errors,
})
return invalid;
}
render() {
return (
<form onSubmit={this.handleSubmit.bind(this)}>
<FormItem label='First name:' input={{
type: 'text',
name: 'firstName',
value: this.state.firstName,
onChange: e => this.setState({ firstName: e.target.value }),
onBlur: () => this.validate(),
}}
/>
<FormItem label='Phone number:' input={{
type: 'tel',
name: 'telNo',
value: this.state.telNo,
onChange: e => this.setState({ telNo: e.target.value }),
onBlur: () => this.validate(),
}}
/>
<button>
Submit
</button>
</form>
)
}
}
and FormItem and ErrorOutput:
const ErrorOutput = ({ error }) => <span>{error}</span>
class FormItem extends React.Component {
render() {
return (
<div>
<label>
{this.props.label}
</label>
<input
{...this.props.input}
/>
{this.props.error && <ErrorOutput error={this.props.error} />}
</div>
);
}
}
const ErrorOutput = ({ errorText }) => <span>{errorText}</span>;
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
firstName: "",
telNo: "",
submit: false,
errors: {} //Add errors object to the state.
};
}
handleSubmit(e) {
e.preventDefault();
const errors = this.validateData();
if (Object.keys(errors).length === 0) {
alert("Success");
}
//else errors exist
this.setState({ errors });
}
validateData = () => {
let errors = {};
const { firstName, telNo } = this.state; // read the values to validate
if (firstName.length === 0) {
errors.firstName = "Required";
} else if (firstName.length > 0 && !firstName.match(/^[a-zA-Z]+$/)) {
errors.firstName = "Letters only";
}
if (telNo.length === 0) {
errors.telNo = "Required";
} else if (telNo.length > 0 && !telNo.match(/^[0-9]+$/)) {
errors.telNo = "Numbers only";
}
return errors;
};
handleValidation(e) {
this.setState({
[e.target.name]: e.target.value
});
}
render() {
const { errors } = this.state; // read errors from the state
return (
<form onSubmit={this.handleSubmit.bind(this)}>
<div>
<label>First name:</label>
<input
type="text"
name="firstName"
value={this.state.firstName}
onChange={this.handleValidation.bind(this)}
/>
{errors.firstName && <ErrorOutput errorText={errors.firstName} />}
</div>
<div>
<label>Phone number:</label>
<input
type="tel"
name="telNo"
value={this.state.telNo}
onChange={this.handleValidation.bind(this)}
/>
{errors.telNo && <ErrorOutput errorText={errors.telNo} />}
</div>
<button>Submit</button>
</form>
);
}
}
render(<App />, document.getElementById("root"));

ReactJS Semantic ui - Cannot type into form when value attribute exists

I am following a ReactJS tutorial to set up a login form. Semantic ui is used and imported. The email and password are passed into the value attribute inside the form. When this happens, I cannot type anything into the form. As soon as I remove it, I can type information in but I assume it won't get passed into anywhere.
Cannot seem to find this issues anywhere else. Has anyone experienced this issue before?
import React from 'react';
import PropTypes from 'prop-types';
import { Form, Button } from 'semantic-ui-react';
import Validator from 'validator';
import InlineError from '../messages/InlineError';
class LoginForm extends React.Component {
state = {
data: {
email: "",
password: ""
},
loading: false,
errors: {}
};
//... is called spread
onChange = e => this.setState({
data: {...this.state.data, [e.target.name]: e.target.value }
});
//() means function takes no params
onSubmit = () => {
const errors = this.validate(this.state.data);
this.setState({errors}); //if there are errors, display them
if(Object.keys(errors).length === 0){
this.props.submit(this.state.data);
}
};
validate = (data) => {
const errors = {};
if(!Validator.isEmail(data.email))
errors.email = "Invalid email";
if(!data.password)
errors.password = "Can't be blank";
return errors;
};
render() {
const { data, errors } = this.state; // import variables into html
return (
<div>
<Form onSubmit={ this.onSubmit }>
<Form.Field error={!!errors.email}>
<label htmlFor="email">Email</label>
<input type="email"
id="email"
placeholder="example#abc.com"
value={ data.email }
onChange={ this.onChange }/>
{errors.email && <InlineError text={errors.email}/>}
</Form.Field>
<Form.Field error={!!errors.email}>
<label htmlFor="password">Password</label>
<input type="password"
id="password"
value={ data.password }
onChange={this.onChange}/>
{errors.password && <InlineError text={errors.password}/>}
</Form.Field>
<Button primary>Login</Button>
</Form>
</div>
);
}
}
LoginForm.propTypes = {
submit: PropTypes.func.isRequired
};
export default LoginForm;
tutorial: https://www.youtube.com/watch?v=NO2DaxhoWHk&t=879s
onChange = e => this.setState({
data: {...this.state.data, [e.target.name]: e.target.value }
});
This function is setting the state to a variable that shares the name of your input field. Hence e.target.name. But your input fields do not have a name attribute.
You can fix that with:
import React from 'react';
import PropTypes from 'prop-types';
import { Form, Button } from 'semantic-ui-react';
import Validator from 'validator';
import InlineError from '../messages/InlineError';
class LoginForm extends React.Component {
state = {
data: {
email: "",
password: ""
},
loading: false,
errors: {}
};
//... is called spread
onChange = e => this.setState({
data: {...this.state.data, [e.target.name]: e.target.value }
});
//() means function takes no params
onSubmit = () => {
const errors = this.validate(this.state.data);
this.setState({errors}); //if there are errors, display them
if(Object.keys(errors).length === 0){
this.props.submit(this.state.data);
}
};
validate = (data) => {
const errors = {};
if(!Validator.isEmail(data.email))
errors.email = "Invalid email";
if(!data.password)
errors.password = "Can't be blank";
return errors;
};
render() {
const { data, errors } = this.state; // import variables into html
return (
<div>
<Form onSubmit={ this.onSubmit }>
<Form.Field error={!!errors.email}>
<label htmlFor="email">Email</label>
<input type="email"
id="email"
name="email"
placeholder="example#abc.com"
value={ data.email }
onChange={ this.onChange }/>
{errors.email && <InlineError text={errors.email}/>}
</Form.Field>
<Form.Field error={!!errors.email}>
<label htmlFor="password">Password</label>
<input type="password"
id="password"
name="password"
value={ data.password }
onChange={this.onChange}/>
{errors.password && <InlineError text={errors.password}/>}
</Form.Field>
<Button primary>Login</Button>
</Form>
</div>
);
}
}
LoginForm.propTypes = {
submit: PropTypes.func.isRequired
};
export default LoginForm;