react-table v7 within react-hook-form - reactjs

i am working on a complex react form where it has few controlled inputs along with grid/table. currently i am using react-hook-form for validation. here is my mockup. idea here is show grid as required until user adds some data. user can add/remove data by clicking "+" or "-" buttons.
when i submit here is what i see in submitted data
{
"fname": "sasa",
"lname": "asasasa"
}
here is the expected output
{
"fname": "sasa",
"lname": "asasasa",
"localAddress":[
{
"street1":"street1",
"street2":"street2",
"city":"city"
},
{
"street1":"street2",
"street2":"street2",
"city":"city"
}
]
}
here is my codesanbox
Codesanbox
not sure how can i integrate react-table (or any table component) with react-hook-form (or any react form). building a form using "react-table" is a must for me.
appreciate any help.

As mentioned in the get-started docs in the "Work with UI library" section:
Option 3: we can set up a custom register using the useEffect
Hook and update the value via setValue.
So here's what needs to be done in your case:
export default function App() {
const { register, handleSubmit, setValue } = useForm();
// ...
React.useEffect(() => {
register({ name: "localaddress" });
}, [register]);
const addLocalAddress = function() {
// ...
setValue("localaddress", d);
setLocalAddress(d);
};
// ...
}
And with this in place you need to get rid of Controller by replacing this:
<Controller
name="tag"
control={methods.control}
as={
<Table1
name="tag"
ref={methods.register}
columns={columns}
data={localAddress}
{...methods}
/>
}
/>;
with this:
<Table1 columns={columns} data={localAddress} />
That should be it. And of course the sandbox.

