react-final-form with multiple select - reactjs

I'm trying to build a form with a multiple value Select component by Material-UI using react-final-form. Somehow with single Select, I can get the value but with multiple, it doesn't. Somehow it seems like react-final-form is holding its own value internally.
Here's a the guiding link from Material-UI for building multiple Select:
https://codesandbox.io/s/sr6pf
I tried to replicate the very first example (without using react hook) in my form and I still miss something ?
https://codesandbox.io/embed/react-final-form-material-ui-example-jfmoe
What should I add to my Component to make this work ?
Thanks,

For some reasons I've managed to figure out the solution for my own question. The proper answer is to create a custom MultiSelect component instead of reusing the one from final-form-material-ui.
Notes: I've tried to use <Select /> from final-form-material-ui but adding multiple prop to the component will not be passed to , this is weird.
So, my custom component would look like this, almost similar to the one from their github with multiple prop added.
import React from 'react';
import FormControl from '#material-ui/core/FormControl';
import FormHelperText from '#material-ui/core/FormHelperText';
import InputLabel from '#material-ui/core/InputLabel';
import Select from '#material-ui/core/Select';
function SelectMulti({
input: { name, value, onChange, ...restInput },
meta,
label,
formControlProps,
...rest
}) {
const showError =
((meta.submitError && !meta.dirtySinceLastSubmit) || meta.error) &&
meta.touched;
return (
<FormControl {...formControlProps} error={showError}>
<InputLabel htmlFor={name} shrink>
{label}
</InputLabel>
<Select
{...rest}
multiple
name={name}
value={value}
onChange={onChange}
inputProps={restInput}
/>
{showError && (
<FormHelperText>{meta.error || meta.submitError}</FormHelperText>
)}
</FormControl>
);
}
SelectMulti.propTypes = {};
export default SelectMulti;
Hope this help someone in the future

I was able to solve this with by setting the fomat as such
<Field
name="concepts"
component={Select}
displayEmpty={trie}
multiple={true}
value={[]}
format={value => value || []}
/>
As per https://github.com/erikras/redux-form-material-ui/issues/212#issuecomment-358376925

Related

Is it possible for MUI autocomplete to be linked with two field values?

Hi I am trying to make a custom search input where user could click on it and be shown the possible filter categories they could select. Also they can optionally type a title keyword to then search with the title and the categories filter selected. A very good example of what I am trying to do is the telegram bugs website search bar. https://bugs.telegram.org/
So i was thinking of using mui autocomplete where when user selects the category it will populate some field state called categories containing an array of selected category. Then when the user types a custom text it will then be stored in keyword field. Is it even possible to do so? If not is there a better approach to do this.
You can achieve this with the help of multiple, freeSolo and filterSelectedOptions props.
I have made a working example similar to your case, You can find it here
import { useState } from 'react';
import Autocomplete from '#mui/material/Autocomplete';
import TextField from '#mui/material/TextField';
const options = [{ value:'1', label:'One'}, { value:'2', label:'Two'}, { value:'3', label:'Three'},];
export default function App() {
const [selectedOptions, setSelectedOptions] = useState([]);
return (
<Autocomplete
multiple
freeSolo
filterSelectedOptions
options={options}
onChange={(_, value) => {
setSelectedOptions(value);
}}
renderInput={(params) => (
<TextField
{...params}
label="Multiple values"
placeholder="Add value"
/>
)}
/>
);
}

React Hooks - Input loses focus when 1 character is typed in

