I will reduce the issue to a simple example. I have a table component rendering a table with queried data. I also have a small form that allows users to input data into the table. The issue i am having is that the default value of the input fields are empty strings.
Sample input field:
<td><input type="text" placeholder="Nombre" name="name" onChange={this.handleChange} value={this.state.name}/></td>
Assume that the state is holding an empty string as default value for the input field
this.state={name:' '}
Assume also that i want to send in my form an empty string to the database. I am doing the mutation the apollo way, with graphql:
const ADD_CONTACTS = gql`
mutation createContact(
$name: String,
){
createContact(
name: $name
){
id
name
}
}`
<Mutation mutation={ADD_CONTACTS}>
{createContact=>(
<form onSubmit={async e=> {
e.preventDefault();
let name = this.state.name;
await createContact({variables : {
"name":name
} })
}}
>
<table>
... table markup with input fields
</form>
)}
</Mutation>
Ok, that's the general context. In actuality the form contains 6 fields. But the issue is that when I send an empty string to the query inside the variable, the moment it sends the query to the server it changes the empty string to a null value so that i end up with this error:
GraphQL error: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'name' cannot be null
I have tested the server using graphiql and the requests themselves can accept empty strings, but it seems to be that the mutation component or apollo, somewhere, is converting my empty string that im passing to the graphql variables into null.
I have managed to make a workaround for this scenario, but it's definitely not the right way to deal with this. If I could somehow get apollo to actually send empty strings instead of null it would solve the issue.
thanks to anyone that has any idea why this i
Try removing async and await.
Related
I am working on converting forms from Formik over to React Hook Form. yup is the validator in both cases. However I've noticed that despite leaving the validation schema in tact, there seems to be some sort of hierarchy mismatch between Formik and RHF.
I noticed for instance that for min, max, required, originally in that order, validation was working correctly with Formik, but I had to invoke required before min & max in order for the correct validation message to trigger on an input. That fix was self-explanatory enough.
But some of these are tricky. For instance:
dob: yup.date().when([], (schema: yup.DateSchema) => {
return boolCheck1 && boolCheck2
? yup.string()
: schema
.min(
moment.utc().add(1, 'day').subtract(126, 'years').toDate(),
'an error message'
)
.max(
moment.utc().subtract(15, 'years').toDate(),
'another error message'
)
.transform((_, originalValue) => {
const date = moment.utc(originalValue, 'MM/DD/YYYY');
return date.isValid()
? date.startOf('day').toDate()
: new Date('');
})
.typeError('A valid date is required (MM/DD/YYYY)')
.required('Please enter a valid date');
})
It seems that regardless of where I am placing the required check, the one that is triggered is typeError ("A valid date is required (MM/DD/YYYY)"). Basically, if a user touches and blurs this input field without having typed anything, it should trigger the required message ("Please enter a valid date".) Only when a user types something that does not pass the typeError check should the typeError message appear.
This was working previously with Formik. However, now, I'm using React Hook Form as such:
const rhfMethods = useForm({
defaultValues,
resolver: yupResolver(validationSchema),
mode: 'onTouched',
});
The only difference really is that now the yupResolver is being used (without which I get a typescript error.)
If anyone could shed any light on this that would be greatly appreciated!
Nvm, looks like a similar question had already been asked and answered.
While that definitely helped shed light on the issue ('' is still a string type, not simply no data), what helped me solve this was this addendum.
I still needed the initial '' to be a valid input type, so instead of returning an Invalid Date (new Date('')) in the else case, I just needed to return undefined.
Final fix:
dob: yup.date().when([], (schema: yup.DateSchema) => {
return boolCheck1 && boolCheck2
? yup.string()
: schema
.min(
moment.utc().add(1, 'day').subtract(126, 'years').toDate(),
'an error message'
)
.max(
moment.utc().subtract(15, 'years').toDate(),
'another error message'
)
.transform((_, originalValue) => {
const date: moment.Moment = moment.utc(
originalValue,
'MM/DD/YYYY'
);
return date.isValid() ? date.startOf('day').toDate() : undefined;
})
.typeError('A valid date is required (MM/DD/YYYY)')
.required('Please enter a valid date');
})
(To be fair... I tried testing on the UI and there was no instance where this .typeError was ever triggered (even with Formik) since every allowable input instance (only numbers can be typed and they're auto-formatted to dates) was already being checked by the other tests... I think I could safely remove that typeError check altogether lol.)
I have a form ingesting some strings and a file. The strings are working correctly, but whenever I check to see if the file has been registered correctly, it's always null.
The interface I'm using:
export interface myDetail {
myText: string;
Picture?: File;
}
Form group for image (not working):
<Form
onSubmit={handleSubmit((data) => {
console.log(data);
setDetail({ ...myDetail, ...data });
})}
>
<Form.Group controlId="formFile" className="mb-3">
<Form.Label>Upload Picture</Form.Label>
<Form.Control as="input" type="file" {...register('Picture')} />
</Form.Group>
button:
<AsyncButton type="submit">Save</AsyncButton>
The string variables get assigned fine, but when I check data, the Picture is set to "0, {}"
Thanks for any help
I don't know what the Form.*** component is, so I think it's difficult to answer.
Can it be reproduced with code-sandobox etc.?
If Form.Control is built as a controlled-component,
you should be able to successfully connect to RHF using useController or Controller.
https://react-hook-form.com/api/usecontroller
https://react-hook-form.com/api/usecontroller/controller
JSON.stringify does not parse File objects. Your data is most likely a FileList containing one File object, hence the "0,{}". 0th index in the array, containing an unserializable File object.
If you break at your console.log(data) and examine the data in a debugger, you'll most likely see your File object as expected. The data is there, but not being displayed to the console. Please see this post for more info:
How can I serialize an input File object to JSON?
Alternatively, you can use FileReader to read the File using readAsDataURL().
I get an error Cannot add property key, object is not extensible using "#apollo/client": "^3.0.2" with ant design.
I can successfully obtain data from the server with my graphql query, however, the problem is that, the data inside the return object is somehow unextendable. This never used to be a problem prior to using apollo client (using apollo boost).
In order to make my data work in a table in ant design, I have to pass in the array data obtained from the data object returned from the server. The part where I get the error is when I attempt to iterate through the items array, and add a key to each object, so that react won't complain in the console of a missing key element for the html.
const items = [...data.items];
console.log("items:", items);
// Add a key for each item to make React stop complaining
// Erorr occurs here!!!
items.forEach(item => {
item.key = item.id;
});
Is there a way we can remove this unnecessary functionality that's hindering our ability in modifying and extending results obtained from the server? Also I tried spreading the array making a deep copy, but that's not working either.
What do I need to do to make sure that I can add an additional key field in each array item, based on the id?
Addendum:
I don't actually need the code that does the items.forEach() stuff. However, if I don't add it, react will complain that the entries in the table are all missing a key value. This is my workaround since tables in ant design get their data from an array of objects, and thus, need to have a key value in there. Since querying data from the server typically doesn't include a key property in an object, this has to be manually added.
Managed to figure it out by deep copying the array data, via lodash.
Use _.cloneDeep() on the returned data's object that is the name of the object/table you are querying for.
import * as _ from "lodash";
import { Table } from "antd";
function Component({ id, query }) {
const { loading, error, data } = useQuery(query, {
variables: { id }
});
if (loading) return <div />;
if (error) return `Error! ${error}`;
const items = _.cloneDeep(data.items);
console.log("items:", items);
// Add a key for each item to make React stop complaining
items.forEach(item => {
item.key = item.id;
});
return (
<Table
dataSource={items}
/>
);
I have saved a query in a DB and recalled it for running again. However, I do not know how re-run the query.
I obtain the query in the onChange and saved the value to the DB as a string for recalling later so it can be worked on.
<ReactiveBase app={this.props.app} url={config.elastic.URL} >
<StateProvider
onChange={(prevState, nextState) => {
queryString = nextState;
console.log('onChange');
}}
I was looking at a way to put that query string in the DataSearch component if that is the correct thing to do. The query is quite large as I have additional components attached to the search.
If I set the value of the DataSearch to my queryString I can not type in the search bar
<DataSearch
dataField={this.props.refinement.searchBar}
componentId="search"
placeholder="Search books or videos"
autosuggest={false}
debounce={debounce}
showClear={true}
customQuery={this.props.customQuery}
defaultValue={this.props.defaultValue}
value={queryString}
>
</DataSearch>
also the query is an elastic search query so I get a search bar full of JSON. I can get the indavidual elements and set queryString to that which gives me half of what I want but with a searchbar I can not type into.
var objectQuery = JSON.parse(selectedQueryString);
queryString = objectQuery.search.value;
Update
This helped in being able to type in the searchBar and update the value and may even be most of the way there for the DataSearch component.
value={this.state.value}
onChange={(value, triggerQuery, event) => {
this.setState(
{
value,
},
() => triggerQuery(),
);
}}
You can not push the query string back into the component and let it handle all the components used to create the query. You have to get the values from the query string and set the values of the components again. The query then will be re-run using via the onChange mehtod.
I have an input field like so:
<input name="name" id="name" onChange(event => onChange(event)) />
I can use input properties like name, id or ref fields to store data in the input field so that on input onChange, in order to be able to perform the following.
onChange(event)
{
data = this.state.data;
data[event.target.id] = event.target.id
this.setState({ data });
}
What if I have many values to store in the input field? Is it possible to set an object with multiple values in the input field? For e.g. if id could be an object, I could do something like, event.target.id.name, event.target.id.age, event.target.id.address. I'm aware that id is not the correct property to store multiple data. However, I'm not sure id or any other property of an input can store an object.
data[event.target.property] = event.target.property
What is the correct approach to this?