material ui warning in using `textField` component with `select` - reactjs

Using TextField as the input param of Select gives the following warning:
Material-UI: children must be passed when using the TextField component with select.
<Select
multiple
value="value"
fullWidth
input={
<TextField name="name" id="id" variant="outlined" label="test"/>
}
>
<MenuItem>1</MenuItem>
<MenuItem>2</MenuItem>
</Select>
What is the proper implementation of the same?

The proper way is to render a TextField as parent component, and providing it select prop
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import MenuItem from '#material-ui/core/MenuItem';
import TextField from '#material-ui/core/TextField';
const useStyles = makeStyles(theme => ({
root: {
width: '100px'
},
}));
export default function SelectTextField() {
const classes = useStyles();
return (
<TextField
classes={{root:classes.root}}
select
name="name"
id="id"
variant="outlined"
label="test"
SelectProps={{
multiple: true,
value: []
}}
>
<MenuItem>1</MenuItem>
<MenuItem>2</MenuItem>
</TextField>
);
}
Code Sandbox

Related

Problem with the Controller using react-hook-form

Here is the code that I've used for the controller:
import React from 'react';
import { useFormContext, Controller } from 'react-hook-form';
import { TextField, Grid } from '#material-ui/core';
function FormInput({ name, label, required }) {
const { control } = useFormContext();
return (
<Grid item xs={12} sm={6}>
<Controller
as={TextField}
name={name}
control={control}
label={label}
fullWidth
required={required}
/>
</Grid>
);
}
export default FormInput;
Here is where I call FormInput:
import React from 'react'
import { InputLabel, Select, MenuItem, Button, Grid, Typography } from '#material-ui/core'
import { useForm, FormProvider } from 'react-hook-form'
import FormInput from './CustomTextField'
const AdressForm = () => {
const methods = useForm()
return (
<>
<Typography variant="h6" gutterBottom>Shippng Adress</Typography>
<FormProvider {...methods}>
<form onSubmit=''>
<Grid container spacing={3}>
<FormInput required name="firstName" label="First name" />
</Grid>
</form>
</FormProvider>
</>
)
}
export default AdressForm
And I get this error:
"TypeError: props.render is not a function"
I'm trying to use the react-hook-form to make the form in the adressForm but I have a problem with the controller.
According to the doc, the as props have been removed in react-hook-form's v7 in favor of render props.
Documentation: https://react-hook-form.com/api/usecontroller/controller

How to reduce height and size of Material-UI<Select> input field in Material UI & formik?

I am making a web project using React, Material, Formik, formik-material-ui.
This is the input screen I am getting of my form. The select input height is bigger than the textfield inputs.
The InputField component is as follows:
import { Field } from "formik";
import { TextField } from "formik-material-ui";
const TextFieldStyle = {
padding: 7,
fontSize: "0.75rem",
};
export default (props: any) => {
return (
<Field
component={TextField}
inputProps={{
style: TextFieldStyle,
}}
size="small"
margin="none"
variant="outlined"
{...props} // add props at the key to override any user defined similar props
>
{props.children}
</Field>
);
};
The Select Field component is as follows:
import { Field } from "formik";
import { TextField } from "formik-material-ui";
const SelectFieldStyle = {
padding: 7,
fontSize: "0.75rem",
};
export default (props: any) => {
return (
<Field
component={TextField}
inputProps={{
style: SelectFieldStyle,
}}
type="text"
select={true}
align="left"
size="small"
fullWidth
margin="none"
variant="outlined"
{...props} // add props at the key to override any user defined similar props
>
<MenuItem value={1}>A</MenuItem>
<MenuItem value={2}>B</MenuItem>
<MenuItem value={3}>C</MenuItem>
</Field>
);
};
Changing the style in Select component is not bringing any visual change.
How can I bring the select component to the same height as input field?
Try using SelectProps on your Select field thusly:
import { Field } from "formik";
import { TextField } from "formik-material-ui";
const SelectFieldStyle = {
padding: 7,
fontSize: "0.75rem",
};
export default (props: any) => {
return (
<Field
component={TextField}
SelectProps={{
style: SelectFieldStyle,
}}
type="text"
select={true}
align="left"
size="small"
fullWidth
margin="none"
variant="outlined"
{...props} // add props at the key to override any user defined similar props
>
<MenuItem value={1}>A</MenuItem>
<MenuItem value={2}>B</MenuItem>
<MenuItem value={3}>C</MenuItem>
</Field>
);
};
Here's the link to the API: https://material-ui.com/api/text-field/

