So I am still relatively new to React. I want to get data from three form inputs (and eventually post that to database). I have been using the internet to help, but I figured would try my luck here. Right here I am trying to just log out what the user texts in the inputs.
import React from 'react';
export default class AddForm extends React.Component {
constructor(props) {
super(props);
}
handlePlace(e) {
e.preventDefault();
const text = this.place.value.trim();
console.log(text);
}
handleDate(e) {
const text = this.date.value
console.log(text);
}
handleNotes(e) {
const text = this.notes.value
console.log(text);
}
render() {
return(
<form className="form">
<h4>Add a Memory</h4><br></br>
<p className="add-info">Keep track of all the fun and amazing places you
have been.</p>
<input type="text" name="place" placeholder="place" ref={input =>
this.place = input}></input><br></br>
<input placeholder="time" ref={input => this.date = input}></input><br>
</br>
<input className="notes" placeholder="notes" ref={input => this.notes =
input}></input><br></br>
<button className="save" type="button" onClick=
{this.handleSave}>Save</button>
</form>
);
}
handleSave() {
console.log(this.handlePlace)
console.log(this.handleDate)
console.log(this.handleNotes)
}
}
While it is possible to use refs to do this, it is not recommended to actually touch the dom unless it is necessary. I think it would be helpful
to think about this the 'react way'.
In React, the state of your application drives the view, including inputs. So what you 'should' be doing is letting the state populate the values of your inputs and then update the state to change what is in those inputs. Something like this:
export default class AddForm extends React.Component {
constructor(props) {
super(props);
//change here
this.handlePlace = this.handlePlace.bind(this);
this.handleDate = this.handleDate.bind(this);
this.handleNotes = this.handleNotes.bind(this);
this.handleSave = this.handleSave.bind(this);
this.state = {
placeText: '',
timeText: '',
noteText: '',
};
}
// all changed
handlePlace(e) {
this.setState({ placeText: e.target.value });
}
handleDate(e) {
this.setState({ dateText: e.target.value });
}
handleNotes(e) {
this.setState({ notesText: e.target.value});
}
render() {
return(
<form className="form">
<h4>Add a Memory</h4><br></br>
<p className="add-info">Keep track of all the fun and amazing places you
have been.</p>
// change here
<input type="text" name="place" placeholder="place" value={this.state.placeText} onChange={(e) => this.handlePlace(e)}></input><br></br>
// change here. e is implicitly passed to the onChange handler
<input type="text" placeholder="time" value={this.state.dateText} onChange={this.handleDate}></input><br>
</br>
//change here
<input className="notes" placeholder="notes" value={this.state.notesText} onChange={this.handleNotes}></input><br></br>
<button className="save" type="button" onClick=
{this.handleSave}>Save</button>
</form>
);
}
handleSave() {
console.log(this.state.placeText)
console.log(this.state.dateText)
console.log(this.state.notesText)
}
}
It seems tedious, but this declarative style really pays off in the long run when you're hunting down bugs. It becomes easier to follow the flow of your data through your application.
Related
I'm working on a CV Generator and I don't know how to properly append the school and field of study values to a new div inside React.
Using the onSubmit function I'm able to get the values after filling them out and clicking save, but I can't figure out where to go from here.
Update
What I want to do is take the values from the input and create a new div above the form that displays those values. For example, I want the School value to show
School: University of Whatever
And the same goes for Field of Study.
Field of Study: Whatever
I know how to do this in vanilla JS but taking the values and appending them to the DOM but it doesn't seem to work that way in React.
class Education extends Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
}
onSubmit = (e) => {
e.preventDefault();
const schoolForm = document.getElementById("school-form").value;
const studyForm = document.getElementById("study-form").value;
};
render() {
return (
<>
<h1 className="title">Education</h1>
<div id="content">
<form>
<label for="school">School</label>
<input
id="school-form"
className="form-row"
type="text"
name="school"
/>
<label for="study">Field of Study</label>
<input
id="study-form"
className="form-row"
type="text"
name="study"
/>
<button onClick={this.onSubmit} className="save">
Save
</button>
<button className="cancel">Cancel</button>
</form>
)}
</div>
</>
);
}
}
export default Education;
You should use state in order to save the values then show it when the user submits.
import React from "react";
class App extends React.Component {
constructor(props) {
super(props);
this.state = { scool: "", study: "", showOutput: false };
this.onSubmit = this.onSubmit.bind(this);
}
onSubmit = (e) => {
e.preventDefault();
this.setState({
showOutput: true
});
};
setStudy = (value) => {
this.setState({
study: value
});
};
setSchool = (value) => {
this.setState({
school: value
});
};
render() {
return (
<>
<h1 className="title">Education</h1>
<div id="content">
{this.state.showOutput && (
<>
<div>{`school: ${this.state.school}`}</div>
<div>{`study: ${this.state.study}`}</div>
</>
)}
<form>
<label for="school">School</label>
<input
id="school-form"
className="form-row"
type="text"
name="school"
onChange={(e) => this.setSchool(e.target.value)}
/>
<label for="study">Field of Study</label>
<input
id="study-form"
className="form-row"
type="text"
name="study"
onChange={(e) => this.setStudy(e.target.value)}
/>
<button onClick={this.onSubmit} className="save">
Save
</button>
<button className="cancel">Cancel</button>
</form>
)
</div>
</>
);
}
}
export default App;
I have also added 2 functions to set state and a condition render based on showOutput.
You don't append things to the DOM in react like you do in vanilla. You want to conditionally render elements.
Make a new element to display the data, and render it only if you have the data. (Conditional rendering is done with && operator)
{this.state.schoolForm && this.state.studyform && <div>
<p>School: {this.state.schoolForm}</p>
<p>Field of Study: {this.state.studyForm}</p>
</div>}
The schoolForm and studyForm should be component state variables. If you only have them as variables in your onSubmit, the data will be lost after the function call ends. Your onSubmit function should only set the state, and then you access your state variables to use the data.
Do not use document.getElementById. You don't want to use the 'document' object with react (Almost never).
You can access the element's value directly using the event object which is automatically passed by onSubmit.
handleSubmit = (event) => {
event.preventDefault();
console.log(event.target.school.value)
console.log(event.target.study.value)
}
Good day, I'm new React and firebase, Today, I using React and Firebase to display, add and delete data. I have some data in the firebase and display it. Now, I want delete some of the data, but I don't know. I create a button delete that whenever the user click it the data will be removed. Please help..
import React , { Component, Fragment } from 'react';
class Form extends Component {
constructor(){
super();
this.state = {
db: [],
name: "",
city: ""
}
this.changHandle = this.changHandle.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
this.removeData = this.removeData.bind(this)
}
componentDidMount() {
const { firestore } = this.props
firestore.collection('cafes').orderBy('name')
.onSnapshot((db) => {
const data = db.docs.map(datas => datas.data())
this.setState({
db: data
})
})
}
changHandle(event){
const {name, value} = event.target
this.setState({[name]:value})
}
handleSubmit(e){
e.preventDefault();
this.props
.firestore
.collection('cafes')
.add({city: this.state.city, name: this.state.name})
}
removeData(id){
this.props
.firestore
.collection('cafes').doc(id).delete();
}
render(){
return (
<div>
<form onSubmit={this.handleSubmit} autoComplete = "off">
<input
type="text"
name="name"
value={this.state.name}
placeholder="Name"
onChange={this.changHandle}
/><br/>
<input
type="text"
name="city"
value={this.state.city}
placeholder="City"
onChange={this.changHandle}
/><br/>
<button type="submit">Add user</button>
</form>
<p>Name:{this.state.name} {this.state.city}</p>
{this.state.db.map(data =>
<div>
<li key={data.id}>{data.name} {data.city}</li>
<button onClick={() => this.removeData(data.id)}>Delete</button>
</div>)
}
</div>
)
}
}
export default Form
MyApplication
The problem is that you are not setting an id for your document in firestore so it is just assigning a random one. In your handle submit button you need to do something like this:
const id = new Date().getTime()
this.props
.firestore
.collection('cafes').doc(id)
.add({city: this.state.city, name: this.state.name, id: id})
class App extends Component {
constructor() {
super()
this.state = {
firstName: ""
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(event) {
this.setState({
firstName: event.target.value
})
}
render() {
return (
<form>
<input type="text" placeholder="First Name" onChange={this.handleChange} />
<h1>{this.state.firstName}</h1>
</form>
);
}
}
export default App;
Hello all, I am currently studying React and seem to be having a hard time grasping all of it. The code that I have here works in that it will show in browser what the user is typing in the input box. What I cannot seem to figure out or get to work, is mapping what is typed in the input to stay on the screen. I.e. when I hit enter, it refreshes and the name goes away. I am trying to now create an unordered list to keep each name displayed on the screen. Any help or links would be greatly appreciated. Thank you
Just add new function (this describe what should be after submit this form) in this case You use:
event.preventDefault() -
The Event interface's preventDefault() method tells the user agent
that if the event does not get explicitly handled, its default action
should not be taken as it normally would be
onSubmit(event){
event.preventDefault()
}
and on form:
<form onSubmit={this.onSubmit}>
To create unordered list use something like this (credit for Robin Wieruch):
import React from 'react';
const initialList = [
'Learn React',
'Learn Firebase',
'Learn GraphQL',
];
const ListWithAddItem = () => {
const [value, setValue] = React.useState('');
const [list, setList] = React.useState(initialList);
const handleChange = event => {
setValue(event.target.value);
};
const handleSubmit = event => {
if (value) {
setList(list.concat(value));
}
setValue('');
event.preventDefault();
};
return (
<div>
<ul>
{list.map(item => (
<li key={item}>{item}</li>
))}
</ul>
<form onSubmit={handleSubmit}>
<input type="text" value={value} onChange={handleChange} />
<button type="submit">Add Item</button>
</form>
</div>
);
};
export default ListWithAddItem;
Currently in react js, when I want to bind a text area or an input with a "state", I will need to set the onChange method and setState() everytime user type in a single letter
I heard if you setState react js refresh and re-render everything in this component
Is there any more efficient way to do so? using "shouldComponentUpdate" will be improper in this case since if I don't make "state" update, all user input will be stuck..
Well, that's how you implement controlled input elements in React.
However, if performance is a major concern of yours, you could either isolate your input element in a separate stateful component, hence only triggering a re-render on itself and not on your entire app.
So something like:
class App extends Component {
render() {
return (
<div>
...
<MyInput />
...
</div>
);
}
}
class MyInput extends Component {
constructor() {
super();
this.state = {value: ""};
}
update = (e) => {
this.setState({value: e.target.value});
}
render() {
return (
<input onChange={this.update} value={this.state.value} />
);
}
}
Alternatively, you could just use an uncontrolled input element. For example:
class App extends Component {
render() {
return (
<div>
...
<input defaultValue="" />
...
</div>
);
}
}
Though, note that controlled inputs are generally recommended.
As #Chris stated, you should create another component to optimize the rerendering to only the specified component.
However, there are usecases where you need to update the parent component or dispatch an action with the value entered in your input to one of your reducers.
For example I created a SearchInput component which updates itself for every character entered in the input but only call the onChange function only if there are 3 characters at least.
Note: The clearTimeout is useful in order to call the onChange function only when the user has stopped typing for at least 200ms.
import React from 'react';
class SearchInput extends React.Component {
constructor(props) {
super(props);
this.tabTimeoutId = [];
this.state = {
value: this.props.value,
};
this.onChangeSearch = this.onChangeSearch.bind(this);
}
componentWillUpdate() {
// If the timoutId exists, it means a timeout is being launch
if (this.tabTimeoutId.length > 1) {
clearTimeout(this.tabTimeoutId[this.tabTimeoutId.length - 2]);
}
}
onChangeSearch(event) {
const { value } = event.target;
this.setState({
value,
});
const timeoutId = setTimeout(() => {
value.length >= this.props.minSearchLength ? this.props.onChange(value) : this.props.resetSearch();
this.tabTimeoutId = [];
}, this.props.searchDelay);
this.tabTimeoutId.push(timeoutId);
}
render() {
const {
onChange,
minSearchLength,
searchDelay,
...otherProps,
} = this.props;
return <input
{...otherProps}
value={this.state.value}
onChange={event => this.onChangeSearch(event)}
/>
}
}
SearchInput.propTypes = {
minSearchLength: React.PropTypes.number,
searchDelay: React.PropTypes.number,
};
SearchInput.defaultProps = {
minSearchLength: 3,
searchDelay: 200,
};
export default SearchInput;
Hope it helps.
You need to bind the onChange() event function inside constructor like as code snippets :
class MyComponent extends Component {
constructor() {
super();
this.state = {value: ""};
this.onChange = this.onChange.bind(this)
}
onChange= (e)=>{
const formthis = this;
let {name, value} = e.target;
formthis.setState({
[name]: value
});
}
render() {
return (
<div>
<form>
<input type="text" name="name" onChange={this.onChange} />
<input type="text" name="email" onChange={this.onChange} />
<input type="text" name="phone" onChange={this.onChange} />
<input type="submit" name="submit" value="Submit" />
</form>
</div>
);
}
}
You don't need a complicated react solution to this problem, just a little common sense about when to update state. The best way to achieve this is to encapsulate your setState call within a timeout.
class Element extends React.Component {
onChange = (e) => {
clearTimeout(this.setStateTimeout)
this.setStateTimeout = setTimeout(()=> {
this.setState({inputValue: e.target.value})
}, 500)
}
}
This will only set state on your react element a 500ms after the last keystroke and will prevent hammering the element with rerenders as your user is typing.
I am trying to pass data from one component to another. but it has no parent child relation and it is independent from each other. i am able to set the state but problem is after clicking enter my text data get cleared. not sure why,
export class EmpSearch extends React.Component {
// Not needed anymore as state going to Redux and not local component state
/*
constructor(props) {
super(props);
this.state = {
Empnumber: ''
};
}
*/
EmpSearch = (e) => {
if (e.key === 'Enter') {
browserHistory.push('/Emp/' + e.target.value);
}
}
updateEmpNumber(e) {
this.props.dispatch({
type: 'UPDATE_EMP_NUMBER',
payload: e.target.value
});
}
render() {
return (
<div className="row">
<form>
<div className="form-group">
<label htmlFor="Empnumber">Emp Number</label>
<input type="text" className="form-control" id="Empnumber" placeholder="Emp Number" value={this.props.Empnumber} onChange={this.updateEmpNumber.bind(this)} onKeyPress={this.EmpSearch}/>
</div>
</form>
</div>
);
}
}
function mapStateToProps(state){
return {
Empnumber: state.Empnumber
}
}
export default connect(mapStateToProps)(EmpSearch);
browserHistory.push('/Emp/' + e.target.value); is probably causing your component to be unmounted and remounted.
You say the value is stored in the redux state, but I can't see where you update the redux state.