Creating a form in React that saves data - javascript

I am trying to create a customer details form in react (currently using react-json-form) where I can reuse the values in the inputs to create a saved file that the app can refer to. I have created the form and can output the results but I am unsure how to save the input values for future use or call them back once they are saved.
If anyone has any suggestions or examples of a form that does this then I would be greatly appreciative.
My code is as follows:
import React, { Component } from 'react';
import JSONTree from 'react-json-tree';
import { BasicForm as Form, Nest, createInput } from 'react-json-form';
const Input = createInput()(props => <input type="text" {...props} />);
const UserFields = () => (
<section>
<h3>User</h3>
<div>Name: <Input path="name" /></div>
<div>Email: <Input path="email" /></div>
</section>
);
export default class ExampleForm extends Component {
state = { data: {} };
updateData = data => this.setState({ data });
render() {
return (
<Form onSubmit={this.updateData}>
<Nest path="user">
<UserFields />
</Nest>
<button type="submit">Submit</button>
<JSONTree data={this.state.data} shouldExpandNode={() => true} />
</Form>
);
}
}

A more simple solution would be to use a form, like a semanti-ui-react form, store the information to the state onChange, then convert the info to JSON for storage.
import { Form, Button } from 'semantic-ui-react'
export default class App extends Component {
constructor() {
super()
this.state = {
name: "",
email: ""
}
}
handleChange = (e, {name, value}) => {
console.log(name, value)
this.setState({[name]: value})
}
render() {
return (
<div>
<Form onSubmit={this.sendDataSomewhere}>
<Form.Field>
<Form.Input name="name" value={this.state.name} onChange={this.handleChange}/>
</Form.Field>
<Form.Field>
<Form.Input name="email" value={this.state.email} onChange={this.handleChange}/>
</Form.Field>
<Button type="submit">Submit</Button>
</Form>
</div>
)
}
}
I use a dynamic method of receiving the input from different fields using the name and val attributes. The values captured in state are then accessible by this.state.whatever
Hope this helped

Related

Is this a good practice in using the useState hook in a component to pass values to the parent of the component?

I am trying to create an Input component that can be used dynamically in any page which stores the input value and has an attribute getValue which lets the parent of the component access it. In App.js I have an object called "info" that stores the values taken from the Input component. Is the following code a good implementation for this problem?
Input.js
const Input = ({placeholder, getValue}) => (
<input
placeholder={placeholder}
onChange={({target}) => getValue(target.placeholder, target.value)}
/>
)
Input.defaultProps = {
getValue: (key, value) => {}
}
export default Input;
App.js
import { useState } from "react";
import Input from "./Input.js";
const App = () => {
const [info, setInfo] = useState({});
const addToInfo = (name, val) => {
let keyWord = (name.charAt(0).toLowerCase() + name.slice(1)).replace(/\s/g, "");
setInfo(prev => ({...prev, [keyWord]: val}))
}
return (
<div>
<h1>Hello {info.firstName && info.firstName} {info.lastName && info.lastName}</h1>
<Input placeholder="First Name" getValue={(name, val) => addToInfo(name, val)}/>
<br/>
<Input placeholder="Last Name" getValue={(name, val) => addToInfo(name, val)}/>
<br/>
<Input placeholder="Email" getValue={(name, val) => addToInfo(name, val)}/>
<br/>
<Input placeholder="Phone Number" getValue={(name, val) => addToInfo(name, val)}/>
<br/>
<button onClick={() => console.log(info)}>Console Log the Info</button>
</div>
);
}
export default App;
I recommend you can take a look at Formik, their approach is exactly the solution to your problem, namely, passing value up to a parent, here's the basic structure they use:
<Formik>
{(props) => (
<Form> // This is from Formik
// your basic form stuff
</Form>
)}
</Formik>
On how to make the <Form></Form> reusable, last time I asked one of their contributor on it, feel free to take a look: Make Formik a custom component
This is Formik official website
Hope this can help you draw some inspiration!

Form input elements are messing with URL of react page

