React forum submit when pressing enter in textarea - html

A am making this forum type app and to create a post you go through a form with a textarea; I want to support Markdown. So to do that you kind of need to support new line functionality. When I press the enter key this error occurs:
This is the code
export default class CreatePost extends Component {
state = {
redirect: false,
Title: '',
Author: '',
Body: '',
};
updateState = e => {
this.setState({ [e.target.name]: e.target.value });
};
createPost = e => {
e.preventDefault();
if (this.state.Body !== '') {
this.props.createPost(this.state);
}
this.setState({ Title: '', Author: '', Body: '', redirect: true });
};
onEnterPress = e => {
if (e.keyCode === 13) {
e.preventDefault();
}
};
render() {
if (this.state.redirect) {
return <Redirect push to='/' />;
}
return (
<div className='createPost'>
<h1>Create Your Post!</h1>
<form onSubmit={this.createPost}>
<label className='input'>
Title:{' '}
<input
type='text'
name='Title'
value={this.state.Title}
onChange={this.updateState}
required
/>
</label>
<label className='input'>
Author:{' '}
<input
type='text'
name='Author'
value={this.state.Author}
onChange={this.updateState}
required
/>
</label>
<textarea
name='Body'
className='body'
value={this.state.Body}
onKeyDown={this.onEnterPress}
onChange={this.updateState}
></textarea>
<input type='submit' value='Submit' className='submitBTN' />
</form>
<Link className='home' to='/'>
Go Home
</Link>
</div>
);
}
}
I have tried the regular on press down function but It doesn't work.
Update #1
I got it to work but I still get the error.

You need to move the onEnterPress to the form itself, because the form also listens to the key presses.
<form onSubmit={this.createPost} onKeyDown={onEnterPress}>
Here is a fiddle.

Related

React - HTML form validation doesn't work

I work on a React project. I wrote HTML codes for create form. There is validation in HTML scripts. I wrote validations but validations doesn't work. The code is below. For example I want the relevant field to be red when the name is not entered or I want it to give a warning message when the name does not comply with the text rules. I must do it without any library. How can I fix it ?
import React, { Component } from 'react'
import EmployeeService from '../services/EmployeeService';
class CreateEmployeeComponent extends Component {
constructor(props) {
super(props)
this.state = {
id: this.props.match.params.id,
firstName: '',
lastName: '',
emailId: ''
}
}
componentDidMount(){
if(this.state.id === '_add'){
return
}else{
EmployeeService.getEmployeeById(this.state.id).then( (res) =>{
let employee = res.data;
this.setState({firstName: employee.firstName,
lastName: employee.lastName,
emailId : employee.emailId
});
});
}
}
saveOrUpdateEmployee = (e) => {
e.preventDefault();
let employee = {firstName: this.state.firstName, lastName: this.state.lastName, emailId: this.state.emailId};
console.log('employee => ' + JSON.stringify(employee));
// step 5
if(this.state.id === '_add'){
EmployeeService.createEmployee(employee).then(res =>{
this.props.history.push('/employees');
});
}else{
EmployeeService.updateEmployee(employee, this.state.id).then( res => {
this.props.history.push('/employees');
});
}
}
changeFirstNameHandler= (event) => {
this.setState({firstName: event.target.value});
}
changeLastNameHandler= (event) => {
this.setState({lastName: event.target.value});
}
changeEmailHandler= (event) => {
this.setState({emailId: event.target.value});
}
cancel(){
this.props.history.push('/employees');
}
getTitle(){
if(this.state.id === '_add'){
return <h3 className="text-center">Add Employee</h3>
}else{
return <h3 className="text-center">Update Employee</h3>
}
}
onSubmit = e => {
e.preventDefault();
}
render() {
return (
<div>
<br></br>
<div className = "container">
<div className = "row">
<div className = "card col-md-6 offset-md-3 offset-md-3">
{
this.getTitle()
}
<div className = "card-body">
<form onSubmit={this.onSubmit} noValidate>
<div className = "form-group">
<label for="validationCustom01" class="form-label">First name</label>
<input type='text' maxLength={20} pattern='[A-Za-z]' placeholder="First Name" name="firstName" className="form-control"
value={this.state.firstName} onChange={this.changeFirstNameHandler} required/>
</div>
<div className = "form-group">
<label> Last Name: </label>
<input type='text' maxLength={20} pattern='[A-Za-z]'class="form-control" placeholder="Last Name" name="lastName" className="form-control"
value={this.state.lastName} onChange={this.changeLastNameHandler} required/>
</div>
<div className = "form-group">
<label> Email Id: </label>
<input type='email' maxLength={35} pattern='[A-Za-z]' placeholder="Email Address" name="emailId" className="form-control"
value={this.state.emailId} onChange={this.changeEmailHandler} required/>
</div>
<button type="submit" className="btn btn-success" onClick={this.saveOrUpdateEmployee}>Save</button>
<button className="btn btn-danger" onClick={this.cancel.bind(this)} style={{marginLeft: "10px"}}>Cancel</button>
</form>
</div>
</div>
</div>
</div>
</div>
)
}
}
export default CreateEmployeeComponent
Remove noValidate from your <form> element.
Additionally, I've personally found that Formik and Yup can be a helpful library.
(Sorry I missed that "no libraries" wasa constraint)
Edit:
I was a muppet and forgot to change the pattern
You can do one of 2 things:
Remove maxLength and change the pattern to pattern="[A-Za-z]{1,20}"
Where 20 will be the new max-length (so 20 or 35, depending on the field)
Only change the pattern to be pattern="[A-Za-z]+"
The + is needed to ensure 1 or more of the [A-Za-z] regex is done. https://regexr.com/
Also don't forget to remove noValidate from your <form> element.
This answer may also prove helpful in setting custom validity status messages and callbacks.