I'm playing with React Hooks - rewriting a form to use hook concepts. Everything works as expected except that once I type any 1 character into the input, the input loses focus.
I guess there is a problem that the outside of the component doesn't know about the internal changes in the component, but how do I resolve this issue?
Here is the useForm Hook:
import React, { useState } from "react";
export default function useForm(defaultState, label) {
const [state, setState] = useState(defaultState);
const FormComponent = () => (
<form>
<label htmlFor={label}>
{label}
<input
type="text"
id={label}
value={state}
placeholder={label}
onChange={e => setState(e.target.value)}
/>
</label>
</form>
);
return [state, FormComponent, setState];
}
Here is the component that uses the Hook:
function App() {
const [formValue, Form, setFormValue] = useForm("San Francisco, CA", "Location");
return (
<Fragment>
<h1>{formValue}</h1>
<Form />
</Fragment>
);
}
While answer by Kais will solve the symptoms, it will leave the cause unaddressed. It will also fail if there are multiple inputs - which one should autofocus itself on rerender then?
The issue happens when you define a component (FormComponent) inside the scope of another function which is called each render of your App component. This gives you a completely new FormComponent each time your App component is rerendered and calls useState. That new component is then, well, without focus.
Personally I would feel agains returning components from a hook. I would instead define a FormComponent component, and only return state from useForm state.
But, a working example closest to your original code could be:
// useForm.js
import React, { useState } from "react";
// Define the FormComponent outside of your useForm hook
const FormComponent = ({ setState, state, label }) => (
<form>
<label htmlFor={label}>
{label}
<input
type="text"
id={label}
value={state}
placeholder={label}
onChange={e => setState(e.target.value)}
/>
</label>
</form>
);
export default function useForm(defaultState, label) {
const [state, setState] = useState(defaultState);
return [
state,
<FormComponent state={state} setState={setState} label={label} />,
setState
];
}
// App.js
import useForm from "./useForm";
export default function App() {
const [formValue, Form] = useForm("San Francisco, CA", "Location");
return (
<>
<h1>{formValue}</h1>
{Form}
</>
);
}
Here's a sandbox
When you enter any text in input box. Parent Component is also re-rendering. So you need to make focus on input manually.
For this, use autoFocus in input tag
<input
type="text"
id={label}
value={state}
placeholder={label}
onChange={e => setState(e.target.value)}
autoFocus
/>
The above answers didn't work for me. The solution that worked for me was much simpler and, for that reason, less obvious.
The Problem
Essentially, the value that I was changing with the input was also being used for each key in a list of inputs.
Hence, when I updated the value the key would change and React would detect that it's different relative to the last key and create a new input in its place. As a new input it wouldn't focus on itself.
However, by using autoFocus it would automatically focus on the newly created input. But the issue wasn't fixed as it was obvious that the input was continually going through a cycle of un-focus and focus.
Here's an article demonstrating the problem.
The Fix
Update the key to an unchangeable value so React would have a stable reference to the list items. In my case I just updated it to the index. This is not ideal (React docs recommend using a stable ID), but in my situation it was okay because the order of the items won't change.
The first solution actually worked for me , initially the functional component which contained the text field was part of the main functional component , i was facing the same issue but when i extracted the text-field component into another page and imported it it worked fine
This was my component
<Paper >
<div>
<div style={{padding:'10px' ,display:'flex'}} >
<inputstyle={{marginRight:'5px'}} value={val} onChange={(e)=>{setVal(e.target.value)}} />
</div>
</div>
</Paper>

Using onPaste with react-select

I'm trying to get onPaste working with react-select. It seems like, that it is not possible to make use of the event.
Basically I'm just doing this within the <Select/>: onPaste={(e) => this.doPasteMagic(e)}
But it is never fired. Am I missing something or is there another way to distinguish between typing and pasting?
I've seen a few suggestions about using onChange, but this seems dirty to me as well.
I was fiddling a lot with that. I find it rather surprising that this is not one of the main features of react-select. Anyway, I've found a workaround for this:
<div style={{height: '100%', width: '100%' }} onPaste={(e) => console.log(e)}>
<Select .../>
</div>
This seems to do the trick and triggers the right event at the right time.
You can create a custom input field and attach an onPaste event handler to it.
import { components } from 'react-select'
const CustomInput = props => (
<components.Input
{...props}
onPaste={myOnPasteHandler} />
)
Then pass this to Select via the 'components' prop:
import Select from 'react-select'
import CustomInput from './CustomInput'
const MySelectComponent = props => (
<Select
// ...
components={{ Input: CustomInput }} />
)

Material-UI - TextField - select text programmatically

Material-UI V1 beta.
Could not find the answer in the Docs.
How do I select text of a TextField component?
Create a ref to it, then call the value of the ref. Something like this:
<TextField ref="myTextField" />
// Call this in the component that contains the text field so 'this' is set properly
function getTextFieldValue() {
return this.refs.myTextField.getValue();
}
This is known as an uncontrolled react component. An alternative would be to use a controlled component and save the value in your state. Here is some info on the difference between controlled and uncontrolled components: https://reactjs.org/docs/uncontrolled-components.html
if you are using a stateless functional component then you can use react hooks.
Also make sure you are using inputRef
import React, { useState, useRef } from "react";
let MyFunctional = props => {
let textInput = useRef(null);
return (
<div>
<Button
onClick={() => {
setTimeout(() => {
console.log(textInput.current.value);
}, 100);
}}
>
Focus TextField
</Button>
<TextField
fullWidth
required
inputRef={textInput}
name="firstName"
type="text"
placeholder="Enter Your First Name"
label="First Name"
/>
</div>
);
};

