I want to create an input field in React.
It basically should display the entered input real-time (managed this part).
However, it also should display a message "no data provided!" when nothing was entered.
My if statement isn't working? Why?
import React from "react"
import ReactDOM from "react-dom"
class Exercise1 extends React.Component {
constructor() {
super()
this.state = {
firstName:""
}
this.handleChange = this.handleChange.bind(this)
}
handleChange (event) {
this.setState({
[event.target.name]: event.target.value
})
}
render() {
let display
if(this.state.firstname != "") {
display=this.state.firstName
} else {
display="no data provided!"
}
return (
<div>
<form>Input:
<input
type="text"
name="firstName"
placeholder = "no data provided!"
value={this.state.firstName}
onChange={this.handleChange}
/>
</form>
<h1>{display}</h1>
</div>
)
}
}
export default Exercise1
PS: please stick with your answer as much as possible to the code above since I am a beginner and can't follow too different approaches.
You have a typo here. Your state variable is firstName (with capital N), but you are trying to check condition with firstname (with small n). You should do this,
if(this.state.firstName != "") {
display = this.state.firstName
} else {
display = "no data provided!"
}
Demo
Hi you can use your if like this
import React from "react"
import ReactDOM from "react-dom"
class Exercise1 extends React.Component {
constructor() {
super()
this.state = {
firstName:""
}
}
handleChange = (event) => {
this.setState({
[event.target.name]: event.target.value
})
}
render() {
const { firstName } = this.state
return (
<div>
<form>Input:
<input
type="text"
name="firstName"
placeholder = "no data provided!"
value={this.state.firstName}
onChange={this.handleChange}
/>
</form>
<h1>{firstName ? firstName : "no data provided!"}</h1>
</div>
)
}
}
export default Exercise1
Related
Could you please tell me How to show different error message on button click ? Actually when pattern is not matched it should show pattern is not matched .Currently I am only able to show required message .here is my code
<TextField
inputRef={register({ required: true, pattern: /^[A-Za-z]+$/i })}
label="First name"
variant="outlined"
name="firstName"
required
helperText={errors.firstName && "First name is required"}
error={errors.firstName ? true : false}
/>
https://codesandbox.io/s/react-hook-form-get-started-j39p0
You could use something like react-validation package. Using it you will be able to reduce the amount of code you will need to write for your own validation, and also consider the corner cases.
You define your validation functions at first:
import validator from 'validator';
const required = (value) => {
if (!value.toString().trim().length) {
// We can return string or jsx as the 'error' prop for the validated Component
return 'require';
}
};
const email = (value) => {
if (!validator.isEmail(value)) {
return `${value} is not a valid email.`
}
};
...
And after that plug them into your form, wherever required:
export default class Login extends Component {
render() {
return <Form>
<h3>Login</h3>
<div>
<label>
Email*
<Input value='email#email.com' name='email' validations={[required, email]}/>
</label>
</div>
...
Check out their documentation. It is pretty widely used package and should be easy to implement.
Hope this will match with your example
Run it here
replace the regular expression with yours
import React from "react";
import ReactDOM from "react-dom";
import { useForm } from "react-hook-form";
import { TextField, Button } from "#material-ui/core";
import Autocomplete from "#material-ui/lab/Autocomplete";
import { object, string } from "yup";
class Usernames extends React.Component {
constructor(props) {
super(props)
this.state = { errorText: '', value: props.value }
}
onChange(event) {
if (event.target.value.match(/[abc]/g)) {
this.setState({ errorText: 'errr' })
} else {
this.setState({ errorText: 'Invalid format' })
}
}
render() {
return (
<TextField hintText="Firstname"
floatingLabelText="Firstname"
name="name"
helperText={this.state.errorText ? this.state.errorText : ""}
errorText= {this.state.errorText}
onChange={this.onChange.bind(this)}
/>
)
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Usernames />, rootElement);
I am trying to update the text field as it is being typed on the input field
Here what I have done so far
constructor() {
super();
this.state = {
text: ""
};
}
changeText(evt){
let txt = this.state.text ;
txt = evt.target.value
this.setState({
text: txt
})
}
render() {
return(
<div>
<input name="option" type="text" onPress={(evt) => changeText(evt)}/>
<div>{this.state.text}</div>
</div>
)
}
There is no result showing up as I update the input element
You need to do this way.
it's easy to read and reusable.
the state will be created on the name of the form element.
import React, { Component } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import './style.css';
class App extends Component{
constructor() {
super();
this.state = {
option: ""
};
}
changeText = evt =>{
this.setState({[evt.target.name]:evt.target.value})
}
render() {
return(
<div>
<input name="option" type="text" value={this.state.option} onChange={this.changeText} />
<div>{this.state.option}</div>
</div>
)
}
}
render(<App />, document.getElementById('root'));
You have to set the value of input to the state <input name="option" type="text" value={this.state.text} onPress={(evt) => changeText(evt)}/>
Check this stackblitz
https://stackblitz.com/edit/react-ntkqxw
you need to change onPress to onChange & changeText(evt) to this.changeText(evt)
You can use onChangeText inside your input field and then use the state value wherever you want to show it, like:
onChangeText{(value)=>this.setState({text: value })}
import React, { Component } from "react";
class Learning extends Component {fName = React.createRef();onForm = e => {
e.preventDefault();console.log(this.fName.value.value);};render() {return (<div><form onSubmit={this.onForm}><inputtype="text" placeholder="Enter Your First Name" ref={this.fName} /><button type="submit" style={BtnStyle}>Submit</button></form></div>);}}export default Learning;
You have to use the current property on the ref:
console.log(this.fName.current.value);
https://reactjs.org/docs/refs-and-the-dom.html#accessing-refs
But consider using a change event and state
class Learning extends Component {
constructor() {
this.state = {
fName: ''
}
}
updateName = (e) => {
this.setState({fName: e.target.value});
}
onForm = (e) => {
e.preventDefault();
console.log(this.state.fName):
}
render() {
return (
<form onSubmit={this.onForm}>
<input onChange={this.updateName} />
</form>
):
}
}
Check the react docs again, refs created using React.createRef() are accessed like this.fName.current.XXX.
So I am trying to get a timestamp on the time a post is made, but fire.database.ServerValue.TIMESTAMP doesn't seem to be working in the addTicket function. When I post the ticket, it doesn't load to the Pending page, and just has variables ticketTitle & ticketBody in the Ask URL. I think I am just confused on how the timestamp works in firebase. How do I properly add the timestamp of the post to the database tuple?
Ask.js:
import React, { Component } from 'react';
import AskForm from '../../components/AskForm.js';
import fire from '../../config/Fire.js';
import { Link, withRouter } from 'react-router-dom'
class Ask extends Component {
constructor(props){
super(props);
this.addTicket = this.addTicket.bind(this);
this.database = fire.database().ref().child('tickets');
this.state = {
tickets: [],
userId: this.props.user.uid
}
}
componentDidMount(){
fire.database().ref('/users/' + this.props.user.uid).once('value').then(function(snapshot) {
var FirstName = (snapshot.val() && snapshot.val().userFirstName);
// ...
console.log(FirstName);
});
}
addTicket(title, body){
this.database.push().set({ ticketUserId: this.props.user.uid, ticketTitle: title, ticketBody: body, ticketStatus: 'pending', ticketTime: fire.database.ServerValue.TIMESTAMP});
alert("Your question has been submitted.")
this.props.history.push('/pending')
}
render() {
return (
<div>
<div className="m-container">
</div>
<div>
<AskForm addTicket={this.addTicket} />
</div>
</div>
);
}
}
export default withRouter(Ask);
AskForm.js
import React, { Component } from 'react';
class AskForm extends Component{
constructor(props){
super(props);
this.state = {
ticketBody: '',
ticketTitle: ''
};
this.handleChange = this.handleChange.bind(this);
this.writeTicket = this.writeTicket.bind(this);
}
// When the user input changes, set the ticketTitle or ticketBody
// to the value of what's in the input box.
handleChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
writeTicket(){
if(this.state.ticketTitle === '' || this.state.ticketBody === ''){
alert('Please complete all fields.')
} else {
// Call a method that sets the ticketTitle and ticketBody for a ticket to
// the value of the input
this.props.addTicket(this.state.ticketTitle, this.state.ticketBody);
// Set inputs back to an empty string
this.setState({
ticketBody: '',
ticketTitle: ''
})
}
}
render(){
return(
<div class="s-container">
<form>
<label for="ticketTitle">Title: </label>
<input
id="ticketTitle"
name="ticketTitle"
type="text"
placeholder="A short sentence to identify your issue"
value={this.state.ticketTitle}
onChange={this.handleChange}
/>
<br/>
<br/>
<label for="ticketBody">Description: </label>
<textarea
id="ticketBody"
name="ticketBody"
placeholder="Placeholder"
value={this.state.ticketBody}
onChange={this.handleChange}
/>
<button
className="m-btn"
onClick={this.writeTicket}>
Submit
</button>
</form>
</div>
)
}
}
export default AskForm;
Revisited my question:
I need to import firebase directly using import * as firebase from 'firebase'; instead of from my config file. Then just pushed the time value to the database with my other values. See below for example.
Code:
import * as firebase from 'firebase';
addMessage(body){
this.questionDatabase.child(this.state.questionId).child('messages').push().set({
messageUserId: fire.auth().currentUser.uid,
messageBody: body,
time: firebase.database.ServerValue.TIMESTAMP
});
}
This works to create a timestamp client side is using firestore: (In this case I export it from my main firebase.js file)
import firebase from "firebase/compat/app";
import "firebase/compat/firestore";
export const serverStamp = firebase.firestore.Timestamp
To use it after importing serverStamp:
var stampNow = serverStamp.now()
I have a few components that each contain inputs, and on the main component I have a button that will send that information all at once to the server. The problem is that the main component that has the button doesn't have the input content of the child components.
In the past I've passed down a method that would send the content back up into the state, but is there an easier less painful way of doing it? It just feels like an odd way of doing that.
Here's a short example of what I have and what I mean.
Main component:
import React from 'react';
import { Button } from 'react-toolbox/lib/button';
import Message from './Message';
class Main extends React.Component {
constructor() {
super();
this.state = { test: '' };
}
render() {
return (
<div className="container mainFrame">
<h2>Program</h2>
<Message />
</div>
);
}
}
export default Main;
And the message component:
import React from 'react';
import axios from 'axios';
import Input from 'react-toolbox/lib/input';
class Message extends React.Component {
constructor() {
super();
this.state = { message: '' };
this.handleChange = this.handleChange.bind(this);
}
handleChange(value) {
this.setState({ message: value });
}
render() {
return (
<Input
type="text"
label="Message"
name="name"
onChange={this.handleChange}
/>
);
}
}
export default Message;
To answer your question, yes. You can try using refs. Add a ref to Message component, and you will be able to access the child component's methods, state and everything. But thats not the conventional way, people generally use callbacks, as you mentioned earlier.
import React from 'react';
import { Button } from 'react-toolbox/lib/button';
import Message from './Message';
class Main extends React.Component {
constructor() {
super();
this.state = { test: '' };
}
clickHandler () {
let childState = this.refs.comp1.state //returns the child's state. not prefered.
let childValue = this.refs.comp1.getValue(); // calling a method that returns the child's value
}
render() {
return (
<div className="container mainFrame">
<h2>Program</h2>
<Message ref="comp1"/>
<Button onClick={this.clickHandler} />
</div>
);
}
}
export default Main;
import React from 'react';
import axios from 'axios';
import Input from 'react-toolbox/lib/input';
class Message extends React.Component {
constructor() {
super();
this.state = { message: '' };
this.handleChange = this.handleChange.bind(this);
}
handleChange(value) {
this.setState({ message: value });
}
getValue () {
return this.state.message;
}
render() {
return (
<Input
type="text"
label="Message"
name="name"
onChange={this.handleChange}
/>
);
}
}
export default Message;
You are doing what is suggested in docs so it's a good way.
I have a button that will send that information all at once to the server
I assume then it might be form you can use. If so you can just handle onSubmit event and create FormData object containing all nested input field names with their values (even in children components). No need for callbacks then.
handleSubmit(e){
e.preventDefault();
const form = e.currentTarget;
const formData = new FormData(form); // send it as a body of your request
// form data object will contain key value pairs corresponding to input `name`s and their values.
}
checkout Retrieving a FormData object from an HTML form