Unable to change text box value in React JS - reactjs

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

Related

TypeError: register is not a function using React Hook Form in React

The Error Message:
If i dont use the Inputs inside div then it works perfectly but when i use Input inside div it shows me this error.
I wanted to keep the hook related stuff separated so it look clean.
why does it only works when its not inside a div?
Login.tsx
import { useHistory } from "react-router-dom";
import { useForm } from "react-hook-form";
import useAuth from "./../hooks/useAuth";
import { Form, Input } from "../components/FormGroup";
import MacNav from "../components/MacNav";
import { loginActionUrl } from "./../services/ApiLinks";
import {
fetchPostResopnse,
successPopUp,
errorPopUp,
} from "./../services/FormHelper";
type Tinputs = {
username: string;
password: string;
};
function Login() {
const auth = useAuth();
const history = useHistory();
const methods = useForm<Tinputs>();
const onSubmit = async (data: Tinputs) => {
const result = await fetchPostResopnse(loginActionUrl, data);
if (result.isAuth) {
successPopUp("Credentials Matched", () => {
auth.signIn(result);
history.push("/admin/dashboard");
});
} else {
errorPopUp("Credentials Does Not Matched");
}
};
return (
<div>
<MacNav />
<div className="section-secondary">
<div className="container">
<div className="contact-form-wrapper">
<div className="title-lg text-center">Enter Your Credentials</div>
<Form formMethods={methods} handler={onSubmit} submitBtn="Submit">
{/*If i dont use Input inside div it works*/}
<div>
<Input name="username" rule={{ required: true }} />
</div>
<Input name="password" rule={{ required: true }} />
</Form>
</div>
</div>
</div>
</div>
);
}
export default Login;
I have wrote the form components here.
FormGroup.tsx
import React from "react";
const Form = ({ children, formMethods, handler, submitBtn }: any) => {
return (
<form onSubmit={formMethods.handleSubmit(handler)}>
{React.Children.map(children, (child) => {
return child.props.name ? (
<div>
{React.createElement(child.type, {
...{
...child.props,
register: formMethods.register,
key: child.props.name,
},
})}
{child.props?.rule && formMethods.errors[child.props.name] && (
<div className="text-danger">
*
{formMethods.errors[child.props.name].message
? formMethods.errors[child.props.name].message
: `${child.props.name} is required`}
</div>
)}
</div>
) : (
child
);
})}
{submitBtn && <button type="submit">{submitBtn}</button>}
</form>
);
};
const Input = ({ register, name, label, rule, ...rest }: any) => {
label = label ? label : name?.charAt(0).toUpperCase() + name?.slice(1);
return (
<div>
<label htmlFor={name}>{label}</label>
<input name={name} ref={register(rule)} {...rest} />
</div>
);
};
const Textarea = ({ register, name, label, rule, ...rest }: any) => {
label = label ? label : name?.charAt(0).toUpperCase() + name?.slice(1);
return (
<div>
<label htmlFor={name}>{label}</label>
<textarea name={name} ref={register(rule)} {...rest}></textarea>
</div>
);
};
const SubmitButton = ({ name, ...rest }: any) => {
return (
<button type="submit" {...rest}>
{name}
</button>
);
};
export { Form, Input, Textarea, SubmitButton };
[1]: https://i.stack.imgur.com/PvEUA.png
Hello according to your code, what happened it's expected
the div doesn't have name so according to this code
{React.Children.map(children, (child) => {
return child.props.name ? (
<div>
{React.createElement(child.type, {
...{
...child.props,
register: formMethods.register,
key: child.props.name,
},
})}
{child.props?.rule && formMethods.errors[child.props.name] && (
<div className="text-danger">
*
{formMethods.errors[child.props.name].message
? formMethods.errors[child.props.name].message
: `${child.props.name} is required`}
</div>
)}
</div>
) : (
child
);
})}
And the below child
<div>
<Input name="username" rule={{ required: true }} />
/div>
The Input component will be rendrered without register prop, so when it will try to call it here, however it's value is undefined, what will cause an error
ref={register(rule)}
I suggest to create a new component
const InputWithDiv = (props) => (
<div>
<Input rule={{ required: true }} {..props} />
/div>
);
and use it like below
<Form formMethods={methods} handler={onSubmit} submitBtn="Submit">
<InputWithDiv name="username" />
<Input name="password" rule={{ required: true }} />
</Form>