react | redux-form | material-ui | how to combine DatePicker with my form

After solving some problems, I stuck with sending DatePicker data to my form. In my form I use mostly elements from redux-form-material-ui, but DatePicker is not a part of it.
I found 2 ways of creating DatePicker component with redux-form.
<Field
name="startDate"
autoOk={true}
floatingLabelText="startDate"
component={(startDate) => {
return <DatePicker {...startDate} />;
}}
onChange={(event, date) => {console.log(date);}}
/>
and
<DatePicker
name="startDate"
autoOk={true}
floatingLabelText="startDate"
onChange={(event, date) => {console.log(date)}} />
The problem is that I don't know the way to update the form data with it. The first example even doesn't show the picked date in text field. I can see in form.myForm store, that I made date field active, but it is never updated with picked date. The second shows picked date, but it is not a part of form.myForm object...
I found some examples in internet (e.g. https://github.com/erikras/redux-form/issues/364 ) but there is no fields object in props, so cannot call this.props.fields.startDate.onChange.
I'm not sure what is a good way of working with redux-form and material-ui, as there is not many working examples. I started to think of making a wrapper to EVERY component I use, which will provide onChange method, which will update my store with any change made in form. But then I don't need redux-form anymore, so I think there must some other solution, I could use.
"react": "15.1.0",
"react-tap-event-plugin": "1.0.0",
"redux": "3.0.5",
"redux-form": "^6.0.0-alpha.4",
"redux-form-material-ui": "^2.0.0",
import React from 'react';
import DatePicker from 'material-ui/DatePicker';
export default ({ input, label, meta: { touched, error }, ...custom }) => {
return (
<DatePicker
onChange={(e, val) => {return input.onChange(val)}}
{...custom}
value={input.value}
/>
);
}
Considering this as in file renderDatePicker.js, usage would be,
<Field name="created_on" component={RenderDatePicker} floatingLabelText="Created Date"/>
Where Field is imported from redux-form.
Remember to provide {created_on: new Date('someiso8601')} in this.props.initialize to autopopulate it.
I used react-datepicker with redux form. The key was to set "selected" property correctly.
const selectedDate = this.props.fields.date.value ?
moment(this.props.fields.date.value, 'DD/MM/YYYY') :
moment();
<DatePicker
{...date}
className="form-control"
dateFormat="DD/MM/YYYY"
selected={selectedDate}
/>
Here is how I use it in my code.
<DatePicker
selected={dateField.value}
onChange={param => dateField.onChange(param)} />
dateField simply comes from:
const {fields: {dateField}, handleSubmit} = this.props;
Not just for this Datepicker but for any custom input, create a separate component to manage the input type and process. Which makes easy to feed your redux-form fields. This is the simple process I was taught by my senior developers when I was a Grad.
In your case, firstly import following additional libraries to your redux-form component
import moment from 'moment'
import { change } from 'redux-form' //We use the change to dispatch the own props of the redux-form component to resetDateField
Create a separate component by importing DatePicker in it. And your component will look something like this:
const datePickerInput = ({input,name,label,...props}) => {
return (
<div>
<label htmlFor={input.name}>
<span>
{label}
</span>
</label>
<div>
<DatePicker
className='form-control'
{...input}
{...props}
/>
</div>
</div>
)
}
Now feed this component to your redux-form field's component.
<Field
name = "startDate"
label = "Start Date"
dateFormat = 'MMM Do YY'
component={datePickerInput}
selected = {/*pass state value of startDate or pass some default null for startDate if nothing selected*/}
onChange={ (date) => {this.handleChangeStartDate(date, 'startDate')} } //bind this function in constructor and also setState value for your start date as null or some default
/>
the onChange function is to be handled something like this using moment:
handleChangeStartDate(date, target) {
const {resetDateField } = this.props
this.setState({
[target]: moment(date)
})
resetDateField('startDate', '' )
}
Now hook this resetDateField in your reduxForm #connect by passing dispatch and ownProps and returning resetDateField value expecting a dispatch of change for current form, field, and field value that you want to change using this resetDateField.

Resources