I am using Semantic-ui-react
Make the options a separate component and use React.memo to stop the options redrawing on every key press.
This isn't a Formik problem but one with how you are defining the options. Either wrap in .memo or even better, don't define the options with an inline .map
don't define the options with an inline .map
I didn't get this, could you please elaborate how should I define options? Options are fetched from REST api
@Puspendert you can do this. I keep the options separate from the actual select so it only ever renders if the options change. The select primitive is also separate from the formik hooks so it only ever renders when its value, error state etc... changes.
const SelectPrimitiveOptions = React.memo((props) => (
<React.Fragment>
{props.options?.map((option, key) => (
<option key={key} value={option.value}>
{option.label}
</option>
))}
</React.Fragment>
));
const SelectPrimitive = React.memo(
(props) => {
const [loading, setLoading] = React.useState(!props.options?.length);
React.useEffect(() => {
setLoading(!props.options?.length);
}, [props.options]);
return (
<div>
<select
name={props.name}
onBlur={props.onBlur}
onChange={props.onChange}
value={props.value}
>
<option value="">{loading ? "Loading..." : "Please select"}</option>
<SelectPrimitiveOptions options={props.options} />
</select>
</div>
);
}
);
const Select: React.FunctionComponent = (props) => {
const [field] = useField(props.name);
return <SelectPrimitive {...props} {...field} />;
};
<Select name="yourFieldName" options={yourOptions}/>
@mattsputnikdigital could you please remove the Typescript from it? I am not known to it.
@Puspendert I have cleaned up the code a bit
You should get Visual Studio Code and start using React with Typescript, it can help predict many different errors in your code and help you write much better apps.
Also don't do this...
<Select options={
[
{value: "1" , label: "Option 1"}
{value: "2" , label: "Option 2"}
]
}
/>
Instead define the array of options outside the form, either in the page container component, from a fetch / api, or import into the current page, and pass the options array to the component by props. This way the options don't change, the way I have shown above, the options would be rebuilt on every render.
Instead define the array of options outside the form .... pass the options array to the component by props
Did this. Declared the data outside the component and passed it as a prop to App component, still same issue.
https://codesandbox.io/s/formik-lag-typing-with-select-rwrdg?file=/src/index.js
@mattsputnikdigital
@Puspendert
>
{({ values, handleSubmit, handleChange }) => (
This means you are inside Formiks render function, each key press will render everything inside again as it calculates a new state, values.
You need to memorise all the components, by making a wrapper component, or as I have shown above, use the useField hook and build a memorised components of the <Form.Select> component.
@mattsputnikdigital the onChange handler causing the .memo component to re-render on each keypress. If it's onChange={handleCategoryChange} it works fine but with onChange={(e, data) => handleCategoryChange(data, setFieldValue)} I am facing the same issue. You can notice the re-render into the console.
@Puspendert you need to wrap them in a useCallback if you are looking to do something like this, which I used for an array of buttons where you can select one, then onClick I used can be converted into an onChange
const Component: React.FunctionComponent = ({ name, ...props }) => {
const { setFieldTouched, setFieldValue } = useFormikContext();
const [field] = useField(name);
const onBlur = useCallback(
(event) => {
setFieldTouched(name, true);
},
[name, setFieldTouched]
);
const onClick = useCallback(
(value) => {
setFieldValue(name, value);
setFieldTouched(name, true);
},
[name, setFieldTouched, setFieldValue]
);
return <ComponentMemorized onBlur={onBlur} onClick={onClick} value={field.value} {...props} />;
};
already using useCallback https://codesandbox.io/s/formik-lag-typing-with-select-rwrdg?file=/src/App.js:1300-1718
I just need a way to get setFieldValue and the selected value inside the handleCategoryChange so that I can use onChange={handleCategoryChange}.
@Puspendert try not to define functions as props as they are recreated every render causing component to render, the below is bad...
onChange={(e, data) =>
handleCategoryChange(data, setFieldValue)
}
@mattsputnikdigital thanku so much. useFormikContext did the wonders for me. I have achieved what I wanted to, but can you please check again if I am doing it the right way, on same link?
@Puspendert had a look, yeah I think you got it. The useFormikContext setFieldValue doesn't change between renders so it is safe to put inside a useCallback and won't cause renders. The useField hook does change every render if you were to access the helper functions, these currently don't work in useCallback.
I had a look at your code and I think you need to add the props.name to the useCallback. name won't change so its fine, but that's how React likes it.
const handleChange = useCallback((e, data) => {
setFieldValue(props.name, data.value);
}, [props.name]);
@mattsputnikdigital I found a more better way, where we don't even require useFormikContext. Pass a onChange prop and pass setFieldValue to it.
<SelectC
name="country"
options={options}
onChange={setFieldValue}
/>
Now change handleChange to
const handleChange = useCallback((e, data) => {
props.onChange.(props.name, data.value);
}, [props.name]);
https://codesandbox.io/s/formik-lag-typing-with-select-rwrdg?file=/src/App2.js