How to pass a functional component inside an event? - javascript

So, I have a form and I want the user to display the values user fills in the fields as a JSON object at the end when the user clicks the submit button.
In Form.js,
state={
group:[
type-A{col1: "",
col2:""
}
]
}
handleSubmit(event) {
event.preventDefault();
<Credentials value={JSON.stringify(this.state)}/>
}
change = e =>{
this.setState({[e.target.name]: e.target.value})
};
render(){
return(
<div class="classform">
<form >
<label>
Column1:
<br/>
<input type="text"
name="group1"
placeholder="Column1"
value={this.state.column1}
onChange={e=> this.change(e)}
//other fields
//input form fields
<button onClick={this.handleSubmit}>Submit</button>
In Credentials.js,
return (
<p>{value}</p>
)
}
export default Credentials
The above code gives me an error, in handleSubmit() in second line (<Credentials value={JSON.stringify(this.state)}/>)
When the user clicks Submit button, I want to get a JSON object for the data entered in the input fields in the form and update it if the user updates any information in the fields.

Move the component to render method. and use conditional rendering.
state = {credentials: false}
handleSubmit = event => {
event.preventDefault();
this.setState({
credentials: true // display Credentials component
});
};
render() {
return (
<div>
<button onClick={this.handleSubmit}>Submit</button>
{this.state.credentials && (
<Credentials value={JSON.stringify(this.state)} />
)}
</div>
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.21.1/babel.min.js"></script>
<div id="root"></div>
<script type="text/babel">
const Credentials = ({ value }) => {
return <p>{value}</p>;
};
class App extends React.Component {
state = { credentials: false };
handleSubmit = event => {
event.preventDefault();
this.setState({
credentials: true // display Credentials component
});
};
change = e => {
const name = e.target.name;
const nameObj = {};
nameObj[name] = e.target.value;
this.setState({ ...nameObj });
};
render() {
return (
<div>
<input
type="text"
name="col1"
value={this.state['col1']}
onChange={e => this.change(e)}
/>
<button onClick={this.handleSubmit}>Submit</button>
{this.state.credentials && (
<Credentials value={JSON.stringify(this.state)} />
)}
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
</script>

Related

Reactjs : How to route to another form on submit and send submitted data to the new page

I need some help. I have created three form class, Employee, Address and Authentication. Inside the Employee form when the user click the submit button, I want it to go to the Address form page and also send the Employee form data to the Address form page. I know that I can put all this on one page, but it will make it hard to read the code. And I am trying to make it match with my backend (spring boot). initialState is a Json an I am importing it from another file if your wondering.
Code for the Employee class
class Employee extends Component {
state = initialState;
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleChange = event => {
const {formData} = this.state;
this.setState({
formData: {
...formData, // leave other values unchanged
[event.target.name]: event.target.value, // update the changed value
}
});
}
validate = () => {
const {formData, errors} = this.state;
const {firstName, lastName, email, dataOfBirth, phoneNum} = formData;
let {firstNameError, lastNameError, emailError} = errors;
if (!firstName) {
firstNameError = 'First name can not be blank'
}
if (!lastName) {
lastNameError = 'Last name can not be blank'
}
if (!validateEmail(email)) {
emailError = `${email} is not valid email`
}
if (!dataOfBirth) {
console.log(dataOfBirth.length)
dataOfBirthError = 'Enter a valid date of birth'
}
if (!phoneNum) {
phoneNumError = 'Enter a valid phone'
}
if (!validatePhoneNumber(phoneNum)) {
phoneNumError = 'Enter a valid phone number'
}
if (firstNameError || lastNameError) {
this.setState( {
errors: {
firstNameError, lastNameError, emailError,
dataOfBirthError
}
})
return false
}
return true
}
handleSubmit(event) {
event.preventDefault()
const isValid = this.validate();
if (isValid) {
this.setState(initialState)
this.props.push("/addressForm")
}
}
render() {
return (
<div className="container_fluid">
<div className="registration_form_container">
<div className="register_context">
<form action="" onSubmit={this.handleSubmit} className="registration_form">
<div className="form-group">
<input type="text" name="firstName" id="firstName"
placeholder={"Enter first name"}
onChange={this.handleChange}
/>
<span>{this.state.errors.firstNameError}</span>
</div>
<div className="form-group">
<input type="text" name="lastName" id="lastName"
placeholder={"Enter last name"}
onChange={this.handleChange}
/>
<span>{this.state.errors.lastNameError}</span>
</div>
<div className="form-group">
<input type="text" name="email" id="email"
placeholder={"Enter email address"}
onChange={this.handleChange}
/>
<span>{this.state.errors.emailError}</span>
</div>
<div className="form-group custom_btn_container">
<input type="submit" className="btn" value="Register"/>
</div>
</form>
</div>
</div>
</div>
)
}
}
export default Employee;
Code for the Address Class
class Address extends Component {
state = initialState;
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleChange = event => {
const {formData} = this.state;
this.setState({
formData: {
...formData, // leave other values unchanged
[event.target.name]: event.target.value, // update the changed value
}
});
}
validate = () => {
const {state, city, street, zipcode} = formData.employeeAddress;
let {firstNameError, lastNameError, emailError, dataOfBirthError, phoneNumError} = errors;
let {employeeAddressError: {streetError, stateError, cityError, zipcodeError}} = errors
if (!street) {
streetError = "Street can not be blank"
}
if (!city) {
cityError = "Street can not be blank"
}
if (streetError || cityError || stateError) {
console.log(dataOfBirth)
this.setState( {
errors: {
employeeAddressError: {
streetError,
cityError,
stateError,
zipcodeError
}
}
})
return false
}
return true
}
handleSubmit(event) {
event.preventDefault()
const isValid = this.validate();
if (isValid) {
this.setState(initialState)
}
}
render() {
return (
<div className="container_fluid login_form_main_container">
<div className="address_context">
<div className="form-group">
<input type="text" name="street" id="street"
placeholder={"Enter street address"}
onChange={this.handleChange}
/>
<span>{this.state.errors.employeeAddressError.streetError}</span>
</div>
<div className="form-group custom_btn_container">
<input type="submit" className="btn" value="Register"/>
</div>
</div>
</div>
)
}
}
export default Address;
In App.js
return (
<Router>
<div className="App">
<Header />
<div className="content">
<Switch>
<Route path="/register" component={Employee} />
<Route path="/login">
<Login Login={LoginDetail} error={error} />
</Route>
<Route path="/addressForm" component={Address} />
<Route path="/" component={Home} />
</Switch>
</div>
</div>
</Router>
);
Inside the header page
export default function Header () {
return (
<div className="container_fluid">
<div className="navbar_container">
<div className="logo_container">
<h2>Logo</h2>
</div>
<nav className="navbar">
<div className="links_container">
<li><Link to="/">Home</Link></li>
<li><Link to="/department">Department</Link></li>
<li><Link to="/register">Register</Link></li>
<li><Link to="/login">Login</Link></li>
</div>
</nav>
</div>
</div>
)
}
As told in comment , best way of doing so is context api and here is a small demo of context
sandbox link
import React, { createContext, useState } from "react";
export const ActualContext = createContext();
const Contextt = (props) => {
return (
<ActualContext.Provider>
{props.children}
</ActualContext.Provider>
);
};
export default Contextt;
This is the way for creating a context , sandbox code will help you.
You can use the redirect router redirect feature to pass props https://reactrouter.com/web/api/Redirect/to-object
It is not a good way to send form data on router parameters and just for readability of the code no need to add another router, I suggest using state managements like Redux or Context API as react says or if you do not want either of them just add another component for example named EmployeeForm and add Employee and Address component to it (for better UI/UX you can switch between forms as user want) and pass data like this in EmployeeForm:
class EmployeeForm extends Component{
handleSubmit = values => {
this.setState({employee: values})
}
render() {
<Address employee={this.state.employee} />
<Employee onSubmit={this.handleSubmit} />
}
}
then use this.props.employee at Address component. It is event better implementation if the forms are very related and user can go back and forth as submitting without losing the states of any of these two components.

React - Generate input field via dropdown

I am new in React and I am confused, What I want is i have a drop down list with options as 1,2,3,4.... upto n. Suppose if I select on dropdown number 5 then dynamically 5 input fields should get generated. Also for each input field which is created. I should be manually able to remove them with a remove button.
I have created adding of input options but it is manual like we click on add button new option is added and when we click remove that particular option with index gets deleted. You can refer this link for code
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
users: [{firstName: "", lastName: ""}]
};
this.handleSubmit = this.handleSubmit.bind(this);
}
addClick(){
this.setState(prevState => ({
users: [...prevState.users, { firstName: "", lastName: "" }]
}))
}
createUI(){
return this.state.users.map((el, i) => (
<div key={i}>
<input placeholder="First Name" name="firstName" value={el.firstName ||''} onChange={this.handleChange.bind(this, i)} />
<input placeholder="Last Name" name="lastName" value={el.lastName ||''} onChange={this.handleChange.bind(this, i)} />
<input type='button' value='remove' onClick={this.removeClick.bind(this, i)}/>
</div>
))
}
handleChange(i, e) {
const { name, value } = e.target;
let users = [...this.state.users];
users[i] = {...users[i], [name]: value};
this.setState({ users });
}
removeClick(i){
let users = [...this.state.users];
users.splice(i, 1);
this.setState({ users });
}
handleSubmit(event) {
alert('A name was submitted: ' + JSON.stringify(this.state.users));
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
{this.createUI()}
<input type='button' value='add more' onClick={this.addClick.bind(this)}/>
<input type="submit" value="Submit" />
</form>
);
}
}
ReactDOM.render(<App />, document.getElementById('container'));
https://jsfiddle.net/mayankshukla5031/qL83cf2v/1/
But now I want generate it with dropdown, selecting the size of input options let say 5 so dynamically 5 options fields are created.
Can anybody guide me on it please.
Somthing like this should help :
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
users: [1],
options: 1,
};
this.handleSubmit = this.handleSubmit.bind(this);
}
addClick(){
const newUsers = Array.from(Array(Number(this.state.options)), (_, i) => i);
this.setState((prevState) => ({
users: [...prevState.users, ...newUsers],
}));
};
createUI() {
return this.state.users.map((el, i) => (
<div key={i}>
<input
placeholder="First Name"
name="firstName"
value={el.firstName || ""}
onChange={this.handleChange.bind(this, i)}
/>
<input
placeholder="Last Name"
name="lastName"
value={el.lastName || ""}
onChange={this.handleChange.bind(this, i)}
/>
<input
type="button"
value="remove"
onClick={this.removeClick.bind(this, i)}
/>
</div>
));
}
handleChange(i, e) {
const { name, value } = e.target;
let users = [...this.state.users];
users[i] = { ...users[i], [name]: value };
this.setState({ users });
}
removeClick(i) {
let users = [...this.state.users];
users.splice(i, 1);
this.setState({ users });
}
handleSubmit(event) {
alert("A name was submitted: " + JSON.stringify(this.state.users));
event.preventDefault();
}
handleInput = (event) => {
event.preventDefault();
this.setState({ options: event.target.value });
};
render() {
return (
<form onSubmit={this.handleSubmit}>
{this.createUI()}
<select defaultValue={this.state.options} onChange={this.handleInput}>
{Array.from(Array(100), (_, i) => i + 1).map((opt) => (
<option>{opt}</option>
))}
</select>
<input
type="button"
value="add more"
onClick={this.addClick.bind(this)}
/>
<input type="submit" value="Submit" />
</form>
);
}
}
ReactDOM.render(
<App />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
I hope this one helps. Link Output keys with ur input fields.
function App({data}){
const [selectedField, setSelectedField] = useState('');
const [output, setOutput] = useState({}); // u can add a default values of the keys in inital state
useEffect(()=>{
if(selectedField !== ''){
const selectedData = data.filter(el=> name === el.name);
if(selectedData){
setOutput(selectedData);
}
}
},[selectedField, data]);
const onDataFieldRemove = (key) => {
setOutput(prevState => {
delete prevState[key]; // if u dont want unwanted data in ur database or use
// prevState[key] = null; if u want to maintain ur default keys;
return prevState;
});
}
return (<div>... add ur input fields and remove button</div>)
}

redux form keeps not setting initial values multi form

ok I'll admit this is a bit of a hot mess but please bear with me.
Trying to understand why my redux-forms:
A: don't set ititialValues until I have set the store elsewhere in the app.. for eg. if I have another event that calls, getFirstTrip, then its in the store, and can load my form correctly with intialValues.
B: wipe themselves out when I click the router link again. Every time I click the router Link it behaves the same as the initial click.. even though the store is there.. the fields and initialValues are empty.
Using:
react-router, react-redux, react-form
structure like this:
view.js
const onSubmit = (formValues) => {
props.updateTrip(formValues); //< traditional put action to api
};
useEffect(() => {
props.getFutureTrip(); // traditional get action to api
//the action dispatches the reducer to create the vacations.nextVacation.tripInfos
}, []);
return (
<div>
Register Page
<TripRegistrationForm onSubmit={onSubmit} />
<Highlight>
{output}</Highlight>
</div>
);
};
const mapStateToProps = state => {
return {
initialValues: state.vacations.nextVacation.tripInfos //< this doesn't seem necessary.
}
}
export default connect(mapStateToProps,{updateTrip, getFutureTrip}) (TripsRegistration);
Parent form TripRegistrationForm.js
class TripRegistrationForm extends Component {
constructor(props) {
console.log("props", props);
super(props);
}
render() {
const { onSubmit, onChange} = this.props;
return (
<div>
<childForm
onChange={onChange}
onSubmit={onSubmit}
/>
</div>
);
}
}
TripRegistrationForm.propTypes = {
onSubmit: PropTypes.func.isRequired,
};
TripRegistrationForm = reduxForm({ form: "tripRegistration" })(TripRegistrationForm);
TripRegistrationForm = connect((state, ownProps) => ({
initialValues: state.vacations.nextVacation.tripInfos
}))(TripRegistrationForm);
export default TripRegistrationForm;
the form contents (these do populate initial values.. but not consistently... ever)
childForm.js
const RegistrationThirdPage = (props) => {
const { handleSubmit, pristine, submitting, lockForm } = props;
return (
<form disabled={lockForm} className="ui form error" >
<FormSection >
<h2>
Trip & Travel Details
</h2>
<Form.Group>
<Field
name="arriving"
placeholder="Arriving"
component={renderDatePicker}
label="Select your arrival date"
/>
<Field
component={renderSelect}
label="Number of Days"
name="packageDays"
options={singleArrayToKVs(colors)}
/>
</Form.Group>
<Form.Group>
<div>
<Button type="button" disabled={lockForm} className="ui-button green" onClick={handleSubmit(values => props.onSubmit({...values, lockVacation:false}))}>
Save my changes
</Button>
</div><div>
<Button type="submit" className="ui-button primary" disabled={bookVacation(pristine,submitting,lockForm)}
onClick={handleSubmit(values => props.onSubmit({...values, lockVacation:true}))}>
Book my vacation
</Button>
</div>
</Form.Group>
</FormSection>
</form>
);
};
export default reduxForm({
form: "tripRegistration", //Form name is same
destroyOnUnmount: false,
forceUnregisterOnUnmount: true, // <------ unregister fields on unmount
// validate,
})(RegistrationThirdPage);
my actions look like this:
export const getFutureTrip = () => async (dispatch,getState) => {
const{token} = getState().auth
if(!token)
return null;
const response = await axios.get(`/api/trip/futureTrip`,{headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
}});
console.log("response.data from getFutureTrip", response.data);
dispatch({ type: GET_FUTURE_TRIP, payload: response.data });
};

