#mui/material <Checkbox> with Formik - reactjs

I wish to build a React (multi step) form using Formik and Mui. I cannot understand how to bind/control the mui checkbox element
import { Button, Checkbox, Typography } from "#mui/material";
import { Field, Form, Formik } from "formik";
import "./styles.css";
export default function App() {
var fruitValues = ["apple", "banana"];
function handleSubmit(values, actions) {
fruitValues = values.fruit;
console.debug(values.fruit);
}
return (
<div className="App">
<Formik initialValues={{ fruit: ["apple"] }} onSubmit={handleSubmit}>
<Form id="test2">
<Checkbox name="fruit" value="apple" label="Apple" />
<Checkbox name="fruit" value="banana" label="Banana" />
<Checkbox name="fruit" value="orange" label="Orange" />
<Button type="submit">Submit</Button>
</Form>
</Formik>
</div>
);
}
See https://codesandbox.io/s/thirsty-wing-91glso?file=/src/App.js:0-1380

I am working on this as well. Seems like something that should be covered in the docs but just isn't.
There is this example. It covers doing something like this ...
<Form>
<Field
type="email"
name="email"
component={TextField}
color={"error"}
/>
But there isn't any explanation of where this comes from or what is happening here.

My reaction is a little late but I faced the same issue and made it work as follow.
import React from 'react'
import { Field, Form, Formik } from 'formik'
import Checkbox from '#mui/material/Checkbox'
import FormControlLabel from '#mui/material/FormControlLabel'
export default function Example() {
return (
<Formik>
<Form>
<Field name="terms_of_condition">
{({ field }) => (
<FormControlLabel
onChange={field.onChange}
control={<Checkbox name="terms_of_condition" />}
label="Terms of conditions"
sx={sx}
value="on"
/>
)}
</Field>
</Form>
</Formik>
)
}

Related

Programmatically control the properties of a component

