My onChange function is not being passed down to child - reactjs

I'm building a higher component where field input is grouped together with some other components. I'm trying to pass down my onChange function to the child component so it can change the state with the new input. all the other props are passed fine and I can see the using chrome dev tools, but even though I can see the onChange function if I console.log the props on the child, it is not present on the form control component which ultimately creates the input text field.
Here is the code, you can notice in the form render the FormGroup which is not using the higher InputFieldGroup component:
higher_component.js
import React from 'react'
import * as FormGroup from 'react-bootstrap/lib/FormGroup'
import * as ControlLabel from 'react-bootstrap/lib/ControlLabel'
import * as FormControl from 'react-bootstrap/lib/FormControl'
export const InputFieldGroup = (props) =>
<FormGroup
controlId={props.controlId}
validationState={props.validationState()}>
<ControlLabel>{props.controlLabel}</ControlLabel>
{console.log(props)} {/* I CAN SEE THE ONCHANGE FUNCTION HERE */}
<FormControl
type={props.type}
name={props.name}
value={props.value}
placeholder={props.placeholder}
onChange={props.handleChange}
/>
<FormControl.Feedback />
</FormGroup>
form.js
export default class RequestQuote extends Component {
...Some other class methods and stuff
handleTextChange = (e) => {
this.setState({ [e.target.name]: e.target.value })
}
render() {
return(
<form>
<InputFieldGroup
controlId='email'
validationState={this.emailValidationState}
controlLabel='email'
type='text'
name='email'
value={this.state.email}
placeholder='Enter Business Email'
onChange={this.handleTextChange}
/>
{/* When I don't use the higher component the onchange works fine */}
<FormGroup>
<ControlLabel>Name</ControlLabel>
<FormControl
type='text'
name='name'
value={this.state.name}
placeholder='Enter Name'
onChange={this.handleTextChange}
/>
<FormControl.Feedback />
</FormGroup>
</form>
)
}
}
Why isn't the onChange function being passed to the child input?

Because it is undefined! In fact you give
onChange={this.handleTextChange}
to the components as a props, if you want to call it do
props.onChange
and not
props.handleChange
otherwise, change the variable onChange={js} to handleChange={js}

Related

react-hook-form access to validation rules in nested component

I'd like to access to the validation rules defined in react-hook-form's Resister in the nested component to dynamically display required indicator(*).
Is there a way to access from Nested component?
I don't want to repeat by passing as a prop.
<TextBox ref={register({ required: true })} label="Serial No" name="serialNo" />
const TextBox = React.forwardRef(({ label, name }, ref) => (
<>
<label htmlFor={name}>
{label} {***required*** && <span>*</span>}
</label>
<input type="text" name={name} id={name} ref={ref} />
</>
))
have a look at https://react-hook-form.com/api#useFormContext
import React from "react";
import { useForm, FormProvider, useFormContext } from "react-hook-form";
export default function App() {
const methods = useForm();
const onSubmit = data => console.log(data);
return (
<FormProvider {...methods} > // pass all methods into the context
<form onSubmit={methods.handleSubmit(onSubmit)}>
<NestedInput />
<input type="submit" />
</form>
</FormProvider>
);
}
function NestedInput() {
const { register } = useFormContext(); // retrieve all hook methods
return <input name="test" ref={register} />;
}
which allow you to access all hook form methods in the nested component level.

Automatically Passing Props on Component