react-hook-form Controller issues

Hi im trying to do one form with react-hook-form and material-ui. I don't want to write Controller every time for all TextFields. Because of that i declare it in another file and call it in my form but its not working i didn't understand why, because in some videos that i watched is working. What is the problem and how i can fix it ?
Form Field
import React from 'react'
import { TextField, Grid } from '#material-ui/core'
import { useForm, Controller, useFormContext } from 'react-hook-form'
const FormField = ({name, label}) => {
const { control } = useForm()
return (
<Grid item xs={12} sm={6} >
<Controller
render = {({field}) =>
<TextField
{...field}
label={label} required
/>}
name={name}
control = {control}
defaultValue=""
/>
</Grid>
)
}
export default FormField
Adress Form
import React from 'react'
import { InputLabel, Select, MenuItem, Button, Grid, Typography, TextField } from '#material-ui/core'
import { useForm, FormProvider, Controller } from 'react-hook-form'
import FormField from './FormField'
import { Link } from 'react-router-dom'
const AdressForm = ({next}) => {
const {handleSubmit, control} = useForm()
return (
<>
<Typography variant="h6" gutterBottom>Shipping Address </Typography>
<form onSubmit={handleSubmit((data) => console.log(data) )}>
<Grid container spacing={3}>
<FormField name='firstName' label='First Name' required='required'/>
<FormField name='lastName' label='Last Name' />
<FormField name='email' label='Email' />
<FormField name='phoneNumber' label='Phone Number' />
</Grid>
<br/>
<div style={{ display: 'flex', justifyContent: 'space-between'}}>
<Button component={Link} to="/cart" variant="outlined">Back to Cart</Button>
<Button type="submit" variant="contained" color="primary">Next</Button>
</div>
</form>
</>
)
}
export default AdressForm
You must use one useForm hook for each form, in your code, you call useForm in every Field components, creating multiple independent form states, which leads to unexpected result.
What you need to do is to call useForm in the parent element and pass the dependencies (register, formState, error...) down the child components, so your form can have one unified state. If you have a deeply nested components, you can use useFormContext to pass the form context to the nested children easily:
import React from "react";
import { useForm, FormProvider, useFormContext } from "react-hook-form";
export default function App() {
const methods = useForm();
const onSubmit = data => console.log(data);
return (
<FormProvider {...methods} > // pass all methods into the context
<form onSubmit={methods.handleSubmit(onSubmit)}>
<NestedInput />
<input type="submit" />
</form>
</FormProvider>
);
}
function NestedInput() {
const { register } = useFormContext(); // retrieve all hook methods
return <input {...register("test")} />;
}

Material-UI: Make IconButton only visible on hover?

