How to set state for text box in functional component - reactjs

I am working on React JS. I have one text-box component and I want to show some default value in it. After that, the user should be allowed to change the value. Now I am unable to change the value. The text box is behaving like read-only. Below is my code
const EditStyleFormComponent = ({
submitting,
invalid,
}) => (
<form className={className} onSubmit={handleSubmit}>
<h2>LSPL (Low Stock Presentation Level)</h2>
<Line />
<InputGroup>
<TextFieldWithValidation name="lsplMan" label="LSPL Manual" input={{ onChnage:'', value: 'Current' }} />
</InputGroup>
</form>
);
Below is my TextFieldWithValidation code.
export const TextFieldWithValidationComponent = ({
meta,
input,
noStyles,
...otherProps
}) => (
<TextField
state={noStyles ? textFieldStates.DEFAULT : getState(meta)}
errorMessage={meta.touched ? meta.error : null}
{...input}
{...otherProps}
/>
);
Below is my TextField code.
const TextField = ({
className,
label,
description,
state,
errorMessage,
isEditable,
spaceAtBottom, // Not used, but we don't want it in otherProps
...otherProps
}) => {
const inputId = _.uniqueId();
return (
<div className={className}>
{label &&
<label htmlFor={inputId}>{label}</label>
}
<div className="input-group" id={isEditable ? 'editable' : 'readonly'}>
<input
id={inputId}
readOnly={!isEditable}
{...otherProps}
/>
{getStatusIcon(state)}
{errorMessage &&
<Error>{errorMessage}</Error>
}
{description &&
<Description>{description}</Description>
}
</div>
</div>
);
};
Can someone help me to fix this issue? Any help would be appreciated. Thanks

You can use State Hook for manage state in functional component.
Example :
const Message = () => {
const [message, setMessage] = useState( '' );
return (
<div>
<input
type="text"
value={message}
placeholder="Enter a message"
onChange={e => setMessage(e.target.value)}
/>
<p>
<strong>{message}</strong>
</p>
</div>
);
};

Yu defined onChange as empty string in EditStyleFormComponent component. So on any change input component just do nothing.
onChange should be some function that will update value.
If you want to use functional components there are two possible solutions:
Lift state up to parent component of EditStyleFormComponent (in case parent is class based component)
Use React Hooks like so (just example!)
const EditStyleFormComponent = ({
submitting,
invalid,
}) => {
const [inputValue, setInputValue] = useState ('Current'); // default value goes here
return <form className={className} onSubmit={handleSubmit}>
<h2>LSPL (Low Stock Presentation Level)</h2>
<Line />
<InputGroup>
<TextFieldWithValidation name="lsplMan" label="LSPL Manual" input={{ onChnage: (e) => { setInputValue(e.target.value); }, value: inputValue }} />
</InputGroup>
</form>
};

Related

Override input value in Child component in React

