React - pass props into input component - reactjs

What I need is to be able to customize textAreaCount and other props in each separate instance of <Texarea/>. The textAreaCount is different for each <Texarea/> so how do I modify the component to be able to pass in custom textAreaCount for each <Texarea/>?
https://codesandbox.io/s/rkv88-forked-vjo0rn?file=/src/App.js:0-948
import React, { useState } from "react";
const Textarea = (value, id, maxLength, textAreaLimit) => {
const [textAreaCount, ChangeTextAreaCount] = React.useState(0);
const [state, setstate] = useState({
headline: "",
title: ""
});
const { headline, title } = state;
const changevalue = (e) => {
setstate({
...state,
[e.target.name]: value
});
ChangeTextAreaCount(e.target.value.length);
};
return (
<>
<p>{textAreaCount}/{textAreaLimit}</p>
<textarea
type="text"
rows={5}
id={id}
value={value}
maxLength={maxLength}
onChange={(e) => {
changevalue(e);
}}
/>
</>
);
};
export default function FullWidthTabs() {
return (
<div>
<Textarea value={headline} id="test" maxLength={5} textAreaLimit={5}/>
<Textarea value={title} id="test2" maxLength={10} textAreaLimit={10}/>
</div>
);
}

Forward the props you need.
const Textarea = (props) => {
const [textAreaCount, setTextAreaCount] = React.useState(0);
const recalculate = (e) => {
setTextAreaCount(e.target.value.length);
};
return (
<>
<p>{textAreaCount}/5</p>
<textarea type="text" rows={5} maxLength={5} onChange={recalculate} {...props} />
</>
);
};
Now it will forward any props into the textarea element. This will set the id and will overwrite the rows prop.
<Textarea id="textarea-1" rows={4} />
<Textarea id="textarea-2" rows={5} maxLength={10} />

As we can see you try to pass props as below:
<Textarea value={title} id="test2" maxLength={10} textAreaLimit={10}/>
But In Your Textarea Component you received props argument as multiple args as below:
const Textarea = (value, id, maxLength, textAreaLimit) => {
return (
<>
</>
);
};
Instead that you need to destruct your props argument or you can set whole passed value props as single object props as below:
Method 1:
const Textarea = ({value, id, maxLength, textAreaLimit}) => {
return (
<>
<textarea type="text" id={id} value={value} rows={5} maxLength={maxLength} onChange={recalculate} textAreaLimit={textAreaLimit} />
</>
);
};
Method 2:
const Textarea = ({...props}) => {
return (
<>
<textarea type="text" id={id} value={value} rows={5} maxLength={maxLength} onChange={recalculate} textAreaLimit={textAreaLimit} />
</>
);
};
Method 3:
const Textarea = (props) => {
return (
<>
<textarea type="text" id={props.id} value={props.value} rows={5} maxLength={props.maxLength} onChange={recalculate} textAreaLimit={props.textAreaLimit} />
// Or Instead you can do as below
// <textarea type="text" rows={5} maxLength={5} onChange={recalculate} {...props} />
</>
);
};

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 implement custom handleOnChange in Formik

as I mentioned in the title, how can I implement my custom handleChange function?
Here's standard example:
const MyTextField = ({ label, ...props }) => {
const [field, meta, helpers] = useField(props);
return (
<>
<input {...field} {...props} />
</>
);
};
const Example = () => (
<div>
<h1>My Form</h1>
<Formik
initialValues={{}}
onSubmit={() => {}}
>
{() => (
<Form>
<MyTextField name="firstName" type="text" label="First Name" />
</Form>
)}
</Formik>
</div>
);
Let's say I have onChange function:
const customHandle = (e) => {
console.log('Custom Function')
}
How can I combine my custom function with Formik
EDIT.
Explanation: I will not use form in my case, I just want to have simple input which will update a 'redux or something else state managmenent'

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;

Value from input field does not display