How to pass Parent props to Children, when rendering using this.props.children() and React.cloneElement?

<CheckboxGroup name="fruits" value={fruits} onChange={this.setFruits}>
{(cb) => (
<>
<label>
<Checkbox value="apple" /> Apple
</label>
<label>
<Checkbox value="orange" /> Orange
</label>
<label>
<Checkbox value="watermelon" /> Watermelon
</label>
</>
)}
</CheckboxGroup>
Here is the parent component. I want to pass the name, value and onChange to the children.
class CheckboxGroup extends React.Component {
constructor(props){
super(props);
this.state = {
value: this.props.value,
name: this.props.name
}
}
render() {
const children = React.Children.map(this.props.children(), child => {
return React.cloneElement(child);
});
return(
<>
{children}
</>
)
}
}
class Checkbox extends React.Component {
constructor(props){
super(props);
}
render() {
console.log(this.props);
return (
<>
<input type="checkbox" value="" name="" />
</>
);
}
}
How to get the props from parents in children? I am using this.props.children() not this.props.children. From parent, I have tried React.cloneElement(child, {this.props}) but does not work.
From what I can tell, there doesn't seem to be a need to use the cloneElement API. You should be able to accomplish what you're after with something much simpler.
We can provide our data (i.e. the fruits) at the very top level, and use the data structure to map over each item to render a checkbox component with the name, value and onChange callback. No need to clone anything, and our components have no state of their own, so they can be simple function components.
const Checkbox = ({ name, value, onChange, isChecked }) => (
<input
type="checkbox"
name={name}
value={value}
onChange={onChange}
checked={isChecked}
/>
)
const CheckboxGroup = ({ name, values, onChange, selectedValues }) => (
<div>
{values.map((value, index) => (
<label key={index}>
<Checkbox
{...{ name, value, onChange }}
isChecked={selectedValues.includes(value)}
/>
{value}
</label>
))}
</div>
)
// an example implementation...
const theFruits = ['Banana', 'Apple', 'Orange']
const MyFruitForm = () => {
const [selectedFruits, setSelectedFruits] = React.useState([])
const handleChange = (event) =>
setSelectedFruits((oldFruits) => {
if (event.target.checked) {
return [...oldFruits, event.target.value]
} else {
return oldFruits.filter((fruit) => fruit !== event.target.value)
}
})
return (
<CheckboxGroup
name="fruit-checkboxes"
values={theFruits}
onChange={handleChange}
selectedValues={selectedFruits}
/>
)
}
ReactDOM.render(<MyFruitForm />, document.getElementById('root'))
<script src="https://unpkg.com/react#17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#17/umd/react-dom.production.min.js"></script>
<div id="root"></div>

How to change focus to another component when Enter key is pressed