Netflify Serverless Function JSON parse error Sendgrid API

My contact form is not sending the frontend user to my thank you page neither is it sending any information to me via the Sendgrid APi. The issue comes from the fact that I'm not sure how to turn the JSON object into a string and then straight back to an Object..... Or as you can see I have a hard time even framing my question. The goal would be to send the information to my email account using Sendgrid API.
The form is live here:
https://www.metsanotus.fi/yhteydenotto
The code is based 99% off these two tutorials:
https://oliverschmidt.dev/blog/adding-a-contact-form-to-a-static-site-with-netlify-functions/
https://dev.to/char_bone/using-netlify-lambda-functions-to-send-emails-from-a-gatsbyjs-site-3pnb
The code for the contact-page:
https://gist.github.com/otsolap/f05cd4e3a1a08794f61a6d5730abc695
import React, { useState } from "react";
import { graphql } from "gatsby"
import { RiSendPlane2Line } from "react-icons/ri";
import Layout from "../components/layout"
import SEO from "../components/seo"
export const pageQuery = graphql`
query ContactQuery($id: String!){
markdownRemark(id: { eq: $id }) {
id
html
excerpt(pruneLength: 140)
frontmatter {
title
}
}
site {
siteMetadata {
title
}
}
}
`
const Contact = ({ data }) => {
const { markdownRemark, site } = data // data.markdownRemark holds your post data
const { frontmatter, html } = markdownRemark
// input type hidden on netlifytä varten, jotta netlify tietää mikä lomake kyseessä.
// contact on meidän lomake, niin kaikki viestit löytyy contact-lomakkeen alta.
// honeypot=bot-field on botteja varten.
// p hidden pitää kohdan piilossa, mutta console.logilla sen löytää. ;-)
const [formState, setFormState] = useState({
name: '',
email: '',
phone: '',
subject: '',
message: '',
})
const handleChange = (e) => {
setFormState({
...formState,
[e.target.name]: e.target.value,
});
}
const handleSendEmail = async (event) => {
event.preventDefault();
try {
const response = await fetch("/.netlify/functions/contact-form-email", {
method: "POST",
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formState),
})
if (!response.ok) {
//EI 200 response
return
}
//all OK
} catch (event) {
//error
}
}
return (
<Layout className="contact-page">
<SEO
title={frontmatter.title}
description={frontmatter.title + " " + site.siteMetadata.title}
/>
<div className="wrapper">
<h1>{frontmatter.title}</h1>
<div className="description" dangerouslySetInnerHTML={{ __html: html }} />
<form className="contact-form"
action="/kiitos"
name="contact"
method="POST"
data-netlify="true"
value="contact"
data-netlify-honeypot="bot-field"
onSubmit={handleSendEmail}
>
<input type="hidden" name="form-name" value="contact" />
<p hidden><input name="bot-field" /></p>
<p>
<label><input
required
placeholder="Nimi *"
type="text"
name="name"
onChange={handleChange}
/>
</label>
</p>
<p>
<label><input required
placeholder="Sähköposti *"
type="email"
name="email"
onChange={handleChange}
/>
</label>
</p>
<p>
<label><input required
placeholder="Puhelin *"
type="number"
name="phone"
onChange={handleChange}
/>
</label>
</p>
<p>
<label><input placeholder="Aihe"
type="text"
name="subject"
onChange={handleChange}
/>
</label>
</p>
<p>
<label><textarea
placeholder="Viesti"
name="message"
onChange={handleChange}
></textarea></label>
</p>
<p className="text-align-center">
<button className="button"
type="submit">
Lähetä<span className="icon -right"><RiSendPlane2Line />
</span>
</button>
</p>
</form>
</div>
</Layout>
)
}
export default Contact
The code for the serverless function:
https://gist.github.com/otsolap/e157b136aee040281f20ba87131014eb
require('dotenv').config();
const sgMail = require('#sendgrid/mail')
const {
SENDGRID_API_KEY,
METSAN_OTUS_NAME,
METSAN_OTUS_ADDRESS }
= process.env
sgMail.setApiKey(SENDGRID_API_KEY)
exports.handler = async (event, context, callback) => {
const payload = JSON.parse(event.body)
const { email, subject, message } = payload
const msg = {
to: METSAN_OTUS_ADDRESS,
name: METSAN_OTUS_NAME,
from: email,
subject: subject ? subject : 'Yhteydenotto lomakkeesta',
text: message,
};
try {
await sgMail.send(msg)
return {
statusCode: 200,
body: "Viesti lähetetty"
}
} catch (e) {
return {
body: e.message,
statusCode: 500,
}
}
};
When I keep the JSON.parse(body.event) this is the error it displays:
https://gist.github.com/otsolap/79830f6cf1e9b247c63c1f3f49c5286b
SyntaxError: Unexpected token u in JSON at position 0
If I change the line 13 of serverless-function.js from JSON.parse(event.body) to (for example) JSON.stringify(event.body) the error becomes this:
TypeError: Cannot destructure property 'email' of 'payload' as it is undefined.
So I guess my question is how should I formulate my serverless function so that the React object from UseState can become readable for my function?

