React JS validate form - javascript

Hello friends I'm pretty new to React JS so I want to ask you this:
I have a Parent and Child component. In Child component I have inputs (name,email etc) and in Parent component I have Button to save the input data in database.
My question is: How to validate inputs(i want them to be required) so the button can NOT call saveData function(to save in database) if the inputs are empty.
Here is Parent Component:
class Parent extends Component{
saveData = (e) => {
//some code
}
render() {
return (
<div>
<Child/>
<Button color="primary" onClick={this.saveData}>Submit</Button>
</div>
);
}
}
And here is Child Component:
class Child extends React.Component {
onInputChange = (e) => {
e.preventDefault();
this.props.onInputChange(e.target.name, e.target.value);
};
render() {
return (
<FormGroup>
<Input name="email" onChange={this.onInputChange}/>
</FormGroup>
);
}
}

I cant see the implementation of your onInputChange function but looks like its coming from parent component.
So, if you have onInputChange in your parent component then, you can have a disable state that is passed to your Button component and you need to set that disable state when onInputChange is called i.e
onInputChange = (name, value) => {
if(value === ''){
this.setState({disable: true});
}
else{
this.setState({
name: value;
disable:false
})
}
}
And pass it to your button i.e.
<Child />
<Button color="primary" disabled={disable} onClick={this.saveData}>

Related

React - How can I make a component from a function inside a parent component?

New to React - I have a component AddForm that looks like this:
class AddForm extends React.Component {
constructor(props) {
super(props);
this.state = {
name: "",
};
}
handleInput = event => {
this.setState({ name: event.target.value });
};
logValue = () => {
console.log(this.state.name)
return <Display text={this.state.name} />
};
render() {
return (
<div>
<input onChange={this.handleInput}
placeholder="Type Here" type="text" />
<button onClick={this.logValue}
type="submit">Submit</button>
</div>
)
}
}
And when the user clicks on the "Submit" button I want it to display what was in the form. I stored the value in form in this.state.name and my component for displaying the text inside of the form looks like this:
class Display extends React.Component {
render() {
return (
<h1>{this.props.text}</h1>
)
}
}
I know that I the state.name can access the form because I console.logged it. I just want to know why my return statement in the logValue function in the AddForm component isn't creating a new component, Display, and how can I make it work?
The click of the button should result in a state change that the render method then uses to return the Display component - something like:
logValue = () => {
this.setState({ showingName: !this.state.showingName });
}
render() {
return (
<div>
{
this.state.showingName ? <Display text={this.state.name} /> : null
}
<input onChange={this.handleInput}
placeholder="Type Here" type="text" />
<button onClick={this.logValue}
type="submit">Submit</button>
</div>
)
}

How to pass data to a form in ReactJS?

How do you pass data to a form to edit in ReactJS?
I have 3 components, Table, Form and a parent component.
return (
<Table />
<Form />
);
Table renders a list of items and each row has an Edit button
So, when the Edit button is clicked, it will call an edit function (passed from parent using props) with the id of the item.
this.props.edit('iAmTheIdOfTheItem');
The edit function will set the id in the parent component state.
edit = (id) => {
this.setState({ id })
}
The selected item is passed to the Form component.
<Form item={ items.find(item => item.id === this.state.id) } />
This should store the current passed data in the Form component state. So that I can use that data to make any changes.
When the update button will be clicked, it will pass the state to the parent component.
Possible Solutions
componentDidMount
I can't set the state using componentDidMount since the Form component is already mounted.
id && < Form />
While this can help me use componentDidMount. But the problem is that the Form component is wrapped in a Modal component. So, closing animation will not work when I update the value and clear the id in the state.
getDerivedStateFromProps
I can use this one as a last resort but it looks like a hack to me and I'm not sure if I really need this. Since I've more than 5 forms, adding this every form does not look good to me.
Any better approach? Suggestions?
This should be helpful
https://codesandbox.io/s/82v98o21wj?fontsize=14
You can use Refs for this.
Reference the child using this.child = React.createRef() and pass this to your child component like this ref={this.child}. Now you can use that child's functions like this this.child.current.yourChildsFunction("object or data you want to pass") then on that child function, just set your form state using the data passed.
Here's a complete example. Sandbox HERE
class Parent extends Component {
constructor(props) {
super(props);
this.child = React.createRef();
}
onClick = () => {
this.child.current.fillForm("Jackie Chan");
};
render() {
return (
<div>
<button onClick={this.onClick}>Click</button>
<br />
<br />
<Child ref={this.child} />
</div>
);
}
}
class Child extends Component {
constructor(props) {
super(props);
this.state = {
name: ""
};
}
handleChange = ({ target }) => {
this.setState({ [target.name]: target.value });
};
fillForm(passedvalue) {
this.setState({ name: passedvalue });
}
render() {
return (
<form>
<label for="name">Name</label>
<input
id="name"
name="name"
onChange={this.handleChange.bind(this)}
value={this.state.name}
/>
</form>
);
}
}
Here an updated version of your sandbox: https://codesandbox.io/s/jprz3449p9
The process is quite basic:
You send the edited item to your form (via props)
The form stores a copy in its own state
When you change the form, it changes the local form state
On submit, you send the state to your parent with a callback (onUpdate here)
What you were missing:
Refresh the local form state when props change
if (!item || id !== item.id) {
this.setState(item);
}
Notify the parent when the item is updated with a callback
update = event => {
event.preventDefault();
this.props.onUpdate(this.state);
this.setState(emptyItem);
};

