Tim Ta
Tim Ta

Reputation: 61

How to create forms with MUI (material ui)

Hiho,

I want to use mui in my current react project. Is there a different/better way to create forms than the following example?:

    const [companyName, setCompanyName] = useState<string>("");
    const [companyNameError, setCompanyNameError] = useState<boolean>(false);
    const changeName = (event: React.ChangeEvent<HTMLInputElement>) => {
        if(event.target.value === "") {
            setCompanyNameError(true);
        } else {
            setCompanyNameError(false);
        }

        event.preventDefault();
        setCompanyName(event.target.value);
    }

    const anyInputFieldEmpty = () => {
        var result = false;
   
        if(companyName === "") {
            setCompanyNameError(true);
            result = true;
        } else {
            setCompanyNameError(false);
        }
        
        // add the same check for all other fields. My real code has multiple input fields

        return result;
    }

    const resetFields = () => {
        setCompanyName("");
    }

    return (
        <div>
            <TextField
                required
                fullWidth
                label="Company Name"
                margin="dense"
                name="companyName"
                value={companyName}
                onChange={changeName}
                helperText={companyNameError ? "Company name is not allowed to be empty!" : ""}
                error={companyNameError}
            />

            <Button
                sx={{ alignSelf: 'center', }}
                variant="contained"
                onClick={() => {
                    if(!anyInputFieldEmpty()) {
                        onSubmitClick(); // some function from somewhere else, which triggers logic
                        resetFields(); // This form is in a popover. The values should be resetted before the user open it again.
                    }
                }}
            >
                Create
            </Button>
        </div>);

It feels wrong to do the validation this way if I use multiple textfields (up to 9). Its a lot of boilerplate code and if I add further validation rules (for example a minimum character count) it goes crazy.

Is this the right way to achive my goal?

T

Upvotes: 4

Views: 12270

Answers (4)

manojadams
manojadams

Reputation: 2430

You can use json to generate forms directly. Few examples:

You can use standard material design or ant UI components with above libraries.
You can also add your own components.

If you have custom requirements that cannot be achieved using above, then you can use

  • react hook form
    • good performance but grouping or layouting not supported out of the box.
  • react formik
    • a popular library for react forms but grouping of layouting not supported out of the box.

Upvotes: 0

Petr Tripolsky
Petr Tripolsky

Reputation: 1613

I think you should check the Formik for hook-based validation and jsonforms, react-declarative from json-schema based view creation

Less code solutions is better on production, but for a learning reason it better to write real code based on hooks, contexts or redux reducers

import { useState } from "react";

import { One, FieldType } from "react-declarative";

const fields = [
  {
    type: FieldType.Text,
    title: "First name",
    defaultValue: "Peter",
    name: "firstName"
  },
  {
    type: FieldType.Text,
    title: "Age",
    isInvalid: ({ age }) => {
      if (age.length === 0) {
        return "Please type your age";
      } else if (parseInt(age) === 0) {
        return "Age must be greater than zero";
      }
    },
    inputFormatterTemplate: "000",
    inputFormatterAllowed: /^[0-9]/,
    name: "age"
  }
];

export const App = () => {
  const [data, setData] = useState(null);

  const handleChange = (data) => setData(data);

  return (
    <>
      <One fields={fields} onChange={handleChange} />
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </>
  );
};

export default App;

screenshot

An example project could be found on this codesandbox

Upvotes: 3

EvoluWil
EvoluWil

Reputation: 111

MUI does not have a native form validator i recommend using react-hook-form + yup it's pretty simple and has a lot of tutorials

https://react-hook-form.com/get-started

EXEMPLE

TextFieldComponent

import { OutlinedTextFieldProps } from '@mui/material';
import React from 'react';
import { Control, useController } from 'react-hook-form';
import { InputContainer, TextFieldStyled } from './text-field.styles';

export interface TextFieldProps extends OutlinedTextFieldProps {
  control: Control;
  helperText: string;
  name: string;
  defaultValue?: string;
  error?: boolean;
}
export const TextField: React.FC<TextFieldProps> = ({
  control,
  helperText,
  name,
  defaultValue,
  error,
  ...rest
}) => {
  const { field } = useController({
    name,
    control,
    defaultValue: defaultValue || ''
  });
  return (
    <InputContainer>
      <TextFieldStyled
        helperText={helperText}
        name={field.name}
        value={field.value}
        onChange={field.onChange}
        fullWidth
        error={error}
        {...rest}
      />
    </InputContainer>
  );
};

Styles

import { TextField } from '@mui/material';
import { styled } from '@mui/material/styles';

export const TextFieldStyled = styled(TextField)`
  .MuiOutlinedInput-root {
    background: ${({ theme }) => theme.palette.background.paper};
  }
  .MuiInputLabel-root {
    color: ${({ theme }) => theme.palette.text.primary};
  }
`;

export const InputContainer = styled('div')`
  display: grid;
  grid-template-columns: 1fr;
  align-items: center;
  justify-content: center;
  width: 100%;
`;

export const InputIconStyled = styled('i')`
  text-align: center;
`;

Usage

// Validator
const validator = yup.object().shape({
  email: yup
    .string()
    .required(translate('contact.email.modal.email.required'))
    .email(translate('contact.email.modal.email.invalid')),
});


// HookForm
const {
    control,
    handleSubmit,
    formState: { errors }
  } = useForm({
    resolver: yupResolver(validator)
});

// Compoenent
<TextField
          label="label"
          fullWidth
          placeholder="placeholder
          size="small"
          control={control}
          helperText={errors?.name?.message}
          error={!!errors?.name}
          name={'name'}
          variant={'outlined'}
        />

Upvotes: 1

Ryan Zeelie
Ryan Zeelie

Reputation: 1510

As others have mentioned. Formik and Yup works great for validation. Formik also provides a way to easily disable your submit buttons. Here is a codesandbox : https://codesandbox.io/s/long-butterfly-seogsw?file=/src/App.js

Upvotes: 2

Related Questions