I have a child input component and parent where I set value.
Parent component
const \[dummyText, setDummyText\] = useState('')
return (
<Input
{...props}
id="first-name"
label="First name"
type="text"
placeholder="Please enter your first name"
value={dummyText}
onChange={(value) => setDummyText(value.target.value)}
/>
)
Child input component
import React, { InputHTMLAttributes, forwardRef } from 'react'
export type Props = {} & InputHTMLAttributes<HTMLInputElement>
const FormInput: React.FC<Props> = forwardRef<HTMLInputElement, Partial<Props>>(
({ ...props }, ref) => <input ref={ref} {...props} />
)
FormInput.displayName = 'FormInput'
export default FormInput
I want to add button in child component where on click I need to clear the value.
The thing is that I want to have clear functionality in child component and I do not want to write extra code for every <Input in parent, and whatever I do in child component, I just can't override props.value.
Does anybody have any experience how to solve this?
If you want to clear the input value you can pass the clearing function as a prop to the child component like this:
const Input = ({ onClearInput, ...otherProps }) => {
return (
<div style={{ display: 'flex', gap: '.5rem' }}>
<input {...otherProps} />
<button onClick={onClearInput}>Clear</button>
</div>
);
};
export default function App() {
const [firstName, setFirstName] = useState('');
const [secondName, setSecondName] = useState('');
const onClearInputHandler = (handler) => handler('');
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '.5rem' }}>
<Input
id="first-name"
label="First name"
type="text"
placeholder="Please enter your first name"
value={firstName}
onChange={(event) => setFirstName(event.target.value)}
onClearInput={() => onClearInputHandler(setFirstName)}
/>
<Input
id="second-name"
label="Second name"
type="text"
placeholder="Please enter your second name"
value={secondName}
onChange={(event) => setSecondName(event.target.value)}
onClearInput={() => onClearInputHandler(setSecondName)}
/>
</div>
);
}
Because you'll have a different state for each input you can pass the set function from useState as a parameter and run it inside the onClearInputHandler to clear the value.
Edit:
Then I think the easiest solutions is to handle the clear function inside the input itself like this:
const Input = (props) => {
const [value, setValue] = useState(props.value || '');
return (
<div style={{ display: 'flex', gap: '.5rem' }}>
<input
{...props}
value={value}
onChange={(event) => setValue(event.target.value)}
/>
<button onClick={() => setValue('')}>Clear</button>
</div>
);
};
export default function App() {
const onSubmitHandler = (event) => {
event.preventDefault();
const formData = new FormData(event.target);
const formProps = Object.fromEntries(formData);
console.log(formProps);
};
return (
<form action="submit" onSubmit={onSubmitHandler}>
<div style={{ display: 'flex', flexDirection: 'column', gap: '.5rem' }}>
<Input
id="first-name"
label="First name"
type="text"
placeholder="Please enter your first name"
name="first-name"
/>
<Input
id="second-name"
label="Second name"
type="text"
placeholder="Please enter your second name"
name="second-name"
/>
</div>
<button type="submit" style={{ margin: '.5rem 0' }}>
Submit
</button>
</form>
);
All inputs are inside the form and you can read the values when the form is submitted. Otherwise you'll have to create a new state or ref for every single Input you have in your parent component. This way the input value and the handler to clear it are always inside the Input component.
Here's a demo on stackblitz: https://stackblitz.com/edit/react-gnyvav?file=src%2FApp.js
There are a few solutions for this, I came up with these two
onValueChange
Another solution might be to refactor the onChange to a onValueChange which instead of the event immediately returns the value. Updated the type and removed the Partial since it was giving some errors.
type Props = {
onValueChange: (value: string) => void;
} & InputHTMLAttributes<HTMLInputElement>;
const FormInput: React.FC<Props> = forwardRef<HTMLInputElement, Props>(
({ onValueChange, ...props }, ref) => {
return (
<>
<input
ref={ref}
onChange={(e) => onValueChange(e.target.value)}
{...props}
/>
<button onClick={() => onValueChange("")}>clear</button>
</>
);
}
);
A issue with this is that if a onChange is provided it will override the existing onChange. We can solve this by simply adding it and passing the e (event)
const FormInput: React.FC<Props> = forwardRef<HTMLInputElement, Props>(
({ onValueChange, onChange, ...props }, ref) => {
return (
<>
<input
ref={ref}
onChange={(e) => {
onValueChange(e.target.value);
onChange(e);
}}
{...props}
/>
<button onClick={() => onValueChange("")}>clear</button>
</>
);
}
);
Which you can then use by
<FormInput
id="first-name"
type="text"
placeholder="Please enter your first name"
value={dummyText}
onValueChange={setDummyText}
// onChange={(e) => console.log(e)}
/>
onClear
Now this does not fully move the functionality to the Input component.
We destructure the onClear function and add it to the onClick of the clear button.
type Props = { onClear: () => void } & InputHTMLAttributes<HTMLInputElement>;
const FormInput: React.FC<Props> = forwardRef<HTMLInputElement, Props>(
({ onClear, ...props }, ref) => {
return (
<>
<input ref={ref} {...props} />
<button onClick={onClear}>clear</button>
</>
);
}
);
Now to use it you can go like this
<Input
id="first-name"
onClear={() => setDummyText("")}
type="text"
placeholder="Please enter your first name"
value={dummyText}
onChange={(event) => setDummyText(event.target.value)}
/>

How to pass state props to another component

I want to pass a state variable to another component but i don't get it what i'm doing wrong.
I have a component for radio inputs and I need to pass that taskLabel to Form component.
path is components/inputs/index.js
const TaskLabel = (props) => {
const [taskLabel, setTaskLabel] = useState('');
return (
<div label={props.taskLabel} >
<input
type='radio'
name='label'
value='urgent'
onChange={(e) => setTaskLabel(e.target.value)}
/>
<input
type='radio'
name='label'
value='not-urgent'
onChange={(e) => setTaskLabel(e.target.value)}
/>
</div>
);
};
i want to receive the taskLabel value, to use it in submitHandler function.
components/form/index.js
const Form = ({taskLabel}) => {
return (
<form onSubmit={submitHandler}>
<input
type='text'
placeholder='Text here'
className='form-input'
value={task}
onChange={(e) => {
setTask(e.target.value);
}}
/>
<TaskLabel taskLabel={taskLabel} />
</form>
)
}
This is what i tried, to pass taskLabel props from label={taskLabel}.
You need to move your state, to Form component, like this:
const [labelProp, setLabelProp] = useState("");
Your TaskLabel component should be
<TaskLabel label={{ labelProp, setLabelProp }} />
That means, you send label, as a prop to TaskLabel component.
In TaskLabel component you need to recive the prosp, by passing to component {label}.
Next, for every input use onChange={(e) => label.setLabelProp(e.target.value)}.
Edited sandbox => https://codesandbox.io/s/laughing-proskuriakova-dhi568?file=/src/components/task-label/index.js
Generally speaking, the concept is "data-down, actions-up". So if you want to pass data down to a lower-level component, you just pass a prop. If you want to update a value from a lower-level component to a higher-level component, you could pass a setter function down as a prop and call that.
Note I just call it taskLevelProp for a little more clarity. You should probably use a better name.
TaskLabel (lower level component)
/* It looks like TaskLabelProp gets passed in from something else.
Otherwise, you would use useState to get your setter function */
const TaskLabel = ({taskLabelProp, setTaskLabelProp}) => {
return (
<div label={props.taskLabel} >
<input
type='radio'
name='label'
value='urgent'
onChange={(e) => setTaskLabelProp(e.target.value)}
/>
<input
type='radio'
name='label'
value='not-urgent'
onChange={(e) => setTaskLabelProp(e.target.value)}
/>
</div>
);
};
Form (higher level component)
const Form = () => {
const [taskLabelProp, setTaskLabelProp] = useState('');
return (
<form onSubmit={submitHandler}>
<input
type='text'
placeholder='Text here'
className='form-input'
value={task}
onChange={(e) => {
setTask(e.target.value);
}}
/>
<TaskLabel taskLabel={taskLabelProp, setTaskLabelProp} />
</form>
)
}
Let me know if this answers your question.
EDIT: Made Form use useState. Based off your code, I was assuming you were using useState at the app level.
function App() {
const [task, setTask] = useState("");
const [taskLabelProp, setTaskLabelProp] = useState("");
const handleChange = (e) => {
setTaskLabelProp(e.target.value);
};
const submitHandler = (e) => {
e.preventDefault();
};
return (
<div className="App">
<form onSubmit={submitHandler}>
<input
type="text"
placeholder="Text here"
className="form-input"
value={task}
onChange={(e) => {
setTask(e.target.value);
}}
/>
<TaskLabel
onChange={handleChange}
taskLabelProp={taskLabelProp}
taskLabel="select"
/>
<button type="submit">fs</button>
</form>
</div>
);
}
export default App;
const TaskLabel = ({ taskLabel, onChange, taskLabelProp }) => {
return (
<div label={taskLabel}>
<input
type="radio"
name="label"
value="urgent"
onChange={(e) => onChange(e)}
checked={taskLabelProp === "urgent"}
/>
urgent
<input
type="radio"
name="label"
value="not-urgent"
onChange={(e) => onChange(e)}
checked={taskLabelProp === "not-urgent"}
/>
not-urgent
</div>
);
};
export default TaskLabel;

Unable to change text box value in React JS

Hi I am working on React JS application. I have assigned default value to textbox on page load. User should be allowed to change this but user is not able to edit the value in textbox. Below is my code.
const EditStyleFormComponent = ({
submitting,
invalid,
}) => (
<form className={className} onSubmit={handleSubmit}>
<h2>LSPL (Low Stock Presentation Level)</h2>
<Line />
<InputGroup>
<TextField value="Current" label="LSPL Manual" isEditable="true" />
</InputGroup>
</form>
);
Below is my TextField.js
const TextField = ({
className,
label,
description,
state,
errorMessage,
isEditable,
spaceAtBottom, // Not used, but we don't want it in otherProps
...otherProps
}) => {
const inputId = _.uniqueId();
return (
<div className={className}>
{label &&
<label htmlFor={inputId}>{label}</label>
}
<div className="input-group" id={isEditable ? 'editable' : 'readonly'}>
<input
id={inputId}
readOnly={!isEditable}
{...otherProps}
/>
{getStatusIcon(state)}
{errorMessage &&
<Error>{errorMessage}</Error>
}
{description &&
<Description>{description}</Description>
}
</div>
</div>
);
};
Can someone help me to fix this issue? Any help would be appreciated. Thanks
Using Uncontrolled input, you may use defaultValue
const TextField = ({
className,
label,
description,
state,
errorMessage,
isEditable,
spaceAtBottom, // Not used, but we don't want it in otherProps
...otherProps
}) => {
const inputId = 1;
return (
<div>
{label &&
<label htmlFor={inputId}>{label}</label>
}
<div className="input-group" id={isEditable ? 'editable' : 'readonly'}>
<input
id={inputId}
readOnly={!isEditable}
{...otherProps}
/>
{errorMessage &&
<Error>{errorMessage}</Error>
}
{description &&
<Description>{description}</Description>
}
</div>
</div>
);
};
const EditStyleFormComponent = ({
submitting,
invalid,
}) => (
<form>
<h2>LSPL (Low Stock Presentation Level)</h2>
<TextField defaultValue="Current" label="LSPL Manual" isEditable="true" />
</form>
);
class Hello extends React.Component {
render() {
return <div><EditStyleFormComponent/></div>;
}
}
ReactDOM.render(
<Hello name="World" />,
document.getElementById('container')
);
See the fiddle here https://jsfiddle.net/0f6n85ym/
Alternatively you can also do it in controlled input.
const TextField = ({
className,
label,
description,
state,
errorMessage,
isEditable,
spaceAtBottom, // Not used, but we don't want it in otherProps
...otherProps
}) => {
const inputId = 1;
return (
<div>
{label &&
<label htmlFor={inputId}>{label}</label>
}
<div className="input-group" id={isEditable ? 'editable' : 'readonly'}>
<input
id={inputId}
readOnly={!isEditable}
{...otherProps}
/>
{errorMessage &&
<Error>{errorMessage}</Error>
}
{description &&
<Description>{description}</Description>
}
</div>
</div>
);
};
const EditStyleFormComponent = ({
submitting,
invalid,
value,
onChange
}) => (
<form>
<h2>LSPL (Low Stock Presentation Level)</h2>
<TextField value={value} onChange={onChange} label="LSPL Manual" isEditable="true" />
</form>
);
class Hello extends React.Component {
constructor(props){
super(props);
this.state = {
name: 'Current'
}
}
onChange = (e)=>{
this.setState({name: e.target.value});
}
render() {
return <div><EditStyleFormComponent value={this.state.name} onChange={this.onChange}/></div>;
}
}
ReactDOM.render(
<Hello name="World" />,
document.getElementById('container')
);
See the fiddle here
https://jsfiddle.net/bshumpy0/
You can use logical OR operator to set defaultValue if value is not provided(if you use controlled input)
Like that:
class App extends Component {
constructor() {
super();
this.state = {
value: ''
};
this.handleChange = this.handleChange.bind(this)
}
handleChange(event) {
this.setState({value: event.target.value});
}
render() {
return (
<div>
<TextField value={this.state.value} defaultValue='213' onChange={this.handleChange} />
</div>
);
}
}
And in TextField component:
<input type='text' value={value || defaultValue} onChange={onChange}/ >
Full example - https://stackblitz.com/edit/react-4daxck

How to add react-phone-number-input to -react-final-form?

I'm currently creating a form using react-final-form and trying to use react-phone-number-input with it through integration as an adapter, as displayed through this example.
I attempted to use the example to learn how it is done, but I'm not sure how to access the component and create the adapter for it properly.
import React from 'react';
import { Form, Field } from 'react-final-form';
import PhoneInput from 'react-phone-number-input';
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
const onSubmit = async values => {
await sleep(300)
window.alert(JSON.stringify(values, 0, 2))
}
const PhoneAdapter = ({ input, meta, ...rest }) => (
<PhoneInput
{...input}
{...rest}
value={input.value}
onChange={(event, value) => input.onChange(value)}
/>
)
class ContactForm extends React.Component {
render() {
return (
<>
<Form
onSubmit={onSubmit}
initialValues={{ }}
render={({ handleSubmit, form, submitting, pristine, values }) => (
<form onSubmit={handleSubmit}>
<fieldset>
<Field component={PhoneAdapter} />
</fieldset>
<fieldset>
<button type="submit" disabled={submitting || pristine}>
Submit
</button>
</fieldset>
<pre>{JSON.stringify(values, 0, 2)}</pre>
</form>
)}
/>
</>
);
}
}
export default ContactForm;
Update: July 2019
Apparently, all you need to do is to spread the input property of Field. Works flawlessly. Learn about spreading if you're not familiar with it.
const PhoneAdapter = ({ input }) => (
<PhoneInput {...input} />
)
<Field name="phone" placeholder="Enter phone number" component={PhoneAdapter} />
I ended up experimenting with the FieldRenderProps props until it worked out. I wasn't so sure whether it would work or not, as react-phone-number-input is two elements in a component. I thought it would implement the input on only one of the elements.
By using input, I gain access to the input's props. Hence, I called upon it's value, as the default looks like so:
<PhoneInput
placeholder="Enter phone number"
value={ this.state.value } // This is what I called.
onChange={ value => this.setState({ value }) }/>
I then did the same for the onChange function prop.
const PhoneAdapter = ({ input }) => (
<PhoneInput value={input.value.value} onChange={value => input.onChange(value)} />
)
Finally, I used the component adapter like so:
<Field name="phone" placeholder="Enter phone number" component={PhoneAdapter} />

with react final-form why is meta.touched always false with third party components?

using final-form, i have a third party input component. i've written an adapter for it. it has a validator as well, but meta.touched is always false. i've tried propagating the onFocus event up to the input, but no luck. what am i doing wrong?
const requiredValidator = value => (value ? undefined : 'is required');
const FloatingLabelInputAdapter = ({ input, meta, ...rest }) => (
<FloatingLabelInput
{...rest}
onChange={(event) => input.onChange(event)}
onFocus={(event) => input.onFocus(event)}
errorText={meta.touched ? meta.error : ''}
/>
)
// used like this:
<Field
component={FloatingLabelInputAdapter}
label="Email"
name="email"
type="text"
validate={requiredValidator}
/>
// and here's the render() of the component
render() {
const { children, label } = this.props;
const { focussing, used } = this.state;
console.log('FloatingLabelInput.props', this.props);
return (
<Group {...this.props} >
<TextInput
focussing={focussing}
innerRef={(comp) => { this.input = comp }}
onFocus={this.onFocusHandle}
onBlur={this.onBlurHandle}
onChange={this.onChange}
type={this.props.type} />
<Label
focussing={focussing}
used={used}>
{label}
</Label>
<Bar focussing={focussing} />
</Group>
);
}
}
annnnd as usual i answer my own question.
i had to propagate the onBlur() event as well, which makes sense since touched docs say it's true only after user has entered and left focus on the input.
<FloatingLabelInput
...
onBlur={(event) => input.onBlur(event)}
/>

Resources