React - Can A Child Component Send Value Back To Parent Form

The InputField & Button are custom components that go into a form to create a form. My issue is how do I send the data back up to form so that on button click, I can fire ajax on the form with data (username & password):
export default auth.authApi(
class SignUpViaEmail extends Component{
constructor(props){
super(props);
this.state = {
email : "",
password : ""
};
this.storeEmail = this.storeEmail.bind( this );
this.storePassword = this.storePassword.bind( this );
}
storeEmail(e){
this.setState({ email : e.target.value });
}
storePassword(e){
this.setState({ password : e.target.value });
}
handleSignUp(){
this.props.handleSignUp(this.state);
}
render(){
return(
<div className="pageContainer">
<form action="" method="post">
<InputField labelClass = "label"
labelText = "Username"
inputId = "signUp_username"
inputType = "email"
inputPlaceholder = "registered email"
inputClass = "input" />
<Button btnClass = "btnClass"
btnLabel = "Submit"
onClickEvent = { handleSignUp } />
</form>
</div>
);
}
}
);
Or Is it not recommended & I should not create custom child components within the form?
child component => InputField
import React,
{ Component } from "react";
export class InputField extends Component{
constructor( props ){
super( props );
this.state = {
value : ""
};
this.onUserInput = this.onUserInput.bind( this );
}
onUserInput( e ){
this.setState({ value : e.target.value });
this.props.storeInParentState({[ this.props.inputType ] : e.target.value });
}
render(){
return <div className = "">
<label htmlFor = {this.props.inputId}
className = {this.props.labelClass}>
{this.props.labelText}
</label>
<input id = {this.props.inputId}
type = {this.props.inputType}
onChange = {this.onUserInput} />
<span className = {this.props.validationClass}>
{ this.props.validationNotice }
</span>
</div>;
}
}
Error : I get the error e.target is undefined on the parent storeEmail func.
React's one-way data-binding model means that child components cannot send back values to parent components unless explicitly allowed to do so. The React way of doing this is to pass down a callback to the child component (see Facebook's "Forms" guide).
class Parent extends Component {
constructor() {
this.state = {
value: ''
};
}
//...
handleChangeValue = event => this.setState({value: event.target.value});
//...
render() {
return (
<Child
value={this.state.value}
onChangeValue={this.handleChangeValue}
/>
);
}
}
class Child extends Component {
//...
render() {
return (
<input
type="text"
value={this.props.value}
onChange={this.props.onChangeValue}
/>
);
}
}
Take note that the parent component handles the state, while the child component only handles displaying. Facebook's "Lifting State Up" guide is a good resource for learning how to do this.
This way, all data lives within the parent component (in state), and child components are only given a way to update that data (callbacks passed down as props). Now your problem is resolved: your parent component has access to all the data it needs (since the data is stored in state), but your child components are in charge of binding the data to their own individual elements, such as <input> tags.
Addendum
In response to this comment:
What if we render a list of the child component? Using this single source of truth in Lifting state up technique will let the parent controls all the state of all the child inputs right? So how can we access each of the value input in the child component to (which is rendered as list) from the parent component?
For this case, you may map a child component for each element in the list. For example:
class Parent extends Component {
//...
handleChangeListValue = index => event => {
this.setState({
list: this.state.list
.map((element, i) => i === index ? event.target.value : element)
});
}
//...
render() {
return this.state.list.map((element, i) => (
<Child
value={element}
onChangeValue={this.handleChangeListValue(i)}
/>
));
P.S. Disclaimer: above code examples are only for illustrative purposes of the concept in question (Lifting State Up), and reflect the state of React code at the time of answering. Other questions about the code such as immutable vs mutable array updates, static vs dynamically generated functions, stateful vs pure components, and class-based vs hooks-based stateful components are better off asked as a separate question altogether.
React class component
Parent.js
import React, { Component } from 'react';
import Child from './child'
class Parent extends Component {
state = {
value: ''
}
onChangeValueHandler = (val) => {
this.setState({ value: val.target.value })
}
render() {
const { value } = this.state;
return (
<div>
<p> the value is : {value} </p>
<Child value={value} onChangeValue={this.onChangeValueHandler} />
</div>
);
}
}
export default Parent;
Child.js
import React, { Component } from 'react';
class Child extends Component {
render() {
const { value , onChangeValue } = this.props;
return (
<div>
<input type="text" value={value} onChange={onChangeValue}/>
</div>
);
}
}
export default Child;
React hooks
Parent.js
import { useState } from "react";
import Child from "./child";
export default function Parent() {
const [value, changeValue] = useState("");
return (
<div>
<h1>{value}</h1>
<Child inputValue={value} onInputValueChange={changeValue} />
</div>
);
}
Child.js
export default function Child(props) {
return (
<div>
<input
type="text"
value={props.inputValue}
onChange={(e) => props.onInputValueChange(e.target.value)}/>
</div>
);
}
Parent.js
import SearchBar from "./components/SearchBar";
function App() {
const handleSubmit = (term) => {
//Log user input
console.log(term);
};
return (
<div>
<SearchBar onPressingEnter={handleSubmit} />
</div>
);
}
export default App;
Child.js
import { useState } from "react";
function SearchBar({ onPressingEnter }) {
const [UserSearch, setname] = useState("[]");
/* The handleChange() function to set a new state for input */
const handleChange = (e) => {
setname(e.target.value);
};
const onHandleSubmit = (event) => {
//prevent form from making a http request
event.preventDefault();
onPressingEnter(UserSearch);
};
return (
<div>
<form onSubmit={onHandleSubmit}>
<input
type="search"
id="mySearch"
value={UserSearch}
onChange={handleChange}
name="q"
placeholder="Search the siteā€¦"
required
/>
</form>
</div>
);
}
export default SearchBar;
You can add a "ref name" in your InputField so you can call some function from it, like:
<InputField
ref="userInput"
labelClass = "label"
labelText = "Username"
inputId = "signUp_username"
inputType = "email"
inputPlaceholder = "registered email"
inputClass = "input" />
So you can access it using refs:
this.refs.userInput.getUsernamePassword();
Where getUsernamePassword function would be inside the InputField component, and with the return you can set the state and call your props.handleSignUp

Programmatically open a route with state in react

I have two types of item, one of which can contain data similar to the other.
Currently when form is used to save an item it saves it then uses browserHistory.push to show the next page.
But I wish add a button that will
save the currently item
redirect them to the form to add the other item type,
partially fill out this form with the data from the first item.
Is there a way to do this using react and not using local storage or session variables?
You should take a look to Redux (or other Flux based libraries) to store data between components and routes, avoiding the excessive prop nesting.
browserHistory.push won't work. It only moves you to a certain location but it doesn't update the application state. You need to update application state, which then will reflect into location update, but not in the opposite direction. Keep in mind that, in React, data comes first, and its representation, even though mutable, doesn't change the data back. The same applies to the location.
To make the redirect alone work, I'd recommend wrapping your component into withRouter higher-order component.
import React, { Component } from 'react';
import { withRouter } from 'react-router';
class MyComponent extends Component {
render() {
return (
<div>
<button
onClick={() => this.props.router.push('/new-location')}>
Click me to go to /new-location
</button>
</div>
);
}
}
But if you need to pass data from one component to another, and the two aren't in hierarchy, I'd agree with Alomsimoy and recommend using Redux. But if, for some reason, it's not an option, you can store this data in a component that is parent to both forms:
class FormA extends Component {
render() {
return (
<form onSubmit={() => this.props.onSubmit()}>
<input
type="text"
value={this.props.inputA}
onChange={(event) => this.props.handleChangeA(event)} />
</form>
);
}
}
class FormB extends Component {
render() {
return (
<form onSubmit={() => this.props.onSubmit()}>
<input
type="text"
value={this.props.inputB}
onChange={(event) => this.props.handleChangeB(event)} />
</form>
);
}
}
while their parent would rule the location and state updates:
class Forms extends Component {
constructor() {
super();
this.state = {};
}
handleChange(name, value) {
this.setState({
[name]: value
});
}
renderForm() {
const {
params: {
stepId
}
} = this.props;
if (stepId === 'step-a') { // <- will be returned for location /form/step-a
return (
<FormA
inputA={this.state.inputA}
handleChangeA={(event) => this.handleChange('inputA', event.target.value)}
onSubmit={() => this.props.router.push('/form/step-b')} />
);
} else if (stepId === 'step-b') { // <- will be returned for location /form/step-b
return (
<FormB
inputB={this.state.inputB}
handleChangeB={{(event) => this.handleChange('inputA', event.target.value)} />
);
}
}
render() {
const {
children
} = this.props;
console.log(this.state); // track changes
return (
<div>
{this.renderForm()}
<button
onClick={() => this.props.router.push('/new-location')}>
Click me to go to /new-location
</button>
</div>
);
}
}
export default withRouter(Forms);
so the route for them would look like
<Route path="form/:stepId" component={Forms} />

Calling a ref from another component (?)

I am working with React in a chat application. I need that evertytime you click on the chat box the input where you enter your messagges, should be focused.
The problem I have is that the chat box which is in the main component, is separately from the component where the input to enter the messages is.
Look, main component
class ChatView extends React.Component {
constructor (props) {
super(props);
this._inputFocus = this._inputFocus.bind(this);
}
_inputFocus () {
let input = React.findDOMNode(this.refs.SHOULDFOCUS-HERE);
input.focus();
}
render () {
if (this.props.mode === 'player') {
dealerPlayerMessages = <ul ref="messages">{messages}</ul>;
// CHAT FORM IS THE COMPONENT CONTAINING THE INPUT
chatForm = <ChatForm onAddMessage={this.addMessage} />;
chatBox = <div>{dealerPlayerMessages}{chatForm}</div>
}
return <div className="dealer-player-box" onClick={this._inputFocus} >
{chatBox}
</div>;
}
}
so, as you see, when you click in the div with class name dealer-player-box, the function this._inputFocus is called.
But the input to enter your message text, is in the component ChatForm:
class ChatForm extends React.Component {
constructor (props) {
super(props);
}
render () {
return (<div className="chat-form">
// THIS IS THE INPUT I NEED TO FOCUS ON
<input className="input-form"
placeholder="Your message..."
ref="SHOULDFOCUS-HERE"
autofocus="true" />
</div>);
}
}
so, from the main component, what should I do in order to focus on that input since in another component ?
Add a ref to your ChatForm:
<ChatForm ref="chatForm" onAddMessage={this.addMessage} />;
In your ChatForm change the input ref to something meaningful:
<input className="input-form"
placeholder="Your message..."
ref="chatInput"
autofocus="true" />
In your ChatForm component add a method which focuses:
focusInput() {
this.refs.chatInput.focus();
}
In your ChatView component modify the _inputFocus method:
_inputFocus() {
this.refs.chatForm.focusInput();
}
Note that using refs might be a little different if you are using deprecated versions of react (<0.14).

Categories

Resources