ContactData.js
import React, { Component } from 'react';
import Button from '../../../components/UI/Button/Button';
import classes from './ContactData.module.css'
class ContactData extends Component {
state = {
name: '',
age: '',
address: {
street: '',
postalCode: ''
}
}
orderHandler = () => {
console.log(this.props.ingredients)
}
render() {
console.log(this.props.ingredients);
return (
<div className={classes.ContactData}>
<h4>Enter Your Contact Data</h4>
<form>
<input type="text" name="name" placeholder="Your Name" />
<input type="email" name="email" placeholder="Your Mail" />
<input type="text" name="street" placeholder="Street" />
<input type="text" name="postal" placeholder="Postal Code" />
<Button btnType="Success" clicked={this.orderHandler}>ORDER</Button>
</form>
</div>
);
}
}
export default ContactData;
CheckOut.js
import React from 'react';
import CheckoutSum from '../../components/Checkout/Checkout'
import { Route } from 'react-router-dom';
import ContactData from '../../container/Checkout/ContactData/ContactData'
class Checkout extends React.Component {
state = {
ingredients: {
salad: 1,
meat: 1,
cheese: 1,
bacon: 1
}
}
componentDidMount() {
const query = new URLSearchParams(this.props.location.search);
const ingredients = {};
for (let param of query.entries()) {
ingredients[param[0]] = parseInt(param[1]);
}
// console.log(ingredients);
this.setState({ ingredients: ingredients });
}
cancelHandle = () => {
this.props.history.goBack();
}
continueHandle = () => {
this.props.history.replace('/checkout/contact-data');
}
render() {
console.log(this.props)
console.log(this.state.ingredients)
return (
<div>
<CheckoutSum
cancel={this.cancelHandle}
continue={this.continueHandle}
ingredients={this.state.ingredients}
/>
<Route
path={this.props.match.path + '/contact-data'}
exact
render={() => (<ContactData ingredients={this.state.ingredients} />)} />
</div>
);
}
}
export default Checkout;
Problem is when I hit Order button in CotanctData component the page reloads and for some reason my URL changes to this http://localhost:3000/checkout/contact-data?name=&email=&street=&postal= and then Checkout component renders again and for some reason componentDidMount fires twice. At last I was expecting the printing of ingredients object.
Also, I'm using search query in URL to change the state of checkout component
Full Project At-https://github.com/aniket-hue/Burger-App-React/tree/Routes
Please bear with me if you don't like the question I didn't know how to frame the question.
You need to either:
Call e.preventDefult() on the <form>'s submit event.
OR, add <button type="button"> to your button to make it not submit your form.
You have to add preventDefault to your orderHandler method, beacause if button in form has no type, the type is automaticlly submit, so on every click your submitting a form. Also can you add type="button" to your button inside form.
orderHandler = e => {
e.preventDefault();
console.log(this.props.ingredients);
}

React-Redux: Insert Data in DB

