THE PROBLEM:
I have a simple Form Select Component. It has several props, and uses React Select V2, as the implementation layer. And I want to pass some extra props. Here is the code:
const SelectDropdownField = ({
prop1,
prop2.
...restProps
}: SelectProps): JSX.Element => (
<Field
name={name}
render={({
field,
form: { setFieldValue, setFieldTouched, errors, touched }
}: FieldProps<FormikValues>): ReactNode => (
return (
</div>
<ReactSelect
.. //someFieldsHere
{...restProps} // this doesn't work
/>
{JSON.Stringify({...restProps}, null, 2)} // this works
</div>
);
}}
/>
);
I am trying to pass the data-testid attribute. But if I pass it inside the ReactSelect, it doesn't work. Outside of it, it works just fine. Any ideas??
Unfortunately, after looking at the code, it seems impossible.
By checking here the render function of react-select, we can see that the only way we could add custom props is via the commonProps object. But checking each child component, there's no where it reaches the DOM. Only innerProps reaches the DOM but the library user has no control over it.
Related
I'm refactoring some code in my app and I noticed that when I moved my input element to be its own reusable component, hook-form's register function stopped working. This can be fixed if you use the plain input element, but I would like to use my own component. Here's a stack blitz with a reproducible example: https://stackblitz.com/edit/react-ts-9bafks?file=App.tsx
If you check what register('text') actually gives you console.log(register("text")) you will see that there is a ref. You have to make your custom inputs to forward that ref.
const TextInput: FC<Props> = React.forwardRef(({ error, ...props }, ref) => {
return (
<div>
<p> {error} </p>
<input {...props} ref={ref}/>
</div>
);
});
I have successfully integrated the creatable with react-final-form but the problem arises when i try to do an API call. I am trying to do an API call with onIputChange but whenever i get a response(in props), my drop down closes. Its closing up on receiving the new options. I tried it with normal select and it works fine. Here is my example code.
<Field
name="tags"
component={ ({
input,
...rest
}) => (<CreatableSelect {...input}
{...rest}}
/>)}
isMulti
options={sortedTagsOptions}
onInputChange={(x) => handleChangeTypeheadInput(x)}/>
The API call works fine. Only one issue occurs, the dropdown closes and goes out of focus as well as the written value goes away in the select. If anyone has any idea on how to deal with this problem please let me know. This works fine without Field component though. Maybe mutate the form values? but having problems in that regard as well.
Solved it by creating a separate component rather than doing it all in one component.
const AppCreateableSelectForm = ( {
input: {
name, onChange, value,
},
className,
incomingOptions,
isClearable,
isMulti,
onFocus,
closeMenuOnSelect,
incomingStyle,
isDisabled,
placeholder,
onInputChange,
noOptionsMessage,
}) => {
return <CreatableSelect
name={name}
isMulti={isMulti}
onFocus={onFocus}
closeMenuOnSelect={closeMenuOnSelect}
options={incomingOptions}
isClearable={isClearable}
placeholder={placeholder ? placeholder:`Select`}
className={className}
isDisabled={isDisabled}
styles={incomingStyle ? incomingStyle : style}
value={value}
onChange={onChange}
onInputChange={onInputChange}
noOptionsMessage={noOptionsMessage}
theme={theme => ({
...theme,
colors: {
...theme.colors,
primary: `#637282`,
},
})}
components={{
IndicatorSeparator: () => null,
}}
/>
Some props might not be used in this code since i copied only relative code.
Using ant design input and forms, I am having trouble using intialValues when a component is referenced in. In this case, I am trying to pass in CustomInput.js, a ant design Input, into my form as <Component/>. Everything works except the initialValues.
Any ideas what to do to make intialValues work?
https://codesandbox.io/s/initialvalues-forked-keftp?file=/index.js:382-414
You have to pass the props from your custom component down to Input, specifically the value prop:
function CustomInput(
{ field, value = "", onChange = (e) => {}, disabled, ...rest },
ref
) {
return (
<Input
ref={ref}
value={value}
onChange={onChange}
disabled={disabled}
{...rest}
/>
);
}
DEMO
Note: I'm unsure as to what your field prop is. Since it's not a supported prop as part of the component's API, I left it out.
import React from "react";
import { Field, Form } from "react-final-form";
export function LogInDialog(props: { open: boolean; onClose: () => void }) {
const onSubmit = vals => {
alert(JSON.stringify(vals));
};
console.log("logindialog");
return (
<Form
key="unique_key_0"
onSubmit={onSubmit}
render={({ handleSubmit, form, submitting, pristine, values }) => (
<form onSubmit={handleSubmit} key="unique_key_1" id="unique_id_1">
<Field
key="unique_key_2"
id="unique_id_2"
name="email"
component={({ input: { onChange, value }, label }) => (
<input
key="unique_key_3"
id="unique_id_3"
type="text"
value={value}
onChange={onChange}
/>
)}
></Field>
</form>
)}
/>
);
}
The input is losing its focus after every keystroke. In devtools, I can see the HTML form is created anew every time (it flashes pink). The React component itself however goes through rendering only once.
There are similar questions, however all of them suggest using unique keys. Such a solution doesn't seem to be working here.
Why is the input losing its focus over and over again? How do I fix it?
https://codesandbox.io/s/busy-torvalds-91zln
Since an inline lambda is used for the component, its identity changes every render.
While according to many other questions an unique key should be enough, moving the component function outside the master component fixes it altogether.
In the example given for <Formik /> here we can gain access to the Formik context props in children with this code:
import React from 'react';
import { Formik } from 'formik';
const BasicExample = () => (
<div>
<h1>My Form</h1>
<Formik
initialValues={{ name: 'jared' }}
onSubmit={(values, actions) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}, 1000);
}}
>
{props => (
<form onSubmit={props.handleSubmit}>
<input
type="text"
onChange={props.handleChange}
onBlur={props.handleBlur}
value={props.values.name}
name="name"
/>
{props.errors.name && <div id="feedback">{props.errors.name}</div>}
<button type="submit">Submit</button>
</form>
)}
</Formik>
</div>
);
I'm having an issue understanding what's happening here:
>
{props => (
Where is this 'props' coming from and what sort of syntax is this. I have a feeling the syntactic sugar of the <Formik /> is making this harder to wrap my head around.
Thanks.
The props you get there are known as render props, please refer to this link for official documentation.
Render Props are a way of passing props down to the children
Using this style pattern, we render children as a function & we can pass parameters to that
In the same way, <Formik/> passding down some renderProps down the children
you can refer to this <Formik/> component code, if you want to see how the props are being passed & how the children are being rendered
Please refer to this article with detailed explanation of render props
Please refer to this code sandbox for live example
Remember that in JSX anything inside two tags is the children.
E.g
<div>
I’m the child
</div>
Well what if you make the children a function?
<div>
{() => { console.log('The children has been executed')}
</div>
Well, if the wrapping component expects it’s children to be a function (the same as an onClick prop is a function) it can call it and pass data when it does.
This is a way of using data from a child component as input to determine what to render.
Here formik is one such component - and it will pass the form props to the children function. This is also known as render props.