Here you go :
First : we need the input box, with name that can generate the texted output
name="localAddress[0][street1]"
name="localAddress[0][street2]"
name="localAddress[0][city]"
name="localAddress[1][street1]"
name="localAddress[1][street2]"
name="localAddress[1][city]"
// to generate the name you can do something like this
<Input
type="text"
name={`localAddress[${i}][${columns[j]["accessor"]}]`}
placeholder={columns[j]["accessor"]}
innerRef={methods.register}
/>
Second : we need access to methods
// pass it from props
<Table1
name="tag"
ref={methods.register}
columns={columns}
data={localAddress}
methods = {methods} // <---- Pass HERE
/>
// access it via props
export const Table1 = React.forwardRef(({ columns, data, methods }, ref) => {
WORKING DEMO : (for output you can check the console)

Related

react MUI TextField inside react-hook-form Controller inside MUI Stepper-Dialog setValue not working

I have a Button that opens a MUI Dialog.
Inside the Dialog I have a MUI Stepper. My Form is split up into different parts. Some Inputs are required others are not.
//Example Input
<Controller
name="stateName"
control={control}
rules={{ required: true }}
render={({ field: { onChange, value } }) => (
<TextField
required
label="stateName"
variant="standard"
onChange={onChange}
value={value}
fullWidth
error={errors.stateName ? true : false}
helperText={errors.stateName ? "Pflichtfeld" : null}
/>
)}
/>
Full Example: https://codesandbox.io/s/gracious-tdd-dkzoqy
When I submit my form I add an entry to an existing list and display it alongside with an edit-Button.
If the edit-Button gets pressed I want to open the Dialog and have the Inputs filled with the values of the edited data.
I tried using react-hook-form setValue("field", value) but it is not working.
I also tried to pass the edit-object via Props to the nested form-steps and use setValue inside these components useEffect utilizing useFormContext() but it didn't work either.
How can I pass the values to the Inputs so they get correctly displayed in the Multi-Step-Form-Dialog?
Working CSB -> https://codesandbox.io/s/eloquent-chaum-znt71c?file=/src/App.tsx
In editHandler, state is a json string, so the simplest fix is to parse it into the object
const editHandler = (stateJSON: any) => {
const state = JSON.parse(stateJSON)
methods.reset(state);
But in submitHandler data is stringified, the submitHanlder should look smth like this:
const submitHandler = (data: any) => {
setContent(prevContent => [...prevContent,data] );
methods.reset();
setEditState(undefined);
setOpen(false);
};
Also checkout this out https://beta.reactjs.org/learn/updating-objects-in-state
and
how to avoid mutations https://www.educative.io/courses/simplifying-javascript-handy-guide/B6yY3r7vEDJ

Focus On Input With Error on Form Submission

It would be nice to have functionality when the user submits a form that fails the validation, it will set focus on the first field with an error.
I have shouldFocusError : true set in the useForm configuration. When I click submit, the validation triggers, but the input is not focused.
Any suggestions on how I can achieve this?
For reference, I am using MUI and react-hook-form to create custom components in the form. For the components themselves, they are using the useController hook like so:
const { field, fieldState } = useController(props);
<TextField
{...field}
error={!!fieldState.error}
helperText={fieldState.error?.message || ''}
/>
Solution:
Spreading the field object returned from the useController hook contains the following:
{
value: ...
name: ...
onChange: ...
onBlur: ...
ref: ...
}
The problem I described above was solved when I removed ref property from the object and passed it separately to inputRef. Like so:
const { field, fieldState } = useController(props);
const { ref, ...fieldProps } = field;
<TextField
{...fieldProps}
inputRef={ref} //or you can use field.ref
id={field.name} //this solved other issues I was having
error={!!fieldState.error}
helperText={fieldState.error?.message || ''}
/>
After doing this, React Hook Form set focus on the first field with an error when the user submits a form that fails the validation.

Is there a way to set defaultValues in a child component using react hook form with useFieldArray hook - not using the useForm hook?

Problem
I want to implement key/value pairs of input fields in a form that can be added by a user.
See animated gif on dynamic fields.
In addition, I want to display saved data when the user has submitted the form and the page is displayed again.
See animated gif on displaying saved dynamic fields.
Preconditions
I'm using react-hook-form V7 (RHF) and its useFieldArray hook.
As I use Material-UI I have to use controlled components.
Working solution
In a simplified app, I have a parent component using the useForm hook and two child components, one for the purpose of demonstration holding normal form fields and an <ArrayFields /> component holding the array fields.
Yesterday I learned by this answer that one way to do it is to set the defaultValues object in the parent's useForm hook like this:
const methods = useForm({
defaultValues: {
email: "john.smith#example.com",
firstName: "John",
lastName: "Smith",
systemRole: "Admin",
envRoles: [ // <-- saved dynamic fields
{ envName: "foo1", envRole: "bar1" },
{ envName: "foo2", envRole: "bar2" }
]
}
});
Here you can see a codesandbox of this working solution.
Question
Nonetheless, I am wondering if it isn't possible to set the defaultValues in the <ArrayFields /> child component?
useFormContext approach
For example like this using the useFormContext hook:
//
const ArrayFields = ({ fieldset }) => {
const { control, getValues } = useFormContext({
defaultValues: {
envRoles: [
{ envName: "foo1", envRole: "bar1" },
{ envName: "foo2", envRole: "bar2" }
]
}
});
const { fields, append, remove } = useFieldArray({
control,
name: "envRoles"
});
...
}
But this is not displaying the saved fields at all
See here a codesandbox version of the useFormContext approach
Props approach
Next, I tried to pass the fields (alias envRoles) as props and set the defaultValues to the Controller directly
// index.js:30
<ArrayFields
fieldset={[
{ envName: "foo1", envRole: "bar1" },
{ envName: "foo2", envRole: "bar2" }
]}
/>
// ArrayFields.js:35
<Controller
render={({ field }) => <input {...field} />}
defaultValue={item.envName} {/* <-- defaultValue on Controller */}
name={`envRoles[${index}].envName`}
control={control}
/>
This displays the defaultValues
but does not work when clicking on the add or delete button
See this codesandbox of the props approach
Question again
So is it really true that RHF does not allow handling all things that matter to the component inside this component?
Thanks in advance for your hints.
I think I found the answer. 🙊
In the documentation of useFieldArray you'll find the replace method.
replace (obj: object[]) => void Replace the entire field array values.
So using a useEffect hook it's finally easy.
useEffect(() => {
replace(fieldset);
}, [fieldset, replace]);
In this codesandbox you will find the example that finally works with setting the defaultValues in the child component.

React-Select: Page turns white after selecting an option

I am pretty new to React and am trying to use React-Select for a simple dropdown menu.
When you selected an option it should display the value under it, for that I'm using the onChange function and the useState Hook, but everytime I select something, the whole page just turns white.
App.js
import "./App.css";
import Select from "react-select";
import { useState } from "react";
function App() {
const [selected, setSelected] = useState(0);
const options = [
{ value: "1", label: "a" },
{ value: "2", label: "b" },
{ value: "3", label: "c" },
];
return (
<div>
<Select placeholder="Choose one"
defaultValue={selected}
onChange={setSelected}
options={options}
/>
<h1>{selected}</h1>
</div>
);
}
export default App;
Any help is appreciated, thank you.
I've done a simple edit on your code:
When onChange fires you set the selected value with onChange={(e)=>setSelected(e.value)}
Here's a functional codesandbox.
If you inspect your console in the browser, you'll see the issue is coming from the h1 component. This is probably what you're trying to achieve:
<h1>{selected.value}</h1>
I found a way to solve this, however I don't know if this is the best solution.
I created a function handleChange
const handleChange = e => {
setSelected(e.value);
}
and added a value prop, and called the function with onChange
<Select placeholder="Choose one"
defaultValue={selected}
value={options.find(obj => obj.value === selected)}
onChange={handleChange}
options={options}
/>
I don't know why
<h1>{selected.value}<h1>
doesn't work, since it's practically the same what I'm doing in handleChange, right?

form & initialValues properties not recognized with multiple forms in one component (redux-form v7.0.4)

I'm creating multiple forms within a single component and to initialize them with the redux store I'm defining the form's name attribute in the<form> element, as opposed to within the reduxForm() helper, which was documented here...
How to embed the same redux-form multiple times on a page?
I'm creating the forms from the 'listing' object and passing it to my components with mapStateToProps(). I'm trying to set the initial values of the form with initialValues={}, but Redux Form is producing the following errors and is asking for the form to be declared in the reduxForm() helper...
1) Failed prop type: The prop form is marked as required in Form(ItemInfo), but its value is undefined.
2) Unknown prop initialValues on tag. Remove this prop from the element.
And this appears to be similar to the issue mentioned here...
https://github.com/erikras/redux-form/issues/28
import _ from 'lodash';
import React, { Component } from 'react';
import { reduxForm, Field } from 'redux-form';
import { connect } from 'react-redux';
import * as actions from '../../../actions';
import {Col} from 'react-grid-system';
import RaisedButton from 'material-ui/RaisedButton';
class ItemInfo extends Component {
renderSingleItem(item){
let theItem = _.map(_.omit(item, '_id'), (value,field) => {
return (
<div key={field}>
<label>{field}</label>
<Field component="input" type="text" name={field} style={{ marginBottom: '5px' }} />
<div className="red-text" style={{ marginBottom: '20px' }}>
</div>
</div>
);
});
return theItem || <div></div>;
}
renderItemInfo() {
if (this.props.listing.listing !== undefined) {
let theItems = _.map(this.props.listing.listing.items, item => {
return (
<Col key={item._id} md={3}>
<form form={`editItemInfo_${item._id}`} initialValues={item}>
{this.renderSingleItem(item)}
<RaisedButton secondary={true} label="Remove Item"/>
<RaisedButton primary={true} label="Update Item"/>
</form>
</Col>
);
});
return theItems || <div></div>;
}
}
render() {
return (
<div className="row">
{this.renderItemInfo()}
</div>
);
}
}
function mapStateToProps({listing}) {
return { listing };
}
ItemInfo = reduxForm({
fields: ["text"],
enableReinitialize: true
})(ItemInfo)
ItemInfo = connect(mapStateToProps,actions)(ItemInfo)
export default ItemInfo
This is an example of the 'listing' object being returned...
{ _id: 59b5eebd33a3a833b6ac1386,
_user: 59aff09a11011f0cfd8d7666,
location: 'nother',
availability: 'jhkvljh',
price: 9860,
__v: 0,
items:
[ { equipmentType: 'kbj;kj',
make: ';jb',
model: ';;jb',
watts: 9860,
bulb: 'kjbkj',
condition: 'kjbkjb',
_id: 59b5eebd33a3a833b6ac1387 },
{ condition: 'houy',
bulb: 'jhg',
watts: 8907,
model: 'mode',
make: 'maker',
equipmentType: 'smoquip',
_id: 59b5f9cf13b37234ed033a75 } ] }
Thanks for your help!
I finally figured out a work-around with a little hack. It appears that this is one part a bug with Redux Form and one part error with my initial implementation.
Correct Implementation
As detailed by #erikras, Redux Form creator...
https://github.com/erikras/redux-form/issues/28
The form config parameter needs to be passed to a decorated component and not to a jsx <form> component. To do this I refactored my form into an imported child component and mapped over these instead...
renderItemForms() {
if (this.props.listing.listing !== undefined) {
return _.map(this.props.listing.listing.items, item => {
return (
<ItemInfo form={`editItemInfo_${item._id}`} initialValues={item} key={item._id} item={item} />
);
});
}
}
Redux Form Bug
The above implementation will connect your forms to the redux store properly, but it will still create a 'Failed prop type: The prop form is marked as required' error that will break your views. The solution I found is to stick any random string in the 'form' property of the redux-form options to prevent the error...
ItemInfo = reduxForm({
form: 'any random string here',
fields: ["text"],
enableReinitialize: true
})(ItemInfo)
The second error message for initialValues was only subsequent to the first 'form parameter' error so now everything is error free and in Redux dev tools I can confirm that the in-line form property is overriding the property from reduxForm() options. Forms are now successfully being managed by the redux store with the correct 'form name/id'...
I hope this helps save someone else a headache minor. Please excuse any incorrect terminology in my explanation above, I'm still a Redux/React noob, but if anyone want more details Im happy to provide more details on my implementation.

Resources