I am trying to set the value inside an input in a Form component using KendoReact but the value is not displayed and upon debugging I see that the value is not defined. What am I doing wrong and what is the right approach here? Here is my code:
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Form, Field, FormElement } from '#progress/kendo-react-form';
import { Input } from '#progress/kendo-react-inputs';
const InputField1 = (fieldRenderProps) => {
const { label, value } = fieldRenderProps;
return (
<div>
<Input label={label} value={value} />
</div>
);
};
const App = () => {
return (
<div>
<Form
render={(formRenderProps) => (
<>
<br />
<br />
<div>
<Field
name="Field1"
label="Field1 Label"
component={InputField1}
value="value1"
/>
</div>
<br />
<br />
</>
)}
/>
</div>
);
};
ReactDOM.render(<App />, document.querySelector('my-app'));
Here is a working codesandbox You have to pass the other props to the Input:
const InputField1 = (fieldRenderProps) => {
const { label, value, ...others } = fieldRenderProps;
console.log(value);
return (
<div>
<Input label={label} value={value} {...others} />
</div>
);
};
const MyCustomInput = (fieldRenderProps) => {
const {label, value, onChange} = fieldRenderProps;
return (
<Input label={label} value={value} onChange={onChange} />
);
};
const App = () => {
const handleSubmit = (dataItem) => alert(JSON.stringify(dataItem, null, 2));
return (
<Form
onSubmit={handleSubmit}
render={(formRenderProps) => (
<FormElement style={{maxWidth: 650}}>
<Field name={'firstName'} label={'First Name'} component={MyCustomInput} />
<div className="k-form-buttons">
<button type={'submit'} disabled=
{!formRenderProps.allowSubmit} className={'k-button'}>
Submit
</button>
</div>
</FormElement>
)}
/>
);
};
ReactDOM.render(
<App />,
document.querySelector('my-app')
);
you have to add onChange in input tag

Is it possible to simple-react-code-editor as a Formik field component?

Trying to get this form field component to take a simple-react-code-editor:
not sure if I'm going about this the right way by trying to pass props form the useField hook, but it works for textfield tags, so thought the same method could apply to this as well. Although, I get the feeling the onValueChange callback is different from the onChange callback that this component doesn't have. Is there a way to add it somehow?
Editor Component:
const MyEditor = ({value, onChange}) => {
const highlight = (value) => {
return(
<Highlight {...defaultProps} theme={theme} code={value} language="sql">
{({ tokens, getLineProps, getTokenProps }) => (
<>
{tokens.map((line, i) => (
<div {...getLineProps({ line, key: i })}>
{line.map((token, key) => (
<span {...getTokenProps({ token, key })} />
))}
</div>
))}
</>
)}
</Highlight>
)
};
return (
<Editor
value={value}
onValueChange={onChange}
highlight={highlight}
padding={'40px'}
style={styles.root}
/>
);
}
export default MyEditor;
Form with Field Component as MyEditor (tried using useField hook):
const FormQueryTextBox = ({...props}) => {
const [field] = useField(props);
return (
<MyEditor onChange={field.onChange} value={field.value}/>
)
}
const validationSchema = yup.object({
query_name: yup
.string()
.required()
.max(50)
});
const AddQueryForm = () => {
return (
<div>
<Formik
validateOnChange={true}
initialValues={{
query:""
}}
validationSchema={validationSchema}
onSubmit={(data, { setSubmitting }) => {
console.log(data);
}}
>
{() => (
<Form>
<div>
<Field
placeholder="query name"
name="query_name"
type="input"
as={TextField}
/>
</div>
<div>
<Field
name="query"
type="input"
as={FormQueryTextBox}
/>
</div>
</Form>
)}
</Formik>
</div>
)
}
components render without errors, but as I type the text doesn't appear.
I figured out that I just need to customize my onChange with the setter from the useFormikContext hook like this:
const FormQueryTextBox = ({...props}) => {
const [field] = useField(props);
const { setFieldValue } = useFormikContext();
return (
<MyEditor {...field} {...props} onChange={val => {
setFieldValue(field.name, val)
}}/>
)
}

Resources