How can I change the focus to description field in TaskCard component, after enter key be pressed on InputField component?
That's the father component
export default class Task extends Component {
constructor(props) {
super(props)
this.state = { cardID: '', list: [] }
this.handleAdd = this.handleAdd.bind(this)
this.handleChange = this.handleChange.bind(this)
this.handleEdit = this.handleEdit.bind(this)
this.handleRemove = this.handleRemove.bind(this)
this.refresh()
}
handleAdd = (event, cardID) => {
...
}
handleChange(e) {
...
}
handleEdit = (e, task) => {
task.description = e.target.value
axios.put(`${URL}/${task._id}`, task)
.then(resp => this.refresh(this.state.description))
}
refresh() {
...
}
handleRemove = task => {
...
}
render() {
return (
<Fragment>
<Row className="card-columns">
<taskCard title="Task Board" cardID='1' color='blue-violet'
handleAdd={this.handleAdd}
handleChange={this.handleChange}
handleEdit={this.handleEdit}
description={this.state.description}
list={this.state.list}
handleRemove={this.handleRemove}
/>
</Fragment >
)
}
}
The TaskCard component contains the description input field. That's the field who needs to receive the focus:
const TaskCard = props => {
const renderRows = () => {
const list = props.list || []
return list.map(task => (
task.cardID === props.cardID &&
<InputField task={task}
handleChange={props.handleChange}
handleEdit={props.handleEdit}
handleRemove={props.handleRemove}
/>
))
}
return (
<Fragment>
{renderRows()}
<Form>
{/* input who needs to receive focus */}
<Form.Control className='input-field description' name='description' type="text" placeholder="Insert a task"
onChange={props.handleChange}
onKeyPress={event => {
if (event.key === 'Enter') {
props.handleAdd(event, props.cardID)
event.target.value= ''
event.preventDefault()
}
}}
/>
</Form>
</Fragment >
)
}
export default TaskCard
Input field component is where the user edit a task and press Enter. When Enter is pressed, the task is modified and I'd like to focus on description field in TaskCard component
const InputField = props => {
return (
<Fragment>
<Row>
<Col xs={12} style={{paddingRight: 0}}>
<Form>
<Form.Group controlId="formBasicEmail">
<Form.Control className='inputs-field' type="text" name={props.task._id}
placeholder={props.task.description}
onChange={props.handleChange}
onKeyPress={event => {
if (event.key === 'Enter') {
props.handleEdit(event, props.task)
event.preventDefault()
}
}}
/>
</Form.Group>
</Form>
</Col>
</Row>
</Fragment >
)
}
export default InputField
Use refs to get a reference to the input in TaskCard and pass it to the input you want to focus:
const TaskCard = props => {
// useRef here
const inputRef = useRef(null)
const renderRows = () => {
const list = props.list || []
return list.map(task => (
task.cardID === props.cardID &&
<InputField task={task}
handleChange={props.handleChange}
handleEdit={props.handleEdit}
handleRemove={props.handleRemove}
/>
))
}
return (
<Fragment>
{renderRows()}
<Form>
{/* input who needs to receive focus */}
...
// pass ref here
ref={inputRef}
}}
/>
</Form>
</Fragment >
)
}
export default TaskCard
Then in the <InputField /> Component pass the ref:
<InputField task={task}
handleChange={props.handleChange}
handleEdit={props.handleEdit}
handleRemove={props.handleRemove}
// pass the ref down
inputRef={inputRef}
/>
Then from within the <InputField /> component call it where ever you call the handleEdit function like this:
() => {
props.handleEdit()
props.inputRef.current.focus()
}

How to set state for text box in functional component

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>
};

Getting undefined for Field name in reduxform

I have redux form in a class component and for some reason when I console log the formValues I am getting undefined what is the problem?
class CreateTeamBox extends Component{
handleFormSubmit({name}){
console.log(name);
}
renderError = ({ touched, error}) => {
if(touched && error) {
return(
<div>{error}</div>
);
}
}
renderInput = ({input, label, type, meta}) => {
return(
<div className={styles.formGroup}>
<label>{label}</label>
<input {...input} />
<div className={styles.errorWrapper}>
{this.renderError(meta)}
</div>
</div>
);
}
render() {
const {handleSubmit, error} = this.props ;
return(
<div className={styles.createTeamBox}>
<div className={styles.titleWrapper}>
<h2>create team</h2>
</div>
<div className={styles.bodyWrapper}>
<div className={styles.submitErrorWrapper}>
{error ? <Error error={error} /> : null}
</div>
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
<Field name="name" component={this.renderInput} label="name" />
<button className={styles.button} type="submit" >create</button>
</form>
</div>
</div>
)
}
}
const validate = (formValues) => {
console.log(formValues.name);
const name = formValues.name;
const errors = validateForm(name);
return errors;
}
export default reduxForm({
form: 'createTeamForm',
validate
})(CreateTeamBox);
I have other reduxform in the same given project with different names, is it causing the problem? Im not sure why it is happening as I havecopied most of the coe from working reduxform in the given project .
the default value of formValues will be empty object, after enter some data to the field then you can get the value, or you can pass the value by using initialValues

Resources