I am making a custom input component with MUI InputBase, and I want to have a "Clear" button endAdornment that only appears when you hover over the input:
<InputBase
inputComponent={getCustomInputComponent()}
onClick={onClick}
...
endAdornment={
<IconButton
size='small'
onClick={handleClear}>
<IconClear fontSize='small'/>
</IconButton>
}
/>
Similar to how their new "Autocomplete" component works: https://material-ui.com/components/autocomplete/
I've looked at the source code of Autocomplete but I can't get it working in my component, any suggestions?
Below is an example that is roughly equivalent to what is being done in Autocomplete. The gist of the approach is to make the icon hidden by default, then flip the visibility to visible on hover of the input (&:hover $clearIndicatorDirty) and when the input is focused (& .Mui-focused), but only if there is currently text in the input (clearIndicatorDirty is only applied when value.length > 0).
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import TextField from "#material-ui/core/TextField";
import IconButton from "#material-ui/core/IconButton";
import ClearIcon from "#material-ui/icons/Clear";
import clsx from "clsx";
const useStyles = makeStyles(theme => ({
root: {
"&:hover $clearIndicatorDirty, & .Mui-focused $clearIndicatorDirty": {
visibility: "visible"
}
},
clearIndicatorDirty: {},
clearIndicator: {
visibility: "hidden"
}
}));
export default function CustomizedInputBase() {
const classes = useStyles();
const [value, setValue] = React.useState("");
return (
<TextField
variant="outlined"
className={classes.root}
value={value}
onChange={e => setValue(e.target.value)}
InputProps={{
endAdornment: (
<IconButton
className={clsx(classes.clearIndicator, {
[classes.clearIndicatorDirty]: value.length > 0
})}
size="small"
onClick={() => {
setValue("");
}}
>
<ClearIcon fontSize="small" />
</IconButton>
)
}}
/>
);
}
Related documentation:
https://cssinjs.org/jss-plugin-nested?v=v10.0.0#use-rulename-to-reference-a-local-rule-within-the-same-style-sheet

Field values don't change when debugging with the redux dev tools plugin

I'm using redux-form to create an authentication form.
import React from 'react';
import Button from '#material-ui/core/Button';
import PropTypes from 'prop-types';
import { Field, reduxForm } from 'redux-form';
import Grid from '#material-ui/core/Grid';
import TextField from '../../components/TextFieldWrapper';
const Authentication = ({
classes,
submitting,
handleSubmit,
}) => (
<div>
<form onSubmit={handleSubmit}>
<Grid
container
direction="column"
alignContent="center"
spacing={24}
>
<Grid item xs={10} md={10} lg={10}>
<Field
name="Email"
type="email"
component={TextField}
label="Email"
/>
</Grid>
<Grid item xs={6}>
<Field
name="Password"
type="password"
component={TextField}
label="Password"
/>
</Grid>
<Grid item xs={6}>
<Button
variant="contained"
color="primary"
type="submit"
disabled={submitting
>
Login
</Button>
</Grid>
</Grid>
</form>
</PaperWrapper>
</div>
);
Authentication.propTypes = {
submitting: PropTypes.bool.isRequired,
handleSubmit: PropTypes.func.isRequired,
};
const AuthenticationForm = reduxForm({
form: 'AuthenticationForm',
})(Authentication);
export default AuthenticationFom;
TextFieldWrapper.jsx:
import React from 'react';
import PropTypes from 'prop-types';
import TextField from '#material-ui/core/TextField';
const TextFieldWrapper = ({
label,
type,
}) => (
<TextField
label={label}
margin="normal"
type={type}
variant="outlined"
/>
);
TextFieldWrapper.propTypes = {
label: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
};
export default TextFieldWrapper;
When I debug the application using redux-devtoolsI find that the Field's value don't change whatever I put in the TextFields.
The state is like this:
Should I add value and onChangefunction to each Field. In the documentation of Redux-form they don't add value or onChange in their FieldComponent Material-Ui Example
When using custom components like that you need to forward to the material ui TextField the input property coming from the TextFieldWrapper.
The doc of Redux Form show it in the part {...input} of renderTextField
I found the solution, I update the TextFieldWrapperComponent:
import React from 'react';
import PropTypes from 'prop-types';
import TextField from '#material-ui/core/TextField';
const TextFieldWrapper = ({
label,
type,
input,
}) => (
<TextField
label={label}
margin="normal"
type={type}
value={input.value}
onChange={input.onChange}
variant="outlined"
/>
);
TextFieldWrapper.propTypes = {
label: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
input: PropTypes.shape({}).isRequired,
};
export default TextFieldWrapper;
So I add the input property to my component in order to get props sent by redux-form.

Resources