Reset form input values in React

I want create a function using with i can reset value in form inputs without submit. I tried create that function in App Component (resetFormFields) and pass it on props to Form Component. It's preety simply when I want to do this onSubmit (e.target.reset()) but I got stuck when I have to do it without submit, on a different element than the form. Can I do that without adding these values to state?
App:
class App extends Component {
state = {
people: [],
formMessages: [],
person: null
};
handleFormSubmit = e => {
e.preventDefault();
const form = e.target;
const name = form.elements["name"].value;
const username = form.elements["username"].value;
this.addPerson(name, email);
form.reset();
};
resetFormFields = () => {
return;
}
render() {
return (
<div className="App">
<Form formSubmit={this.handleFormSubmit}
reset={this.resetFormFields} />
</div>
);
}
Form:
const Form = props => (
<form className={classes.Form}
id="form"
onSubmit={props.formSubmit}>
<input autoFocus
id="name"
type="text"
defaultValue=""
placeholder="Name..."
/>
<input
id="email"
type="text"
defaultValue=""
placeholder="Email..."
/>
<Button
btnType="Submit"
form="form"
type='submit'>
Submit
</Button>
<label onClick={props.reset}>Reset fields</label>
</form> );
onHandleFormSubmit = (e) =>{
e.preventDefault();
e.target.reset();
}
You need to make your inputs controlled by passing the value you store in your state then you just have to reset the state values and your component value resets.
check this sample below
handleInputChange = (e) => {
let { name, value } = e.target;
this.setState({
...this.state,
inputs: {
[name]: value
}
});
}
your component will now look like
<input name='fullName' value={this.state.inputs.fullName} onChange={this.handleInputChange} />
Your reset function will just clear the state and your input field will be empty since it's controlled via state
resetInputFields = () => {
this.setState({ inputs: {} })
}
you should give set your input values based on component state, then just update the component state
class App extends Component {
state = {
people: [],
formMessages: [],
person: null,
name: "",
email: "",
};
updateState = (newState) => {
this.setState(newState);
}
handleFormSubmit = e => {
e.preventDefault();
this.addPerson(this.state.name, this.state.email);
form.reset();
};
resetFormFields = () => {
this.setState({name:"", email: ""});
}
render() {
return (
<div className="App">
<Form formSubmit={this.handleFormSubmit} updateState={this.updateState}
reset={this.resetFormFields} email={this.state.email} name={this.state.name} />
</div>
);
}
and then
const Form = props => (
<form className={classes.Form}
id="form"
onSubmit={props.formSubmit}>
<input autoFocus
id="name"
type="text"
defaultValue=""
value={this.props.name}
onChange={(e) => this.props.updateState({name: e.target.value})}
placeholder="Name..."
/>
<input
id="email"
type="text"
defaultValue=""
value={this.props.email}
onChange={(e) => this.props.updateState({email: e.target.value})}
placeholder="Email..."
/>
<Button
btnType="Submit"
form="form"
type='submit'>
Submit
</Button>
<label onClick={props.reset}>Reset fields</label>
</form> );

Printing list in <ul>

I want to print a new <ul> list of <li> movies.
I don't see any list nor elements.
I also get a warning:
index.js:2178 Warning: A component is changing an uncontrolled input of type text to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://reactjs.org/docs/forms.html#controlled-components
in input (at index.js:54)
in label (at index.js:52)
in form (at index.js:51)
in div (at index.js:50)
in Movie (at index.js:70)
This is my code:
class Movie extends React.Component {
constructor(props) {
super(props);
this.state = {value: '',
list: [],
checked: true
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.addMovie = this.addMovie.bind(this);
this.listMovies = this.listMovies.bind(this);
}
handleChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
handleSubmit(event) {
event.preventDefault();
this.addMovie();
}
addMovie(value){
this.setState({ list: [...this.state.list, value] });
console.log(...this.state.list);
}
listMovies(){
return(
<ul>
{this.state.list.map((item) => <li key={this.state.value}>{this.state.value}</li>)}
</ul>
);
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>
Movie name:
<input name="movieName" type="text" value={this.state.movieName} onChange={this.handleChange} />
Favorite?
<input name="favorite" type="checkbox" checked={this.state.favorite} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
<button onClick={this.listMovies}>
List Movies
</button>
</div>
);
}
}
ReactDOM.render(
<Movie />,
document.getElementById('root')
);
I would really want to print only my Favorites movies
I'm guessing you want a simple movies list with favorites. Not the best one but working code:
import React from 'react';
import { render } from 'react-dom';
class App extends React.Component {
state = {
favorite: false,
movieName: "",
movies: [],
filter: true,
};
handleChange = (event) =>
event.target.name === "favorite"
? this.setState({ [event.target.name]: event.target.checked })
: this.setState( { [ event.target.name]: event.target.value } );
handleSubmit = ( event ) => {
event.preventDefault();
this.setState({
movies: [...this.state.movies, {name: this.state.movieName, favorite: this.state.favorite }]
});
}
listFavoriteMovies = () => (
<ul>
{this.state.movies
.filter( movie => movie.favorite )
.map( movie => <li>{movie.name}</li>)}
</ul>
);
listAllMovies = () => (
<ul>
{this.state.movies
.map(movie => <li>{movie.name}</li>)}
</ul>
);
changeFilter = () =>
this.setState( prevState => ( {
filter: !prevState.filter,
}))
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>
Movie name:
<input name="movieName" type="text" onChange={this.handleChange} />
Favorite?
<input name="favorite" type="checkbox" onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
<p>Showing only favorite movies.</p>
<ul>
{
this.state.filter
? this.listFavoriteMovies()
: this.listAllMovies()
}
</ul>
<button onClick={this.changeFilter}>Click toggle for all/favorites.</button>
</div>
);
}
}
render(<App />, document.getElementById('root'));
if you initially pass undefined or null as the value prop, the
component starts life as an "uncontrolled" component. Once you
interact with the component, we set a value and react changes it to a
"controlled" component, and issues the warning.
In your code initialise movieName in your state to get rid of warning.
For more information check here

Categories