I'm learning to use react-redux, so sorry if I'm doing confusing!
I'm trying to create a form and insert data into the db.
Then I have created 2 page, one for the form and another one.
RequestForm
handleChange = (e) => {
let meeting = this.state.meeting;
meeting[e.target.name] = e.target.value;
this.setState({ meeting });
};
handleSubmit(event) {
event.preventDefault();
console.log(this.state.meeting);
this.props.addMeeting(this.state)
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<div>
<label>Motivation:</label>
<input
type="text"
name="motivation"
onChange={(event) => this.handleChange(event)}
/>
</div>
<div>
<label>Date:</label>
<input
type="date"
name="date"
onChange={(event) => this.handleChange(event)}
/>
</div>
MeetingRequest.js
import RequestForm from './RequestForm';
import { connect } from 'react-redux';
import {addMeeting} from '../Redux/actions';
class MeetingRequest extends Component {
render() {
const addMeeting = this.props
return (
<div>
<h2>Request Meeting</h2>
<RequestForm
addMeeting={addMeeting}
/>
</div>
);
}
}
function mapDispatchToProps(dispatch) {
return {
addMeeting: (meeting) => dispatch(addMeeting(meeting)),
};
}
export default connect(null, mapDispatchToProps)(MeetingRequest);
EDIT. Thank you to the comments the firt error is resolved, now I have a 500 problem. Could it be a problem about the Actions.js??
And also in your opinion is right how I'm trying to do??
Thank you
Change:
-const addMeeting = this.props
+const { addMeeting } = this.props
in MeetingRequest.js
Regarding your 500 error on API, it may be because you're sending JSON while setting multipart/form-data. Inspect using Network tab in dev tools.
If your backend need multipart/form-data you have to convert your state.meeting object to FormData

Passing multiple input fields between React Form state-less component and state-full Root component

I'm a beginner with React and I'm attempting to make an address book application (front-end only, for now). I have my root component called App in the App.js file and I'm trying to move the form element from the root component into its own component (state-less I guess) in the AddContact.js file. I was able to find a way (searching SO) to pass the form input fields when the form is part of the root component to the form submit handler function. But I'm not able to do the same when the form element is part of a different component. Below are the App.js and AddContact.js files. How should I go about doing so? Preferably in the simplest way, while adhering to react/es6 best practices.
App.js
import React, { Component } from 'react';
import './App.css';
//import AddContact from './AddContact/AddContact';
//import './AddContact/AddContact.css';
import ContactList from './ContactList/ContactList.js';
import Cockpit from './Cockpit/Cockpit.js';
class App extends Component {
state = {
contacts:[]
};
addContactHandler = (event) => {
event.preventDefault();
//console.log(event.target[0].value);
//console.log(event.target.name.value);
const name = this.input1.value;
const phone = this.input2.value;
const email = this.input3.value;
let contacts = [...this.state.contacts];
if (name.length>0 && phone.length>0 && email.length>0){
let contact = {
name: name,
phone: phone,
email: email
};
contacts.push(contact);
console.log("contacts: ", contacts);
this.setState({
contacts:contacts
});
}
this.input1.value = '';
this.input2.value = '';
this.input3.value = '';
};
render() {
let contactList = null;
let contacts = [...this.state.contacts];
if (contacts.length > 0) {
contactList = (
<ContactList
contacts = {contacts}
/>
);
}
return (
<div className="App">
<Cockpit/>
<p>Add New Contact:</p>
<form className="AddContact" onSubmit={(event)=>this.addContactHandler(event)}>
<label>Name:
<input type="text" name="name" ref={(name) => {this.input1 = name}}/>
</label>
<label>Phone:
<input type="text" name="phone" ref={(phone) => {this.input2 = phone}}/>
</label>
<label>Email:
<input type="text" name="email" ref={(email) => {this.input3 = email}}/>
</label>
<input type="submit" value="Submit" />
</form>
{/* <AddContact submit={(event)=>this.addContactHandler(event)}/> */}
{contactList}
</div>
);
}
}
export default App;
AddContact.js
// Use at a later stage
import React from 'react';
import './AddContact.css';
const AddContact = (props) => {
return (
<div >
<p>Add New Contact:</p>
<form className="AddContact" onSubmit={props.submit}>
<label>Name:
<input type="text" ref={(name) => {this.input = name}}/>
</label>
<label>Phone:
<input type="text" ref={(phone) => {this.input2 = phone}}/>
</label>
<label>Email:
<input type="text" ref={(email) => {this.input3 = email}}/>
</label>
<input type="submit" value="Submit" />
</form>
</div>
);
};
export default AddContact;
Moving the form element into its own dedicated component is a great approach. But don't try to handle the child component's form submit event from the parent component. The child component owns the form, it should handle its mutations and submission too. With that in mind, this is how I'd structure the components:
Your App renders the AddContact component.
For your AddContact component, don't pass an onSubmit handler to it. Instead, pass an onAdd handler that expects a contact object as its argument.
Make your AddContact component stateful! Is there any particular reason you would want it to be stateless? Make it stateful, let it handle form updates as part of its state (use Controlled Components). And finally, on the form onSubmit event, package up a contact object and call the props.onAdd handler with it.
This will be a cleaner, more rationally separated architecture for your components. The parent component shouldn't have to worry about a child component's form submit behavior, but should rather expect a nicely packaged up contact object.
Check this out: https://mn627xy99.codesandbox.io/
class AddContact extends React.Component {
state = {
name: "",
phone: "",
email: "",
}
submit = e => {
e.preventDefault();
this.props.onAdd(this.state);
// Clear the form
this.setState({name: "", phone: "", email: ""});
}
change = e => {
e.preventDefault();
this.setState({[e.currentTarget.name]: e.currentTarget.value});
}
render() {
return (
<div >
<p>Add New Contact:</p>
<form className="AddContact" onSubmit={this.submit}>
<label>Name:
<input type="text" name="name" onChange={this.change} value={this.state.name} />
</label>
<label>Phone:
<input type="text" name="phone" onChange={this.change} value={this.state.phone} />
</label>
<label>Email:
<input type="text" name="email" onChange={this.change} value={this.state.email} />
</label>
<input type="submit" value="Submit" />
</form>
</div>
);
}
}

How to pass select dropdown's data from redux-form to backend api in redux

I have a redux form where I have 2 input fields and one select drop-down. I wan to store the values of the inputs to a backend api. I am able to get the values from the inputs and pass it to the server, but for some reason I am not able to correctly get the select drop-down value and pass it to the back-end server. I know I am doing some silly mistake with the state, but I am not able to figure out what is that.My file is as shown below:
import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { createPosts } from '../actions/posts_action';
class CreatePost extends Component {
constructor() {
super();
this.state = {
selectValue : 'react'
};
this.handleChange = this.handleChange.bind(this);
this.renderCategory = this.renderCategory.bind(this);
}
renderField(field) {
return(
<div className="title-design">
<label className="label-design"> {field.label} </label>
<input
type="text"
className="title-input"
{...field.input}
/>
<div className="text-help has-danger">
{field.meta.touched ? field.meta.error : ''}
</div>
</div>
);
}
handleChange(e) {
this.setState({selectValue: e.target.value});
console.log(this.state.selectValue);
}
renderCategory(field) {
return(
<div className="title-design">
<label className="label-design">{field.label} </label>
<select
name="categories"
className="title-input"
value={this.state.selectValue}
onChange={this.handleChange} >
<option value="react">React</option>
<option value="redux">Redux</option>
<option value="udacity">Udacity</option>
</select>
{this.state.selectValue}
</div>
);
}
onSubmit(values) {
this.props.createPosts(values, () => {
this.props.history.push('/');
});
}
render() {
const { handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<Field
label="Title for Post"
name="title"
component={this.renderField}
/>
<Field
label="Post Content"
name="body"
component={this.renderField}
/>
<Field
label="Category"
name="category"
component={this.renderCategory}
/>
<button type="submit" className="btn btn-primary">Submit</button>
<Link to="/">
<button className="cancel-button">Cancel</button>
</Link>
</form>
);
}
}
function validate(values) {
const errors = {} ;
if (!values.title) {
errors.title = "Enter a title";
}
if (!values.body) {
errors.body = "Enter some content";
}
return errors;
}
export default reduxForm({
validate : validate, //validate
form : 'CreatePostForm'
})(
connect(null,{ createPosts })(CreatePost)
);
NOTE: Also,I want to generate a unique id for each of the records that is passed to the server so that I can refer to them when I want to retrieve data and display it.Can anyone please guide me how to proceed with both the issues?
setState is not synchronous, it has a callback so you should have something like:
handleChange(e) {
this.setState({selectValue: e.target.value}, () => {
console.log(this.state.selectValue);
});
}
To set the value of the field, you can use change whose signature is:
change(field:String, value:any)
So your handler will just become:
handleChange(e) {
const value = e.target.value;
this.props.change("categories", value);
this.setState({ "selectValue": value }, () => {
console.log(value);
});
}
and in your renderCategory you can remove the value attribute:
...
<select
name="categories"
className="title-input"
onChange={this.handleChange} >
...
To set some random id for every submission, you can find lots of libraries to generate a random one; to assign to the record, use again change method and field "id" will be sent to the server:
this.props.change('id', <random generated Id>)
See this sandbox a rough implementation

Categories