Component doesn't trigger functions - reactjs

render() {
const { newRepo, loading, repositories } = this.state;
return (
<Container>
<RepoForm onSubmit={this.handleSubmit}>
<FormInput onChange={this.handleInputChange} value={newRepo} />
<SubmitButton loading={loading} />
</RepoForm>
<RepoList repositories={repositories} />
</Container>
);
export default function FormInput() {
return <Container type="text" placeholder="Adicionar repositório" />;
}
My code doesn't activate the function onChange when I change the input. The two codes are in separate files. Why is this happening?

We don't have the detailed implementation of the Container component. Usually, you will need to pass down the even handler from parent to child as a prop.
// get the reference of the onChange prop
export default function FormInput({ onChange }) {
// connect onChange function with the actual component that handles input
return <Container type="text" onChange={onChange} placeholder="Adicionar repositório" />;
}
I hope it helps.

Related

How to pass onChange and props in a function

I am trying to change the id of my input using props but I am also using an onChange event.
import React from "react";
function Toggle({ onChange }) {
return (
<>
<input type="checkbox" id="switch" onChange={onChange} />
<label htmlFor="switch"></label>
</>
);
}
export default Toggle;
I have tried like this:
import React from "react";
function Toggle({ onChange }, props) {
return (
<>
<input type="checkbox" id={props.id} onChange={onChange} />
<label htmlFor={props.id}>
</label>
</>
);
}
export default Toggle;
How do I do this?
You are destructuring props, you can obtain the other values like this:
{ onChange, ...props }
Your attempt
({ onChange }, props)
Is destructuring the first argument, and assuming that there is a second one called props
The ...props pattern from above is called a "rest" pattern, it also works with arrays
You can destructure id also.
function Toggle({ onChange, id }) {
return (
<>
<input type="checkbox" id={id} onChange={onChange} />
<label htmlFor={id}>
</label>
</>
);
}

react-hook -form with SliderComponent input

Im new to react and I am trying to use a SliderComponent in a react Hook form but I cant seem able to fully understand how Controller works.
Here is my SliderComponent using react-input-slider:
export default function SliderComponent(props) {
const { axis, xmax, xmin, xstep, onChange } = props;;
return (
<div>
<Slider
axis={axis}
x={value.x}
xmax={xmax}
xmin={xmin}
xstep={xstep}
onChange={onChange}
/>
</div>
);
}
And here is my form:
export default function BiometricForm() {
const { handleSubmit, control } = useForm();
return (
<div className="registerForm_Container">
<form onSubmit={handleSubmit(onsubmit)}>
<Controller
control={control}
name="test"
render={({ props: { x, axis, xmax, xmin, xstep } }) => (
<SliderComponent
axis={"x"}
xmax={100}
xmin={1}
xstep={1}
value={x}
onChange={(e) => x.onChange(parseInt(e.target.value))}
/>
)}
/>
<button className="registerForm_Container_button" type="submit">
Register
</button>
</form>
</div>
);
}
I think it might be something to do with useState and that I am not able to reach useState of component. I have read that maybe its not necessary , any help? Thank you!
You are absolutely right, if you use RHF you don't need useState because RHF handles the form state for you. The important thing here is to pass an onChange handler to your <SliderComponent />, so that RHF can watch for value changes of your <SliderComponent /> and update the form state. You should also provide a defaultValue for the field, if you don't want it to be undefined if the user doesn't change the slider before submitting.
Also without the useState inside <SliderComponent />, you can also omit the <SliderComponent /> and just use <Slider />.
function BiometricForm() {
const { handleSubmit, control } = useForm();
const onSubmit = (data) => console.log(data);
return (
<div className="registerForm_Container">
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
control={control}
name="test"
defaultValue={50}
render={({ field: { value, onChange } }) => (
<Slider
axis={"x"}
xmax={100}
xmin={1}
xstep={1}
onChange={({ x }) => onChange(x)}
x={value}
/>
)}
/>
<input type="submit" />
</form>
</div>
);
}

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.

Unable to retrieve the input field of material UI using refs in react js

I am developing a Web application using React JS + Material UI core. Now, I am building a form with the material ui control. Now, I am trying to retrieve the input field value (TextField) using refs of React. It is always saying undefined.
This is my component
class CreateEventComponent extends React.Component{
constructor(props)
{
super(props)
}
submitCreateEventForm(e)
{
e.preventDefault();
alert(this.refs.name.input.value)
}
render()
{
return (
<MuiThemeProvider>
<div className={scss['page-container']}>
<Grid
spacing={16}
container>
<Grid item md={12}>
<Card>
<CardContent>
<form onSubmit={this.submitCreateEventForm.bind(this)}>
<div>
<TextField
ref="name"
className={scss['form-control']}
name="name"
label="Name" />
</div>
<div>
<Grid>
<Button type="submit" color="primary" variant="raised">Save</Button>
</Grid>
</div>
</form>
</CardContent>
</Card>
</Grid>
</Grid>
</div>
</MuiThemeProvider>
)
}
}
function mapStateToProps(state)
{
return {
};
}
function matchDispatchToProps(dispatch)
{
return bindActionCreators({
}, dispatch);
}
const enhance = compose(withWidth(), withStyles(themeStyles, { withTheme: true }), connect(mapStateToProps, matchDispatchToProps))
export default enhance(CreateEventComponent);
As you can see, when form submits, I am trying to alert the name input field using refs. But it is always showing "undefined". I tried using this to fetch the value of TextField.
alert(this.refs.name.value)
It throws error saying name is undefined. So, how can I fetch the value of TextField using Ref?
I used this way as well.
I create ref in the constructor
constructor(props)
{
super(props)
this.nameRef = React.createRef();
}
Then set the ref for the TextField
<TextField
ref={this.nameRef}
className={scss['form-control']}
name="name"
label="Name" />
Then retrieve the values in this ways.
this.nameRef.value
this.nameRef.input.value
It is giving me the same error as well.
Original Answer
You need to create a ref in your constructor.
From the docs:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef(); // create a ref
}
render() {
return <div ref={this.myRef} />;
}
}
Updated Answer
According to Material UI's documentation, you need to pass in a callback to the inputRef prop on your <TextField />.
So, in addition to the original answer, try this as well:
<TextField
inputRef={e => this.nameRef = e}
className={scss['form-control']}
name="name"
label="Name" />
if you are using a stateless functional component with material ui then you can use react hooks.
import React, { useState, useRef } from "react";
let MyComponent = (props) => {
let textInput = useRef(null);
return (
<div>
<Button
onClick={() => {
setTimeout(() => {
textInput.current.focus();
textInput.current.click();
textInput.current.value="myname";
console.log(textInput.current.value);
}, 100);
}}
>
Focus TextField
</Button>
<TextField
fullWidth
required
inputRef={textInput}
name="firstName"
type="text"
placeholder="Enter Your First Name"
label="First Name"
/>
</div>
);
};
For me, this solves the problem:
<TextField
ref={this.nameRef}
onChange={e => {
this.nameRef.current.value = e.target.value;
}}
className={scss['form-control']}
name="name"
label="Name" />

Resources