I am building a game in which I want players to be able to add as few as two or as many as four players. Each player has a name and a character.
The class thus far looks like this:
class UserForm extends Component {
constructor(props) {
super(props);
this.state = {
warnings: {},
error: null,
success: null,
name: "",
piece: "",
};
this.submitRequest = this.submitRequest.bind(this);
}
submitRequest(event) {
event.preventDefault();
var content = {
name: this.state.name,
piece: this.state.piece
};
createUser(content)
.then(() => {
return this.setState({
success: "User Added!",
error: null,
warnings: {}
});
})
.catch(err => {
return this.setState({
error: err.message || "An unexpected error occurred",
warnings: {}
});
});
}
addUser(stateName, e,playerNumber) {
this.setState({
[stateName]: e.target.value,
[playerNumber]:playerNumber
});
}
render() {
var inputFields = (
< div >
<label>
Name:
<input type="text" onChange={e => this.addUser("name", e)} />
</label>
<br />
<label>
Piece:
<input type="text" onChange={e => this.addUser("piece", e)} />
</label>
</div >
);
return (
<div>
<form onSubmit={this.submitRequest}>
<label>
Player 1
</label>
<br />
{inputFields}
Player 2
<br />
{inputFields}
Player 3
<br />
{inputFields}
Player 4
<br />
{inputFields}
<input type="submit" value="Submit" />
</form>
<div className="alert alert-danger mt-2" role="alert">
{this.state.error}
{this.state.success}
</div>
</div >
);
}
}
export default UserForm;
I want all the players to be added to my database when one submit button is pressed. But the data is essentially the same in each case. I've been fiddling around with this by using for loops, etc. for a while but it doesn't seem quite right. Grateful for any help!
Thanks,
First thing
You are passing only two Parameters I.e, name, event to addUser function from input field but in addUser you are trying to access three parameters
addUser(stateName, e,playerNumber) //this is wrong
addUser(stateName, e) //this is correct
Then as you mentioned you want to submit all players info in one submit in this case you need three states
this.state= {
name:””,
piece:””,
userData: []
}
So whenever addUser is called then push player information to a state array and finally send that array when form is submitted. You can iterate list of players info in the backend while constructing a query.
Also when you assign value to name and piece state you have to pass these states as a value to an input field otherwise you won’t get value
<input type=“text” value={this.state.name} onChange={e => this.addUser(“name”, e)} />
Related
I'm a bit ashamed to write this topic because even if I got a HTML formation (4 years ago) I can't resolve my problem :
I can't use the value of an input type text in a function, this is my code:
import React from 'react'
//import db from '../../constants/FirebaseConfig'
class Account extends React.Component {
state = {
name: '',
content: ''
}
_formSubmit(e) {
e.preventDefault()
var name = document.getElementsByName('name')
console.log(name)
}
render(){
return(
<div className="container">
<br></br>
<h3 className="title">Account</h3>
<form id="form">
<br></br>
<label>Titre</label>
<input type="text" name="name" placeholder="Ici le nom de l'Article"/>
<br></br><br></br>
<label>Contenue</label>
<input type="text" name="content" placeholder="Ici le contenue de l'article"/>
<br></br><br></br>
<input type="button" value="suivant" onClick={(e) =>this._formSubmit(e)}/>
<br></br>
</form>
</div>
)
}
}
export default Account
After attempting to debug a few times I think the problem is with the var name object which is a NodeList<input> type.
My goal is to stock the 'name' and 'content' value in a firebase db.
Thank You
Two things:
getElementsByName is plural -- note the 's' in 'Elements'. So it doesn't just return one element, it returns a list, even if there's just one.
Since you are using React, you don't need to pull the value of the input that way at all. Instead, on the input itself, just add a value and onChange property, and then track the value being typed in there as state.
You already have a spot for them in your state, so just go ahead and use that. You'll track it live as they type, not just at form submission.
class Account extends React.Component {
state = {
name: '',
content: ''
}
_formSubmit(e) {
//just process your form _submission_ here
}
onChangeName = (e) => {
this.setState({
name: e.target.value
});
}
render(){
return(
<div className="container">
<br></br>
<h3 className="title">Account</h3>
<form id="form">
<br></br>
<label>Titre</label>
<input type="text" name="name" placeholder="Ici le nom de l'Article" value={this.state.name} onChange={this.onChangeName} />
Without suggesting any workaround but trying to make exactly what you posted work the way you're expecting it to, Here's what I figured out you were trying to do:
Your Problem:
_formSubmit(e) {
e.preventDefault()
var name = document.getElementsByName('name') //Get's a list of Elements with the given name
console.log(name) // This prints the list contained in name variable
}
The Solution:
_formSubmit(e) {
e.preventDefault()
// Assuming you're trying to print the values of all inputs
// First - Get all elements whose values you want to log, as per your initial code
var name = document.getElementsByName('name'); // returns ListNode<HTMLElement>
// Now loop through each input and print it's value to console
name.forEach(
function(input){
console.log(input.value); //Print the value of each input listed
}
);
}
On HandleChange function, it will automatically check the name where you have changed, and it will update the value
import React, { Component } from "react";
export default class App extends Component {
state = {
name: "",
content: "",
datas: []
};
handleChange = e => {
this.setState({
[e.target.name]: e.target.value
});
};
handleSubmit = e => {
e.preventDefault();
const data = {
name: this.state.name,
content: this.state.name
};
this.setState({
datas: [...this.state.datas, data],
name: "",
content: ""
});
};
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input
type="text"
name="name"
value={this.state.name}
onChange={this.handleChange}
placeholder="Ici le nom de l'Article"
/>
<input
type="text"
name="content"
value={this.state.content}
onChange={this.handleChange}
placeholder="Ici le nom de l'Article"
/>
<button>Submit</button>
</form>
<div>
{this.state.datas.map(item => {
return (
<div key={Math.random()}>
<h6>{Math.random()}</h6>
<h4>Name:{item.name}</h4>
<h4>Content:{item.content}</h4>
</div>
);
})}
</div>
</div>
);
}
}
So, I'm pretty new to React and wasn't able to solve this issue. I'm trying to pre-populate a form with information fetched from the database using a parent component. The child component is supposed to show the data as the default and then change states when the user edits the form.
This is how I pass the data as props (Edit 2):
componentDidMount() {
const ticketID = this.props.match.params.ticketID;
axios.get(`http://127.0.0.1:8000/api/tickets/${ticketID}`).then(res => {
this.setState({ ticket: res.data }, () => console.log(this.state.ticket));
});
}
{this.state && this.state.ticket ? (
<TicketForm
requestType="put"
ticketID={this.props.match.params.ticketID}
btnText="Update"
ticket={this.state.ticket}
/>
) : (
<div />
)}
This works fine. I'm able to get the data and console.log it, but I'm unable to set it to be the default state. The form remains blank and when I try to type something on it throws the 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)". <--- Solved (Edit 2)
I followed this tutorial https://www.youtube.com/watch?v=IK9k9hSuYeA&t=373s.
Child component is as follows:
class TicketForm extends React.Component {
constructor(props) {
super(props);
this.state = {
name: props.ticket.name || "",
description: props.ticket.description || ""
};
handleChange = e => {
this.setState({
[e.target.name]: e.target.value
});
};
(...)
render() {
return (
<div className="form-container">
<div className="card mb-3">
<div className="card-header">
<h2>{this.state.btnText} Ticket</h2>
</div>
<div className="card-body">
<form
onSubmit={e =>
this.onSubmit(e, this.props.requestType, this.props.ticketID)
}
>
<div className="form-group">
<label htmlFor="name">Name</label>
<input
type="text"
name="name"
className="form-control form-control-lg"
placeholder="Ticket name"
value={this.state.name}
onChange={e => {
this.handleChange(e);
}}
/>
</div>
<div className="form-group">
<label htmlFor="description">Description</label>
<textarea
name="description"
className="form-control form-control-lg"
rows="3"
placeholder="Ticket Description"
value={this.state.description}
onChange={e => {
this.handleChange(e);
}}
></textarea>
</div>
(...)
)}
I spent hours trying to fix this and read a bunch of posts on here, but still wasn't able to solve this issue. Any help would be much appreciated.
Edit 1: I finally figured out why props is undefined on the constructor. When I pass ticket as props, ticket is still being fetched from the database, so the value received by the constructor is still undefined.
Edit 2: Tried using ternary operator on the parent child to make sure props were only passed on after state was fully set.
This error should only occur if the initial state for name or description is undefined, which you're not checking for in your code.
Try changing your constructor to:
constructor(props) {
super();
this.state = {
name: props.ticket.name || '',
description: props.ticket.description || '',
};
This way, if you have an undefined value for either of them, you still set an empty string as a fallback value.
You are supposed to call super() with props as an argument: super(props)
constructor(props) {
super(props); // <-- instead of super()
this.state = {
name: props.ticket.name,
description: props.ticket.description
};
...
}
I'm not sure that makes a difference, it would seem that this.state.ticket passed before is not being set correctly. Can you try console.log(this.state.ticket) and see if it is indeed an object of the form {name: ..., description: ...}?
This should work: You should understand the basics of state and props before you deep dive. When you initialize a state in a constructor, it is only initialized once. React has no way to update the component state based on props. So, in order to achieve this, they provided a hook getDerivedStateFromProps as the name suggests to derive the state from props. Hope it helps you to clear your queries.
On the other note, I would encourage you to start with React Hooks rather than class components as these errors can be easily handled using useEffect in functional components. I hope I am able to give you a better overview. Let me know if you still find any difficulties.
class TicketForm extends React.Component {
constructor(props) {
super(props);
this.state = {
name: "",
description: ""
};
static getDerivedStateFromProps(props, state) {
// Stringify as to check ticket is an object. In case it is a string
// or integer you can directly, check the equality
if (JSON.stringify(props.ticket) !== JSON.stringify(state)) {
return {
name: props.ticket.name,
description: props.ticket.description
}
}
return null;
}
handleChange = e => {
this.setState({
[e.target.name]: e.target.value
});
};
(...)
render() {
return (
<div className="form-container">
<div className="card mb-3">
<div className="card-header">
<h2>{this.state.btnText} Ticket</h2>
</div>
<div className="card-body">
<form
onSubmit={e =>
this.onSubmit(e, this.props.requestType, this.props.ticketID)
}
>
<div className="form-group">
<label htmlFor="name">Name</label>
<input
type="text"
name="name"
className="form-control form-control-lg"
placeholder="Ticket name"
value={this.state.name}
onChange={e => {
this.handleChange(e);
}}
/>
</div>
<div className="form-group">
<label htmlFor="description">Description</label>
<textarea
name="description"
className="form-control form-control-lg"
rows="3"
placeholder="Ticket Description"
value={this.state.description}
onChange={e => {
this.handleChange(e);
}}
></textarea>
</div>
(...)
)}
I am somewhat new to react, and I am having trouble recognizing whats causing my form to be broken. The submit button works fine and the name adds fine, but i cannot type into my price text-field for some reason. when adding items to the list, the dollar sign for price is adding, but i cant type anything into the price field.
import React, { Component } from 'react';
class ItemForm extends Component {
state = { name: '', price: ''}
handleChange = (e) => {
const { name, value } = e.target
this.setState({ [name]: value })
}
handleSubmit = (e) => {
//stop page from reloading
e.preventDefault();
//add item to groceries array
const { addItem } = this.props
addItem(this.state.name, this.state.price)
// this.props.addItem(this.state.price)
//clear form on submit
this.setState({name: '', price: ''})
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input
required
placeholder='add Grocery Item'
name="name"
value={this.state.name}
onChange={this.handleChange}
/>
<br />
<input
placeholder='add price (optional)'
name="price"
value={this.state.price}
onChange={this.handleChange}
/>
<br />
<input class = "btn btn-primary" type = "submit" value =
"Add" />
</form>
)
}
}
export default ItemForm;
I think you accidentally put price="price" instead of name="price"
I working on a small personal project using React on Rails. I am very new to both of these things.
I have a react component that is a form. I also have another component that has some inputs that the user can add as many as needed. Using the Add Properties button. I am trying to save the state of each input that is added. I could have the component itself save the state but how then would I send it with my fetch post request that happens onClick?
I have looked at react's context API but cant figure out if this would help me. Also I have never used redux so it is possible I should look into that as well.
https://reactjs.org/docs/context.html
https://redux.js.org/basics/usagewithreact
I understand that I don't want to reach into state. So I was trying to figure out how to create an array of objects that will hold the input values of each input pair. But I cannot seem to wrap my mind around how to implement.
class ProductsCreate extends React.Component {
constructor(props) {
super(props);
this.state = {
name: '',
upc: '',
availableOn: '',
inputs: []
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
if (e.target.name === 'name') {
this.setState({ name: e.target.value });
}
if (e.target.name === 'upc') {
this.setState({ upc: e.target.value });
}
if (e.target.name === 'date') {
this.setState({ availableOn: e.target.value });
}
}
submitData = () => {
fetch(`/send_data`, {
method: 'POST',
body: JSON.stringify({
name: this.state.name,
upc: this.state.upc,
availableOn: this.state.availableOn
}),
headers: {
'Content-Type': 'application/json'
},
credentials: 'same-origin'
})
.then(response => {
return response.json;
})
.then(data => {
console.log(data);
});
};
clickHandler = e => {
e.preventDefault();
this.submitData();
};
appendInput = e => {
e.preventDefault();
const newInput = `input-${this.state.inputs.length}`;
this.setState({ inputs: this.state.inputs.concat([newInput]) });
};
render() {
return (
<div className="form_container">
<h1>Products</h1>
<form>
<label>Name</label>
<input type="text" name="name" onChange={this.handleChange} />
<label>UPC</label>
<input type="text" name="upc" onChange={this.handleChange} />
<label>Availiable On</label>
<input
type="text"
name="date"
placeholder="mm/dd/yyyy"
onChange={this.handleChange}
/>
<h1>Properties</h1>
{this.state.inputs.map(input => (
<Properties key={input} />
))}
<button onClick={this.appendInput}>Add Properties</button>
<button onClick={this.clickHandler}>Save</button>
</form>
</div>
);
}
}
export default ProductsCreate;
This is the component that will be added on click
import React from 'react';
class Properties extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="property_container">
<label>Property Name</label>
<input type="text" name="propertyName" />
<label>Property Value</label>
<input type="text" name="propertyValue" />
</div>
);
}
}
export default Properties;
Pass handle change as prop to Properties component and then use that prop in input onChange within your Properties component. Make the following edits. I also suggest if you don't want to continously update the state on every character typed use debounce.
ProductsCreate
{this.state.inputs.map(input => (
<Properties key={input} onChange={this.handleChange} name={input}/>
))}
Properties
<input type="text" name={this.props.name} onChange={this.props.onChange}/>
I have the following reactjs component.
class Login extends Component {
render() {
if (this.props.session.Authenticated) {
return (
<div></div>
);
}
return (
<div>
<input type="text" placeholder="Username" />
<input type="password" placeholder="Password" />
<input
type="button"
value="Login"
onClick={() => this.props.LoginAction("admin", "password")}
/>
</div>
);
}
}
The Login component makes use of a prop that is set via redux, named "session.Authenticated". If an user session is not authenticated, I present a couple of input fields (one for username and another for password) and a Login button. If I press on the Login button I want to emit an action with the username and password values, passed as parameters. Now, how do I get the values of the two input fields for the parameters ?
IOW, I want to remove the hardcoded "admin, password" in the above code with the values of the two input fields. How to achieve this ?
All the examples point to redux-form which is a new dependency that I do not want to add. I want to keep my dependencies minimal and so using only react, redux and react-redux in my project.
Something like this:
class Login extends Component {
constructor(props){
super(props);
this.state = {
model = {
login: "",
password: ""
}
}
}
render() {
if (this.props.session.Authenticated) {
return null;
}
return (
<div>
<input type="text" value={this.state.model.value.login} onChange={e => this.updateModel(e.target.value, 'login')}placeholder="Username" />
<input type="password" value={this.state.model.password }onChange={e => this.updateModel(e.target.value, 'password')} placeholder="Password" />
<input
type="button"
value="Login"
onClick={() => this.props.LoginAction(this.state.model.login, this.state.model.password)}
/>
</div>
);
}
updateModel(value, name){
var model = extend({}, this.state.model);
model[name] = value;
this.setState({model});
}
}