Suppose I have a component that a parameter from another component called jobData. The component could be a value or be undefined. If it has a value, I want to assign it to a textField property called defaultValue. If jobData is undefined, I want to assign it to something else. I tried the below in my code, but it didn't work. Is there any/another way to do this?
import React from 'react'
import {Dialog, TextField} from '#mui/material'
export default function myFunction({jobData}) {
return(
<div>
<form>
<TextField
autoFocus
margin="dense"
width="100%"
id="my_id"
label="My ID"
type="text"
name="myID"
defaultValue={if({jobData.length} > 0){
{jobData[0]['id']}
} else { {jobData.length.toString()}
}
/>
</form>
</div>
)
Try using encapsulation
import React from 'react'
import {Dialog, TextField} from '#mui/material'
export default function myFunction({jobData}) {
function isJobDataUndefined(){
if(jobData.length > 0){
return jobdata[0]['id']
}
return jobData.length.toString()
}
return(
<div>
<form>
<TextField
autoFocus
margin="dense"
width="100%"
id="my_id"
label="My ID"
type="text"
name="myID"
defaultValue={isJobDataUndefined()}
/>
</form>
</div>
)

Material-UI Multiline TextField scrolls horizontally

I'm trying to use Material UI to implement a multiline TextField. Instead of being multi-line the input field is scrolling horizontally. I have replicated the issue in Code Sandbox (link below). It is the Ad Description field in FormItemDetails.js that I want to be multi-line. I'm grateful for any help! Thanks
Code Sandbox
import React, { useState, useEffect } from 'react';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import TextField from 'material-ui/TextField';
import RaisedButton from 'material-ui/RaisedButton';
export default function FormItemDetails(props) {
const { values, handleChange } = props;
const [ error, setError ] = useState(null)
console.log(values)
const errorDiv = error
? <div className="error">
<i class="material-icons error-icon">error_outline</i>
{error}
</div>
: '';
useEffect(() => setError(null), []);
function cont(e) {
e.preventDefault();
const requiredFields = ['title', 'description', 'price']
for (const key of requiredFields) {
if (!values[key]) {
setError(`A ${key} is required`)
return
}
}
props.nextStep();
};
function back() {
props.prevStep();
}
return (
<MuiThemeProvider>
<>
<h2>Enter the advertisement details</h2>
{errorDiv}
<TextField
required={true}
fullWidth={true}
hintText="Enter Your Ad Title"
floatingLabelText="Ad Title"
onChange={handleChange('title')}
defaultValue={values.title}
/>
<br />
<TextField
required={true}
fullWidth={true}
multiline
hintText="Enter Your Ad Description"
floatingLabelText="Ad Description"
onChange={handleChange('description')}
defaultValue={values.description}
/>
<br />
<TextField
required
fullWidth
hintText="Enter Your Asking Price"
floatingLabelText="Price"
onChange={handleChange('price')}
defaultValue={values.price}
/>
<br />
<RaisedButton
label="Continue"
primary={true}
style={styles.button}
onClick={cont}
/>
<RaisedButton
label="Back"
primary={false}
style={styles.button}
onClick={back}
/>
</>
</MuiThemeProvider>
)
}
Update the material-ui library used in the project. Actually, you're using material-ui/TextField and have to change to #material-ui/core/TextField for achieved expected behaviour in the textField. Check this demo codesandbox.io/s/t8dkp
Important: Check the last library version as well.

How to use custom radio component with react-final-form?

I am trying to use a custom Radio component with React-final-form but it is not acting as a radio button but as a checkbox, ie, all the buttons are open for selection.
The 3rd party Radio button has the following schema:
checked boolean
Whether or not radio is checked
onChange () => void
Called when the user attempts to change the checked state
name string
The input name, used to reference the element in JavaScript
I created a custom Component for using the Radio Component:
const CustomRadio = (props: any) => (
<Radio
{...props.input}
{...props.rest}
name={props.name}
onChange={() => props.input.onChange()}
/>
)
and I am using it as follows:
<Field name="food"
component={CustomRadio}
value="1"
/>
<Field name="food"
component={CustomRadio}
value="2"
/>
Being very new to RFF and new to React, I may be doing something very wrong, hence any help will be appreciated.
Basically, I want to use RFF with my 3rd party components. Though I have been successful to use my Input component with RFF as expected, Radio Button is the one creating problems.
Here is the correct implementation for Radio with react-final-form:-
https://codesandbox.io/s/vibrant-easley-5n1ek?file=/index.js
/* eslint-disable jsx-a11y/accessible-emoji */
import React from "react";
import { render } from "react-dom";
import Styles from "./Styles";
import { Form, Field } from "react-final-form";
import RadioGroup from "#material-ui/core/RadioGroup";
import FormControlLabel from "#material-ui/core/FormControlLabel";
import FormControl from "#material-ui/core/FormControl";
import Radio from "#material-ui/core/Radio";
import FormLabel from "#material-ui/core/FormLabel";
const RadioWrapper = (props) => {
const {
input: { checked, value, name, onChange, ...restInput },
} = props;
return (
<Radio
name={name}
inputProps={restInput}
onChange={onChange}
checked={checked}
value={value}
/>
);
};
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const onSubmit = async (values) => {
await sleep(300);
window.alert(JSON.stringify(values, 0, 2));
};
const App = () => {
return (
<Styles>
<h1>React Final Form - Simple Example</h1>
<a
href="https://final-form.org/react"
target="_blank"
rel="noopener noreferrer"
>
Read Docs
</a>
<Form
onSubmit={onSubmit}
initialValues={{
employed: false,
all_sub_tenants: "true"
}}
render={({ handleSubmit, form, submitting, pristine, values }) => (
<form onSubmit={handleSubmit}>
<FormControl component="fieldset">
<FormLabel component="legend">
Select Tenants
</FormLabel>
<RadioGroup aria-label="allSubTenants" name="allSubTenants">
<FormControlLabel
value="true"
control={
<Field
name="all_sub_tenants"
component={RadioWrapper}
type="radio"
value={"true"}
/>
}
label="All Sub-Tenants"
/>
<FormControlLabel
value="false"
control={
<Field
name="all_sub_tenants"
component={RadioWrapper}
type="radio"
value={"false"}
/>
}
label="Select Sub-Tenants"
/>
</RadioGroup>
</FormControl>
<div>
<label>Notes</label>
<Field name="notes" component="textarea" placeholder="Notes" />
</div>
<div className="buttons">
<button type="submit" disabled={submitting || pristine}>
Submit
</button>
<button
type="button"
onClick={form.reset}
disabled={submitting || pristine}
>
Reset
</button>
</div>
<pre>{JSON.stringify(values, 0, 2)}</pre>
</form>
)}
/>
</Styles>
);
};
render(<App />, document.getElementById("root"));

Use react-input-mask with antd in react-final-form

I would like to use react-input-mask with Ant Design Input in react-final-form. In order to use antd with react-final-form I also had to install redux-form-antd. So the file looks like this:
import React from "react";
import ReactDOM from "react-dom";
import { Button } from "antd";
import { Form, Field } from "react-final-form";
import InputMask from "react-input-mask";
import { TextField } from "redux-form-antd";
import "antd/dist/antd.css";
const onSubmit = async values => {
window.alert(JSON.stringify(values, 0, 2));
};
const Input = props => <InputMask {...props} />;
function App() {
return (
<Form
onSubmit={onSubmit}
render={({ handleSubmit, values }) => (
<form onSubmit={handleSubmit}>
<Field
name="mask"
parse={value =>
value
.replace(/\)/g, "")
.replace(/\(/g, "")
.replace(/-/g, "")
.replace(/ /g, "")
}
render={({ input, meta }) => (
<div>
<label>mask phone</label>
<Input mask="+7 (999) 999-99-99" {...input} />
{meta.touched && meta.error && <span>{meta.error}</span>}
</div>
)}
/>
<Field
name="antd"
component={TextField}
label="antd phone"
placeholder="Phone"
/>
<Button className="submit-button" type="primary">
Send
</Button>
<pre>{JSON.stringify(values, 0, 2)}</pre>
</form>
)}
/>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Here is a codesandbox example.
I could only get to work a regular input with an InputMask (input 1) or an antd input without a mask (input 2).
How can I add an InputMask to antd input?
I've managed to use react-input-mask with antd and react-final-form without any external libraries.
Here is my component:
import React from "react";
import InputMask from "react-input-mask";
import { Input } from "antd";
import FormItem from "antd/lib/form/FormItem";
const MaskInput = props => {
const { disabled, mask, label, meta, required } = props;
return (
<FormItem
label={label}
validateStatus={
meta.touched ? (meta.error ? "error" : "success") : undefined
}
help={meta.touched ? (meta.error ? meta.error : undefined) : undefined}
hasFeedback={meta.touched ? true : false}
required={required}
>
<InputMask
mask={mask}
disabled={disabled}
autoComplete="off"
{...props.input}
>
<Input />
</InputMask>
</FormItem>
);
};
export default MaskInput;
Then it is passed to the component prop of the Field:
<Field
name="phone"
label="Phone"
component={MaskInput}
mask="+7 (999) 999-99-99"
required
/>
Here is the link to the codesandbox example.
I've never used either of those libraries, but you might want to check out using format-string-by-pattern with react-final-form's built-in parsing and formatting functionality to achieve a similar thing.
I bet you could throw redux-form-antd components into here pretty easily...

how to create re-usable field component in redux-form?

I have 2-3 fields that is reused is other forms of my application. So i wanted to create those fields as component so that I can resuse is my other forms. but redux-form is complaining that
Error: Field must be inside a component decorated with reduxForm()
Any ideas how can I achieve it? BTW, I am using material-ui
EDIT: providing a better e.g. consider the material-ui toolbar
http://www.material-ui.com/#/components/toolbar
My toolbar consists of a selectField, textField, Toggle button, which may couple of forms. In my app, I want to keep this toolbar in all the forms where I create objects in my application, so I want to include this toolbar in all the forms. After the below answer I tried something dirty like below.
class BaseBar extends React.Component { // eslint-disable-line react/prefer-stateless-function
constructor(props) {
super(props);
this.state = {
value: 3,
isGlueOpen: false,
};
}
handleChange = (event, index, value) => {
this.setState({value: value});
}
render() {
return (
<div>
<Toolbar>
<ToolbarGroup firstChild={true}>
<DropDownMenu value={this.state.value} onChange={this.handleChange}>
<MenuItem value={1} primaryText="All Broadcasts" />
<MenuItem value={2} primaryText="All Voice" />
<MenuItem value={3} primaryText="All Text" />
<MenuItem value={4} primaryText="Complete Voice" />
<MenuItem value={5} primaryText="Complete Text" />
<MenuItem value={6} primaryText="Active Voice" />
<MenuItem value={7} primaryText="Active Text" />
</DropDownMenu>
</ToolbarGroup>
<ToolbarSeparator />
<ToolbarGroup>
<ToggleButton onChange={this.props.glueToggle}/>
</ToolbarGroup>
</Toolbar>
</div>
);
}
}
export default BaseBar;
and including the form like below
<form onSubmit={handleSubmit}>
<div>
<Field
name="basebar"
component={BaseBar}
label="project"
/>
</div>
<div>
<Field
name="subject"
component={renderTextField}
label="subject"
/>
</div>
</form>
But on submit, I am getting the values for subject field but not the basebar values, any suggestions or approach is greatly appreciated.
The error is that you're trying to use the Field outside the context of a connected Redux Form.
Perhaps you could create your shareable component without the <Field> component itself, and then use that component in as many forms as you need, wrapping it with Field. See example usage in the documentation:
http://redux-form.com/6.8.0/docs/api/Field.md/#usage
import MyCustomInput from './MyCustomInput'
...
<Field name="myField" component={MyCustomInput}/>
Where MyCustomInput is your custom, common component.
Or if you have custom logic to map Field props to your components, then use a function which you can then export and reuse in as many forms as you need.
// this is your custom component now
export const renderMyCustomField = (field) => (
<div className="input-row">
<input {...field.input} type="text"/>
{field.meta.touched && field.meta.error &&
<span className="error">{field.meta.error}</span>}
</div>
)
...
//which you can then import in your forms
import { renderMyCustomField } from '...'
// inside your render() method
<Field name="myField" component={renderMyCustomField}/>
Welcome to reusable redux form field component :)
Here my elegant soloution:
import React from 'react';
import { string, object } from 'prop-types';
import { Field } from 'redux-form/immutable';
const Input = ({
input,
meta: { touched, error },
...rest
}) => (
<div>
<input
{...input}
{...rest}
/>
{touched && error && <span>{error}</span>}
</div>
);
Input.propTypes = {
input: object.isRequired,
meta: object.isRequired,
type: string.isRequired
};
Input.defaultProps = {
input: null,
meta: null,
type: 'text'
};
export default props => <Field {...props} component={Input} />;
How to use?
import Input from './Input';
<form>
...
<Input
autoComplete="off"
name="email"
placeholder="Email"
type="email"
/>
...
</form>

Resources