I have created an Input component that I have styled with Styled Components. I am now using that Input component with Formik. I would like to be able to automatically set the onChange, onBlur and value props rather than have to set them each and every time (like what happens if I would use the Formik Field component).
That is, right now this is how my component looks when used:
<Input
name="firstName"
onBlur={handleBlur}
onChange={handleChange}
value={values.firstName}
/>
What I would like the component to look like when being used is this:
<Input name="firstName" />
Then, behind the scenes, onBlur would be set to handleBlur, onChange would be set to handleChange and value would be set to values.[name]. That is, to the value of the name prop. So, in this example, it would be set to values.firstName. If the name prop was set to lastName, then the value prop would automatically be set to values.lastName.
Any idea how I can do this?
NOTE: I know that the Field prop from Formik does this, but I want to use my custom Input component instead.
UPDATE
Here is some other code that may be relevant to answering this question.
Input Component
export const Input = props => {
const {
forId,
name,
placeholder,
} = props
const titleCase = startCase(name)
return (
<InputBase {...props}>
<InputSection>
<InputContent
id={forId ? forId : name}
placeholder={placeholder ? placeholder : titleCase}
type="text"
{...props}
/>
</InputSection>
</InputBase>
)
}
InputContent Component
export const InputContent = styled.input`
// STYLES
`
Formik with Form
<Formik
render={props => {
const {
handleBlur,
handleChange,
values,
} = props
return (
<Form>
<Input
name="firstName"
onBlur={handleBlur}
onChange={handleChange}
value={values.firstName}
/>
<Button type="submit">Submit</Button>
</Form>
)
}}
initialValues={{firstName: ''}
validationSchema={validationSchema}
/>
I don't think this is good idea. But what you can do is create HOC and wrap it
// lasyWrapper.js
export function lazyWrapper(Input) {
return LazyWrapper extends React.Component {
render() {
return (
<Input
{...this.props}
name={this.props.name}
onBlur={this.props.handleBlur}
onChange={this.props.handleChange}
value={this.props.values[this.props.name]}
/>
)
}
}
}
// Input.js
export default lazyWrapper(Input)
// use somewhere
<Input
name="firstName"
{...this}
/>
But this is really BAD idea.

Problem connect Redux with parent props from React

I have been trying for several days to solve a problem between connect of redux and a props of a parent component. I use redux in my child component and at the same time use props to call a handlechange function and a state of the parent component.
When in the child component use connect does not update this.props.args properly, however in the parent there is no problem. Then I have a validation so that when all the fields are complete, my submit button will be activated.
Additionally implement a state and a function in the parent component to detect the movement of the mouse and pass it to the child component, to clear some doubts, the amazing thing is that in this case if it detects changes in this.props.mouse and much more rare is that when I fill a field and then move the mouse, my this.props.args in the child component if it is updated. I'm a little disconcerted and I do not know what is due.
Parent Component:
import React, { Component } from "react";
import SignUp from './login/SignUp';
class Login extends Component {
state = {
showRegister: true,
argsSignup: {},
move: {},
}
handleChange = (ev, input) => {
let argsSignup = this.state.argsSignup;
argsSignup[input.name] = input.value;
this.setState({argsSignup});
//console.log(this.state.argsSignup);
}
handleMouseMove = (event) => {
this.setState({
move: {
x: event.clientX,
y: event.clientY
}
});
}
render() {
const {showRegister, argsSignup, move} = this.state;
return(
<div onMouseMove={this.handleMouseMove}>
{showRegister && <SignUp args={argsSignup} handleChange={this.handleChange} mouse={move} />}
</div>
);
}
}
export default (Login);
Child Component (with connect):
import React, { Component } from "react";
import { connect } from "react-redux";
import { Form, Button } from 'semantic-ui-react';
import { signInFacebook } from "../../redux/createActions";
class SignUp extends Component {
render() {
const {args, handleChange, signInFacebook} = this.props;
return(
<React.Fragment >
<Form >
<Form.Field>
<Form.Input name='email' onChange={handleChange} placeholder='numero de movil o correo electronico' />
</Form.Field>
<Form.Field>
<Form.Input name='name' onChange={handleChange} placeholder='Nombre completo'/>
</Form.Field>
<Form.Field>
<Form.Input name='username' onChange={handleChange} placeholder='Nombre de usuario' />
</Form.Field>
<Form.Field>
<Form.Input name='password' onChange={handleChange} type="password" placeholder='contraseña' />
</Form.Field>
<Button
type='submit'
disabled={!args.email || !args.username || !args.name || !args.password }
primary
fluid>
Regístrate
</Button>
</Form>
<button onClick={signInFacebook}>Inicia sesion con Facebook</button>
</React.Fragment>
)
}
}
export default connect(null, {signInFacebook})(SignUp);
On the other hand, if I delete the connect from my child component, everything works great and if it detects the changes of this.props.args
Child Component (without connect):
import React, { Component } from "react";
import { connect } from "react-redux";
import { Form, Button } from 'semantic-ui-react';
import { signInFacebook } from "../../redux/createActions";
class SignUp extends Component {
render() {
const {args, handleChange, signInFacebook} = this.props;
return(
<React.Fragment >
<Form >
<Form.Field>
<Form.Input name='email' onChange={handleChange} placeholder='numero de movil o correo electronico' />
</Form.Field>
<Form.Field>
<Form.Input name='name' onChange={handleChange} placeholder='Nombre completo'/>
</Form.Field>
<Form.Field>
<Form.Input name='username' onChange={handleChange} placeholder='Nombre de usuario' />
</Form.Field>
<Form.Field>
<Form.Input name='password' onChange={handleChange} type="password" placeholder='contraseña' />
</Form.Field>
<Button
type='submit'
disabled={!args.email || !args.username || !args.name || !args.password }
primary
fluid>
Regístrate
</Button>
</Form>
<button onClick={signInFacebook}>Inicia sesion con Facebook</button>
</React.Fragment>
)
}
}
export default (SignUp);
I appreciate your help from now on. Thank you!!
When you connect your component, you are establishing a connection with Redux and the props of that component will be fed by the entire application state.
export default connect(mapStateToProps, {signInFacebook})(SignUp);
Your first parameter for connect function holds the mapStateToProps function which essentially maps the application state to props of the SignUp component. Keeping that null means that you're expecting a null state object from Redux and hence you are unable to get the desired result in this.props.args. Removing connect statement makes the props which are passed down to the child component accessible through this.props. That's the crux of Redux.

Submitting redux form values

I am new to react and redux technology. now started building an application that contains several redux forms. We want to submit simple form with values.
For ex: login form
Username : text input field
Password: text input field
Submit button
After entering values in fields and click on submit button i want to get the username and password field values in object or json data .. so that I can store it to my server with POST method.
Right now we are using handleSubmit(), but data is not coming as object
1 - The best practice to deal with input values are making them controlled. Which means :
Instead of
<input type='password' />
You do :
<input
type='password'
value={password}
onChange={ event => myInputHandler( event.target.value ) }
/>
The value might come from your state, redux state or as a props etc.
Your handler function differs according to where you store it.
I will give you an example with react state :
<input
type='password'
value={this.state.password}
onChange={ event => this.setState({ password : event.target.value }) }
/>
So whenever someone types, your onChange handler will be called, so that your react state will update with the input ( event.target.value ).
2 - If you need these values when a user submits, then you need to wrap these input fields within a form element and attach a onSubmit handler.
onSubmitHandler( event ){
event.preventDefault()
let password = this.state.password
// use password or other input fields, send to server etc.
}
<form onSubmit={ event => this.onSubmitHandler(event) }>
<input
type='password'
value={this.state.password}
onChange={ event => this.setState({ password : event.target.value }) }
/>
</form>
Hope you get what you need.
If you are using redux to store state then use redux-from then use redux from
import React from 'react'
import {Field, reduxForm} from 'redux-form'
const SimpleForm = props => {
const {handleSubmit, submitting} = props return (
<form onSubmit={handleSubmit(e=>console.log('your form detail here', e))}>
<div>
<label>First Name</label>
<div>
<Field name="firstName" component="input" type="text" placeholder="First Name" />
</div>
</div>
<div>
<label>Last Name</label>
<div>
<Field name="lastName" component="input" type="text" placeholder="Last Name" />
</div>
</div>
<div>
<button type="submit" disabled={pristine || submitting}>Submit</button>
</div>
</form>
) }
export default reduxForm({ form: 'simple'})(SimpleForm)
Go here for more detail
https://redux-form.com
I put the name of the input as the key that I want to use.
Then each time the input changes I destructure the event passed to the onChange function, and I use the name,value to update the state.
On form submit make sure to use preventDefault(); in order to avoid the page refreshing.
import React, { Component } from 'react'
class FormExample extends Component {
constructor(props){
super(props)
this.state = {
formData: {}
}
}
handleInputChange = ({ target: { name,value } }) => {
this.setState({
formData: {
...this.state.formData,
[name]: value
}
})
}
handleFormSubmit = e => {
e.preventDefault()
// This is your object
console.log(this.state.formData)
}
render() {
return(
<div>
<Form
handleSubmit={this.handleFormSubmit}
handleChange={this.handleInputChange}
/>
</div>
)
}
}
const Form = ({ handleSubmit, handleChange }) => (
<form onSubmit={handleSubmit}>
<input onChange={handleChange} name="username" type="text" placeholder="Username" />
<input onChange={handleChange} name="password" type="password" placeholder="Password" />
<button>Submit</button>
</form>
)
export default FormExample

How to get an input value using "refs" in react-bootstrap form?

I'm trying to create a form that appears in modal. So when user input a value, that value is stored in local storage. Here's a picture that help's you to understand what I mean:
Here is the code of the form:
function FieldGroup({id, label, help, ...props}) {
return (
<ReactBootstrap.FormGroup controlId={id}>
<ReactBootstrap.ControlLabel>{label}</ReactBootstrap.ControlLabel>
<ReactBootstrap.FormControl {...props} />
{help && <ReactBootstrap.HelpBlock>{help}</ReactBootstrap.HelpBlock>}
</ReactBootstrap.FormGroup>
);
}
const formInstance = (
<form>
<FieldGroup
id="formControlsText"
type="text"
label="Text"
placeholder="Recipe Name"
inputRef={ref => { this.input = ref; }}
/>
<ReactBootstrap.FormGroup controlId="formControlsTextarea">
<ReactBootstrap.ControlLabel>Ingredients</ReactBootstrap.ControlLabel>
<ReactBootstrap.FormControl componentClass="textarea" placeholder="Enter Ingredients" />
</ReactBootstrap.FormGroup>
</form>
);
As I've read in bootstrap React tutorial, I should add
<FormControl inputRef={ref => { this.input = ref; }} /> to the FormControl props. But after adding it I get an error when modal form is invoked:
`
I just made this issue. My code:
<FormControl
componentClass="input"
placeholder="Enter recipe title"
inputRef={(ref) => {this.input = ref}}
defaultValue={title}/>
</FormGroup>
And then you can get the value from <FormControl> in some handler like this:
console.log(this.input.value);
Details can be found in my repo: https://github.com/kerf007/recipebook
I have same problem with you, and this is my solution
const FieldGroup = ({id, label, help, inputRef, ...props}) =>
<FormGroup controlId={id}>
<ControlLabel>{label}</ControlLabel>
<FormControl {...props} inputRef={inputRef}/>
{help && <HelpBlock>{help}</HelpBlock>}
</FormGroup>
and my form
<form>
<FieldGroup
id="bookName"
type="text"
label="Name"
placeholder="Enter name..."
inputRef = {(input) => this.inputName = input }
/>
<FieldGroup
id="bookAuthor"
label="Author"
type="text"
placeholder="author 's name..."
inputRef={(input) => this.inputAuthor = input}
/>
</form>
then you can get book 's name and author 's name value by:
this.inputName.value and this.inputAuthor.value
This issue (or more like a change in the way it works) is related to React-Bootstrap. The way you're doing it won't work anymore.
The <FormControl> component directly renders the or other specified component. If you need to access the value of an uncontrolled <FormControl>, attach a ref to it as you would with an uncontrolled input, then call ReactDOM.findDOMNode(ref) to get the DOM node. You can then interact with that node as you would with any other uncontrolled input.
Here's an example:
var React = require('react');
var ReactDOM = require('react-dom');
var FormControl = require('react-bootstrap').FormControl;
React.createClass({
render: function() {
return (<FormControl ref="formControl" />);
},
getFormControlNode: function() {
// Get the underlying <input> DOM element
return ReactDOM.findDOMNode(this.refs.formControl);
}
});
As soon as you get the DOM element, you will be able to retrieve the value: this.getFormControlNode().value or do anything else that you want.
PS: Here's a related github issue on this topic.
This worked for me, using https://reactjs.org/docs/refs-and-the-dom.html
constructor(props) {
super(props);
this.email = React.createRef();
}
submit() {
var email = this.email.current.value;
console.log(email);
}
render() {
return (
<Form>
<Form.Control type="email" placeholder="Your email" ref={this.email} />
<Button variant="primary" onClick={()=>this.submit()}>Send</Button>
</Form>
);
}
I think what it suggests to use is the ref callback attribute, so just change inputRef to ref.
FYI: https://facebook.github.io/react/docs/refs-and-the-dom.html
Hello this solution worked for me!
<Form
noValidate
validated={validated}
onSubmit={(e) => this.handleSubmit(e)}
style={{ width: '100%' }}
>
<Form.Group controlId="formBasicEmail">
<Form.Label>Email address</Form.Label>
<Form.Control type="email" placeholder="Enter email" inputRef={(ref) => { this.email = ref }} required />
<Form.Text className="text-muted"> Well never share your email with anyone else.
</Form.Text>
</Form.Group>
</Form>
handleSubmit(event) {
console.log(event.target.elements.formBasicPassword.value)
}
I think I found how to get the ref from React-Bootstrap <Form/>.
import React, {createRef} from 'react'
interface definedProps {}
interface definedState {
myRef: Object //I didn't found the more accurate type
}
export default class SomeClass extends React.Component<definedProps,definedState> {
state = {
myRef: React.createRef<Form<any>>() //That's it!
}
const handleClose = () => {
console.log('this.state.myRef: ', this.state.myRef); //Here we can take data from Form
debugger; //todo: remove
}
render() {
return (
<div>
<Form ref={this.state.myRef}> { /*Here we're connecting the form's ref to State*/}
<Form.Group controlId='formName'>
<Form.Control
type='text'
placeholder='Enter Name'
/>
</Form.Group>
...
<Button
variant='primary'
onClick={handleClose}
>
Save Changes
</Button>
</Form>
</div>
)
}
}

Resources