Why do I see [object Object] in my React input field instead of placeholder text?

I got the problem when implementing form input validation in React.
Once I open the form, there is always [object object] appear as the default input.
Here is the display:
Ideally, I want to have the below display:
And here is my code, any idea how shall I fix it?
const ErrorValidationLabel = ({ txtLbl }) => (
<label htmlFor="" style={{ color: "red" }}>
{txtLbl}
</label>
);
const txtFieldState = {
value: "",
valid:true,
}
class Setting extends Component {
constructor() {
this.state = {
name: {...txtFieldState, fileName:"name", required:true, requiredTxt:"Device ID is required"} ,
}
this.handleNameChange = this.handleNameChange.bind(this);
this.handleNameSearch = this.handleNameSearch.bind(this);
handleNameChange(event) {
this.setState({ name: event.target.value });
}
handleNameSearch(event) {
//http request call logic
}
}
render() {
const renderNameError = this.state.name.valid? "" : <ErrorValidationLabel txtLbl={this.state.name.requiredTxt} />;
return (
<form onSubmit={this.handleNameSearch}>
<label >
Enter Your Device Id:
</label>
<Input name="name" type="text" placeholder="ex:12345678910" value={this.state.name} onChange={this.handleNameChange} required/>
{renderNameError}
<Input type="submit" value="Search Device" />
</form>
);
}
You're setting the value of your input to this.state.name, but this.state.name is an object.
this.state = {
name: {...txtFieldState, fileName:"name", required:true, requiredTxt:"Device ID is required"}
}
You should set it to this.state.name.value.
I should warn you, however, that the code this.setState({ name: event.target.value }) will overwrite your entire name object, which likely isn't what you want.
You're using a spread operator already, so I would suggest
this.setState(state => ({ name: { ...state.name, value: event.target.value } }))`.

How can I reassign saved file in input file tag ReactJS

In my react component I have this type of code :
state = {
title: '',
file: ''
}
handleTextChange = (e) => {
this.setState({
[e.target.id] : e.target.value
})
}
handleFileChange = (e) => {
this.setState({
[e.target.id] : e.target.files[0]
})
}
render(){
return(
<input type="text" id="title" defaultValue={title} onBlur={this.handleTextChange}/>
<input type="file" id="file" onChange={this.handleFileChange}/>
)
}
Now the problem is when I change text, the selected file automatically deselect and I have to select again it. I have to do this thing every time when I change the text in textbox.
How can I set default value in the file the same as I can do in textbox so every time I do not have to select the same file.
You have to concern about immutability here. Your state is an object. so every time you are setting state, you should only change, changing property only.
state = {
title: '',
file: ''
}
handleTextChange = (e) => {
this.setState({
...state,
[e.target.id] : e.target.value
})
}
handleFileChange = (e) => {
this.setState({
...state,
[e.target.id] : e.target.files[0]
})
}
render(){
return(
<input type="text" id="title" defaultValue={title} onBlur={this.handleTextChange}/>
<input type="file" id="file" onChange={this.handleFileChange}/>
)
}

Convert HTML form with action to React form submit with logic

Folks, I think I'm either missing something here or I don't know what I don't know.
What I have is:
<form action="/orders/populate" method="post">
<input type="hidden" name="name" id="name"/>
<input type="hidden" name="rating" id="rating"/>
<input type="submit" name="commit" value="Rate Now" />
</form>
What I want to do is:
Class myComponent extends React.PureComponent {
handleSubmit(e) {
e.preventDefault(); // don't know if this is necessary
sendAnalytics();
// then form submit
}
render () {
return (
<form action="/orders/populate" method="post" onSubmit={this.handleSubmit.bind(this)}>
<input type="hidden" name="name" id="name"/>
<input type="hidden" name="rating" id="rating"/>
<input type="submit" name="commit" value="Rate Now" />
</form>
);
}
}
Don't know what has to be done here. Can someone point out an example similar to this? Or perhaps give me a sample code below?
All help appreciated.
Class myComponent extends React.PureComponent {
this.state = {
name: '' // initial value for name
rating: '' // initial value for rating
}
handleInput = e => {
this.setState({[e.target.name]: e.target.value})
}
handleSubmit = e => {
const { name, rating } = this.state;
e.preventDefault(); // yes, this is necessary otherwise it's refresh your page.
sendAnalytics(name, rating); // api call to store in DB. to call API use axios npm package
}
render () {
const { name, rating } = this.state;
return (
<form onSubmit={this.handleSubmit}>
<input type="text" name="name" value={name} id="name" onChange={(e) => this.handleSubmit(e)}/>
<input type="text" name="rating" value={rating} id="rating" onChange={(e) => this.handleSubmit(e)}/>
<input type="submit" name="commit" value="Rate Now" />
</form>
);
}
}
Have you looked at the docs for handling forms in React? This will give you insights in how to use forms with react, since it handles a bit different than regular html forms
This is a common problem I've faced in React. You have one of three ways:
1) Use a third party React-Form library to do the job. There are several.
2) Use React-hooks (a very recent addition to React).
3) Create a generic Form class to handle this state management for you...like so:
export default class Form extends React.Component {
constructor(props) {
super(props);
this.state = {
values: {}
};
}
#boundMethod
handleSubmit(event) {
event.preventDefault();
this.props.submit(this.state.values);
}
#boundMethod
handleChange(event) {
const { name, value } = event.target;
const newValues = Object.assign(
{ ...this.state.values },
{ [name]: value }
);
this.setState({
values: newValues
});
}
public render() {
const { values } = this.state;
return (
<form onSubmit={this.handleSubmit} noValidate={true}>
<div>
{React.Children.map(
this.props.children,
child => (
{React.cloneElement(child, {
value: values[child.props.name],
onChange: this.handleChange
})}
)
)}
<div>
<button type="submit">
Submit
</button>
</div>
</div>
</form>
);
}
}
Then you will be able to use this Form class like so:
<Form
submit={values => {
/* work with values */
}}
>
<input type="hidden" name="name" />
<input type="hidden" name="rating" />
</Form>;
PS:
Keep in mind boundMethod Decorator is something that is not natively available but a module called 'autobind-decorator' I tend to use a lot to deal with this not being bound.