Am new to react and i have a custom input where am handling the value and input handler via a custom hook but would like to get the value and the input handler to the parent component using the custom input but am stuck on how to achieve this.
I have written the following code.
On the custom hook
import {useReducer} from "react";
const INITAL_STATE = {value:'',valid:false,pristine:true, error:''}
const REDUCER_ACTIONS = { input:"INPUT", blur:"BLUR"}
const reducer = (state,action)=>{
if (action.type === REDUCER_ACTIONS.input){
return {...state, value: action.value}
}
if (action.type === REDUCER_ACTIONS.blur){
return {...state, pristine: false}
}
return INITAL_STATE;
}
const useForm = () => {
const [inputState, dispatch] = useReducer(reducer,INITAL_STATE)
const onBlurHandler = (event) => {
dispatch({type:REDUCER_ACTIONS.blur});
}
const onInputHandler = (event) => {
dispatch({type:REDUCER_ACTIONS.input,value:event.target.value})
}
return {
...inputState,
onBlurHandler,
onInputHandler
}
};
export default useForm;
And for my custom input i have
import useForm from "../../hooks/use-form";
const CustomInput = (props) => {
const {value, onInputHandler, onBlurHandler} = useForm(); //uses custom hook
return <>
<label htmlFor={props.id}>{props.label}</label>
<input value={value} onBlur={onBlurHandler} onInput={onInputHandler}
{...props} />
</>
}
export default CustomInput;
The above custom input has the onInput and onBlur pointing to the custom hooks since i want to reuse the functionality on other input types like select and date pickers without having to duplicate them.
On my parent component am simply calling the Custom input like
function App() {
return (
<div className="container">
<CustomInput onInputHandler={} label="First name"/>
</div>
);
}
export default App;
I would like to pass the onInputHandler and value as a props back to the parent component from the custom input but am stuck on how to do this. How do i proceed?
When you say you need to pass value, I guess you wanted to pass the initial value of the input to CustomInput. To achieve that you can pass another prop.
App.js pass initialValue to CustomInput
<CustomInput
initialValue={"abc"}
label="First name"
/>
In CustomInput pass initialValue prop to useForm hook as an argument.
const { value, onInputHandler, onBlurHandler } = useForm(props.initialValue);
Set the initialValue as the value in initial state in useForm.
const useForm = (initialValue) => {
const [inputState, dispatch] = useReducer(reducer, {
...INITAL_STATE,
value: initialValue
});
...
...
}
To pass the onInputHandler as a prop you can check if onInputHandler is available as a prop and call it along with onInputHandler coming from useForm.
In App.js defines another function that accepts event as an argument.
export default function App() {
const onInputHandler = (e) => {
console.log(e);
};
return (
<div className="App">
<CustomInput
...
onInputHandler={onInputHandler}
label="First name"
/>
</div>
);
}
In CustomInput change the onInput handler like below. You can change the logic as per your needs (I called onInputHandler in useForm and prop).
<input
value={value}
onBlur={onBlurHandler}
onInput={(e) => {
props.onInputHandler && props.onInputHandler(e);
onInputHandler(e);
}}
{...props}
/>
My approach to this will be to simply call the onInputHandler() from hooks and onInputHandler() from the props received from Parent and send the e.target.value as a prop to these functions.
const CustomInput = (props) => {
const { value, onInputHandler, onBlurHandler } = useForm(); //uses custom hook
console.log(value);
const handleInputChange = (e: any) => {
onInputHandler(e);
props.onInputHandler(e.target.value);
};
return (
<>
<label htmlFor={props.id}>{props.label}</label>
<input
value={value}
onBlur={onBlurHandler}
onInput={(e) => {
handleInputChange(e);
}}
{...props}
/>
</>
);
};
export default CustomInput;
And in the parent component we can receive them as props returned from that function and use it according to our requirement.
function App() {
return (
<div className="container">
<CustomInput
label="name"
onInputHandler={(value: string) => console.log("App",value)}
/>
</div>
);
}
export default App;
sandbox link : https://codesandbox.io/s/nifty-lamport-2czb8?file=/src/App.tsx:228-339
import React from "react";
export default function Form ({handleSubmit, handleChange, value}) {
return (
<form onSubmit = {handleSubmit}>
<label>
<input type="text" name="name" onChange = {handleChange}/>
</label>
<input type="submit" value="Submit" />
</form>
);
}
I'm trying to create a basic ToDoList App. I'm getting stuck on the part in which you render the users input into my ToDoList Component. When I alert the value of e.target.value I get undefined. What is the problem? EDIT: Added my form component.
import "./styles.css";
import Header from "./Header.js";
import Form from "./Form.js";
import {useState} from "react";
import ToDoList from "./ToDoList.js"
export default function App() {
const[items, setItems] = useState([]);
let value = "";
function handleChange(e) {
value = e.target.value;
}
function handleSubmit(e) {
setItems([...items,
e.target.value
]);
alert('A name was submitted: ' + e.target.value);
e.preventDefault();
}
return (
<div className="App">
<div>
<Header/>
</div>
<div>
<Form handleChange = {handleChange} handleSubmit = {handleSubmit} value = {value}/>
</div>
<div>
<ToDoList items = {items}/>
</div>
</div>
);
}
Because you pass whole form as event to handleSubmit and items. If you want to keep this style of code, you must use input value changes. Also you must define e.preventDefault() before doing everything. So:
import "./styles.css";
import Header from "./Header.js";
import Form from "./Form.js";
import {useState} from "react";
import ToDoList from "./ToDoList.js"
export default function App() {
const[inputValue, setInputValue] = useState();
const[items, setItems] = useState([]);
let value = "";
function handleChange(e) {
setInputValue(e.target.value);
}
function handleSubmit(e) {
e.preventDefault();
setItems([...items,
inputValue
]);
setInputValue("");
alert('A name was submitted: ' + e.target.value);
}
return (
<div className="App">
<div>
<Header/>
</div>
<div>
<Form handleChange={handleChange} handleSubmit={handleSubmit} value = {value}/>
</div>
<div>
<ToDoList items = {items}/>
</div>
</div>
);
}
I need to update the value of Form.Item manually. I have a custom component, which returns selected value, and want to pass this value to the Form for validation and so I can send it.
Is this possible with antd?
Here's the simplified code:
import { Form } from "antd";
import { FC, ReactElement, useEffect, useState } from "react";
const Child: FC<{
returnValue: (value: any) => void;
}> = ({ returnValue }): ReactElement => {
return <input onChange={(e) => returnValue(e.currentTarget.value)} />;
};
export default function App() {
const { useForm } = Form;
const [form] = useForm();
const [value, setValue] = useState<string>("");
const setNewValue = (newVal: string) => setValue(newVal);
useEffect(() => {
console.log(value);
}, [value]);
return (
<div className="App">
test
<Form form={form}>
<Form.Item
//value={value} I'm looking form smth like this
>
<Child returnValue={setNewValue} />
</Form.Item>
</Form>
</div>
);
}
And here is the code sandbox.
Using <Input> from antd will not work in my case. This is a simplified problem. I have a way more complex component, which behaves in a similar way. The returnValue is how I managed to pull the value out of the component.
For a class based component, this is how you would define a form
class CustomForm extends React.Component {
formRef = React.createRef();
constructor()
render(){
return(
<Form
ref={this.formRef}
name="customForm"
>
<Form.Item label="Email" name="email">
<Input />
</Form.Item>
</Form>
)}
}
and this is how you set form.items value
componentDidUpdate(){
this.formRef.current.setFieldsValue({
email: this.props.customerData.map((d) => d.email),
});
}
you can convert the logic for the functional component.
How to render the component lazily, on the select of a value change.
Here is my below snippet, i am not able to see the title, nothing is rendering
App.js
import React, { useState } from "react";
import "./styles.css";
import { SectionOne, SectionTwo } from "./ChildComponents";
export default function App() {
const [state, setState] = useState(null);
const sectionToBeDisplayed = {
section_one: (title = "section one") => <SectionOne title={title} />,
section_two: (title = "section two") => <SectionTwo title={title} />
};
const handleSelectChange = (e) => {
setState(e.target.value);
};
return (
<div className="App">
<select onChange={handleSelectChange}>
<option disabled selected value>
{" "}
-- select an option --{" "}
</option>
<option value="section_one">SectionOne</option>
<option value="section_two">SectionTwo</option>
</select>
{sectionToBeDisplayed[state]}
</div>
);
}
ChildComponents.js
import React from "react";
export const SectionOne = ({ title }) => {
return <h1>{title}</h1>;
};
export const SectionTwo = ({ title }) => {
return <h2>{title}</h2>;
};
So based on the selection only i need to load this component, I am newbie to react, read that we can use React.lazy but i don't see any use case like this. Also whenever i change it should not build the dom again. Should we use useMemo, I am not clear to use React.memo or useMemo, Which one is better.
You need to invoke the function component:
// Component is a function component
const Component = (title = "section one") => <SectionOne title={title} />;
// Invoke it
<Component/>
const sectionToBeDisplayed = {
section_one: ...
};
export default function App() {
...
const SectionComponent = sectionToBeDisplayed[state];
return (
<div className="App">
...
<SectionComponent />
</div>
);
}
Could you please tell me how to get input field value on button click in react , I am using react hooks .I want to get first name and lastname value on button click. I already pass name attribute in my function component.
Here is my code
import React, { Component, useState } from 'react';
import { render } from 'react-dom';
export default function InputField({name,label}) {
const [state, setState] = useState('')
return (
<div>
<label>{label}</label>
<input type="text"
value={state}
name={name}
onChange={(e) => setState(e.target.value)} />
{state}
</div>
);
}
Use <form> tag with useRef hook
Wrap your <InputField> tags with an html <form> tag and put a react ref on the later. Like this:
import React, { Component, useRef } from 'react'
import { render } from 'react-dom'
import InputField from './inputfield'
import './style.css'
function App () {
const nameForm = useRef(null)
const handleClickEvent = () => {
const form = nameForm.current
alert(`${form['firstname'].value} ${form['lastname'].value}`)
}
return (
<div>
<form ref={nameForm}>
<InputField label={'first name'} name={'firstname'}/>
<InputField label={'last name'} name={'lastname'}/>
</form>
<button onClick={handleClickEvent}>gett value</button>
</div>
)
}
render(<App />, document.getElementById('root'))
Working example: https://stackblitz.com/edit/react-shtnxj
The Easiest Way For Me is useRef
With useRef it's pretty simple. Just add ref name and then submit.
const email = useRef(null);
function submitForm(e){
e.preventDefault();
console.log(email.current.value);
}
return (
<div>
<form onSubmit={submitForm}>
<input type="text" ref={email} />
<button>Submit</button>
</form>
</div>
)
You could always lift up the state in parent component.
codeSandbox link
Parent Component
import React from "react";
import ReactDOM from "react-dom";
import ChildComponent from "./Child";
const { useState } = React;
function App() {
const [first_name, setFirstName] = useState("");
const [last_name, setLastName] = useState("");
const handleFirstNameChange = ({ target }) => {
setFirstName(target.value);
};
const handleLastNameChange = ({ target }) => {
setLastName(target.value);
};
const handleClick = () => {
console.log(first_name);
console.log(last_name);
};
return (
<div className="App">
<ChildComponent
label="first name"
onChange={handleFirstNameChange}
value={first_name}
/>
<ChildComponent
label="last name"
onChange={handleLastNameChange}
value={last_name}
/>
<button onClick={handleClick}>Click me</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Child Component
import React from "react";
const ChildComponent = ({ label, onChange, value, name }) => {
return (
<div>
<label>{label}</label>
<input type="text" value={value} name={name} onChange={onChange} />
</div>
);
};
export default ChildComponent;
You could always combine onChange handler for first name and last name.
Hope that helps!!!
A good solution is to move the state from InputField component into index:
const [F_name, setF_name] = useState('')
const [L_name, setL_name] = useState('')
now you should pass state value and event handler to InputField to change the state when input is changed:
<InputField label={'first name'} name={'firstname'} value={F_name} changed={(name) => setF_name(name)}/>
In Your InputField field: edit it to be like:
<input type="text"
value={value}
name={name}
onChange={(e) => changed(e.target.value)} />
See Working Demo Here
import React, { useRef } from 'react'
const ViewDetail = () => {
const textFirstName = useRef(null)
const onChange = e => {
console.log(textFirstName.current.state.value)
}
return <Input maxLength={30} ref={textFirstName} placeholder="Nombre" onChange=onChange} />
}
I can think of these approaches -
You can pull the state up to the parent component.
App.js
const [user, setUser] = useState('');
return (
<Inputfield setValue={setUser} value={user} />
);
InputField.js
<input value={props.value} onChange={(e) => setValue(e.target.value)} />
You can use ref to access indiviual element value.
If you have data distributed across multiple components you can also make use of Context API
Hope this helps!
Do let me know if you need more info on any of the option. Thanks!
You should do the react hooks work on your index and pass the value and the onChange function to your InputField component.
//index page
import React, { Component, useState } from 'react';
import { render } from 'react-dom';
import InputField from './inputfield';
import './style.css';
function App() {
const [firstname, setFirstName] = useState('');
const [lastname, setLastName] = useState('');
const handleClickEvent = ()=>{
setFirstName('Will');
setLastName('smith');
}
return (
<div>
<InputField
label={'first name'}
name={'firstname'}
value={firstname}
onChange={setFirstName}
/>
<InputField
label={'last name'}
name={'lastname'}
value={lastname}
onChange={setLastName}
/>
<button
onClick={handleClickEvent}
>Get value</button>
</div>
);
}
render(<App />, document.getElementById('root'));
// input field
import React, { Component, useState } from 'react';
import { render } from 'react-dom';
export default function InputField({name,label, value, onChange}) {
return (
<div>
<label>{label}</label>
<input type="text"
value={value}
name={name}
onChange={(e) => onChange(e.target.value)} />
{value}
</div>
);
}
While keeping the majority of your structure the same, I think the simplest and most React solution is to use forwardRef() which in a nut-shell let's us communicate between then parent-component and child-components.
See working sandbox.
App.js
import React, { useRef } from "react";
import InputField from "./InputField";
import ReactDOM from "react-dom";
function App() {
const handleClickEvent = () => {
if (firstName.current && lastName.current) {
console.log(`firstName: ${firstName.current.value}`);
console.log(`lastName: ${lastName.current.value}`);
}
};
const firstName = useRef(null);
const lastName = useRef(null);
return (
<div>
<InputField ref={firstName} label={"first name"} name={"firstname"} />
<InputField ref={lastName} label={"last name"} name={"lastname"} />
<button onClick={handleClickEvent}>Get value</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
InputField.js
import React, { useState } from "react";
const InputField = React.forwardRef((props, ref) => {
const [state, setState] = useState("");
return (
<div>
<label>{props.label}</label>
<input
ref={ref}
type="text"
value={state}
name={props.name}
onChange={e => setState(e.target.value)}
/>
{state}
</div>
);
});
export default InputField;
Notice that with this structure, you are not required to pass in any state updating function as props to the InputField component. The value that you enter into each input will be strictly maintained by the individual component. It is independent from the Parent, and therefore makes it much more reusable.
The refs we created allow us to tap into specific elements of the InputField so we extract the desired values. In this case, we can get first-name and last-name through the handleClickEvent function.
you can achieve this doing the following:
import React, { Component, useState } from 'react';
import { render } from 'react-dom';
export default function InputField({name,label}) {
const [state, setState] = useState('');
const handleChange = e => {
setState(e.target.value);
};
return (
<div>
<label>{label}</label>
<input
type="text"
value={state}
name={name}
onChange={handleChange}
/>
{state}
</div>
);
}
Hopes this helps.
well one simple(but not necessarily recommended) way is to provide an id or a ref like this in index.js
<InputField label={'first name'} name={'firstname'} id={"ip1"}/>
<InputField label={'last name'} name={'lastname'} id={"ip2"}/>
and in your inputfield.js pass the id props to the input fields like this
<input type="text"
value={state}
name={name}
onChange={(e) => setState(e.target.value)}
id= {id}/>
Now you can call them in the onClick of the button like this in index.js
const handleClickEvent = ()=>{
alert(document.getElementById("ip1").value);
}
The second, more preferable way is to set the state variable in index.js
function App() {
const [stateIp1, setStateIp1] = useState('');
const [stateIp2, setStateIp2] = useState('');
const handleClickEvent = ()=>{
alert(stateIp1);
}
return (
<div>
<InputField label={'first name'} state={stateIp1} setState={setStateIp1} name={'firstname'} id={"ip1"}/>
<InputField label={'last name'}state={stateIp2} setState={setStateIp2} name={'lastname'} id={"ip2"}/>
<button
onClick={handleClickEvent}
>Get value</button>
</div>
);
}
Now your inputfield.js becomes
export default function InputField({name,label,id,setState,state}) {
return (
<div>
<label>{label}</label>
<input type="text"
value={state}
name={name}
onChange={(e) => setState(e.target.value)} id= {id}/>
</div>
);