antd design react problem with form reload and useState - reactjs

I'm using ant design in react and i would like to make a coordinate select.
I have a select (customerSelect) with an array and another select ( with a state (appezzamentiUs) like options to another select.
const [form] = useForm();
const [appezzamentiUs, setAppezzamentiUs] = useState <Array<{value: string,label: string, info: any}>> ([]);
function changeCustomer(value: Cliente) {
let id = value.id || '';
let appezzamentiArr = store.appezzamentisDc?.items.filter((a)=>{return a.cliente?.id == id}).map(item =>{
let label = item.denominazione? item.denominazione : "";
let value = item.id? item.id : '';
let info = item? item: ''
return {value: value, label: label, info: info};
}) || [{value:"",label:"", info: ""}];
setAppezzamentiUs(appezzamentiArr);
console.log("appezzamenti " + id)
console.log(appezzamentiArr)
}
...
<Form.Item name={"customer"}>
<Select
id={"customerSelect"}
style={{width: "100%"}}
showSearch
placeholder="Find customer"
optionFilterProp="children"
filterOption={(input, option) =>
(option?.label ?? '').toString().toLowerCase().includes(input.toLowerCase())
}
options={customerOption()}
onSelect={(value, option)=> {
changeCustomer(option.info)
}}
allowClear
/>
</Form.Item>
<Form.Item name={"appezzamenti"}>
<Select
mode="multiple"
style={{ width: '100%' }}
placeholder="Seleziona Appezzamenti"
options={appezzamentiUs}
/>
</Form.Item>
When i select a customer the second select works perfect, it get right option but the form is reloaded and lost the customer selection. the row setAppezzamentiUs(appezzamentiArr); reload form.
Isn't the right way this approach? i could use state with form without have this problems?
Thanks
I tried to comment the row setAppezzamentiUs(appezzamentiArr); and the form doesnt reload

Related

Getting selection from Fluent UI dropdown in React project

I am printing to the console a user selection from a Fluent UI dropdown. The only challenge I'm running into is grabbing the actual selection. I don't see how FLUENT is handling that, as it's not mentioned in their documentation.
My code looks like this:
<Dropdown
placeholder="Select Reason"
label="Send to History with Reason"
onChange={(selectedKey) => this.onReasonChange(selectedKey)}
options={this.graveyardReasons}
onRenderLabel={this.onRenderLabel}
styles={{
root: {
maxWidth: 300,
minWidth: 300,
},
}}
/>
And here's the function that gets called on a selection being made:
onReasonChange = (selection) => {
console.log('selection.target: ', selection.target);
};
This is what prints to the console:
<div data-is-focusable="true" id="Dropdown23" tabindex="0" role="listbox" aria-haspopup="listbox" aria-expanded="false" aria-labelledby="Dropdown23-label Dropdown23-option" class="ms-Dropdown dropdown-155"><span id="Dropdown23-option" class="ms-Dropdown-title title-220" aria-live="polite" aria-atomic="true" aria-invalid="false" role="option" aria-setsize="4" aria-posinset="2" aria-selected="true">Selection 1</span></div>
How do I pull the value Selection 1 from this div? And, is there a simpler way to get the value that I'm simply missing?
As Fluent UI docs says about onChange:
(
event: React.FormEvent<HTMLDivElement>,
option?: IDropdownOption,
index?: number
) => void
Callback issued when the selected option changes.
So selected value is the second parameter. Let me show an example:
const DropdownBasicExample: React.FunctionComponent = () => {
const onReasonChange = (e, selection) => {
console.log('selection: ', selection);
};
return (
<Stack tokens={stackTokens}>
<Dropdown
placeholder="Select an option 1"
label="Basic uncontrolled example"
onChange={onReasonChange}
options={options}
styles={dropdownStyles}
/>
</Stack>
);
};

setting Date value on LocalStorage

I would like some help to set this date Value into a LocalStorage and then When reloading the app, I would like that the default value of the date component was retrieved from local Storage. Right Now, I'm Using TextField with type date on React js.
const [dateBegin,setDateBegin] = React.useState(new Date())
<TextField
id="date"
label="InĂ­cio "
type="date"
multiline={false}
onChange={
(e)=>{setDateBegin(new Date( e.target.value));
localStorage.setItem('#legis/datebegin',dateBegin)
}}
InputLabelProps={{
color:'secondary',
className:"DatePicker",
style : {color:"#ffff",},
shrink: true,
}}
inputProps={{
style: { color: "#ffff" },
}}
/>
<div style = {{marginLeft:50}}>
<TextField
However, This is not working right now. Would please help me how to set the date value and then when to retrieve, and if possible how to set the default value of this TextField as the value I retrieved from localStorage?
You trying to save a Date object in local storage where it accepts string only as the key value.
You can parse the string date you saving and then it'll work fine or as an alternative, use a string date format if possible in your use case:
const dateObjFromString = new Date(localStorage.getItem('date'))
In addition to what #Dvir Hazout said, if you read from a state variable right after setting it, the value will always be stale.
const [val, setVal] = useState(1)
return (
<div
onClick={() => {
setVal(2)
console.log(val) // logs 1 on first click, 2 on subsequent clicks
}}
/>
)
Instead, you need to assign it to a normal variable:
const [val, setVal] = useState(1)
return (
<div
onClick={() => {
const newVal = 2
setVal(newVal)
console.log(newVal) // works as expected
}}
/>
)

How do i default check a radio button in react?

I am trying to default check the first radio button which the following code helps me to do. When loaded the page the first radio button is checked but the problem i am facing is that it doesn't allow me to check the other buttons that also are present in the array.
constructor(props: any) {
super(props);
this.state = {
selectedSort: '',
sort: ['Apple', 'Orange '],
}
}
this.state.sort.map((sortType:string, index:number) => {
return <span key={`${sortType}${index}` onClick={() => this.setSort(sortType)} >
<input type="radio" id={sortType}
value={this.state.selectedSort}
name={sortType} defaultChecked={index===0}
}/>
<span>{sortType}</span>
})
private setSort = (selectedSort: string) => {
this.setState({
selectedSort: selectedSort
});
}
Issue
The defaultChecked value is a boolean but your condition sortType === 0 will always evaluate false since your sortType is only ever one of your sort state values, i.e. ["Apple", "Orange "].
Solution
If you want the first radio button to be default checked then you should compare against the mapped index.
defaultChecked={index === 0}
Other Issues & Suggestions
Radio button group inputs should all have the same name attribute.
Use a semantic label to wrap your inputs so they are more accessible.
Use the radio input's onChange event callback versus an onClick, to update state.
The sortType values alone should be sufficient for a React key.
Code:
{this.state.sort.map((sortType, index) => (
<label key={sortType}>
<input
type="radio"
id={sortType}
value={selectedSort}
name="sortType"
defaultChecked={index === 0}
onChange={(e) => this.setState({ selectedSort: e.target.id })}
/>
{sortType}
</label>
))}
Additionally, I suggest converting this to a fully controlled input since you have already all the parts for it. Remove the value attribute and use the checked prop. Set what you want the initial checked state to be. This will allow you have have already valid checked state.
state = {
selectedSort: 'Apple',
sort: ['Apple', 'Orange '],
}
{this.state.sort.map((sortType, index) => (
<label key={sortType}>
<input
type="radio"
id={sortType}
name="sortType"
checked={sortType === this.state.selectedSort}
onChange={(e) => this.setState({ selectedSort: e.target.id })}
/>
{sortType}
</label>
))}
Demo

React select multiple true- comma separated value display

Is it possible to get the display of selected values as comma separated.. instead of the box with cross sign
import Select from 'react-select'
<Select
name=''
styles={customStyles}
isClearable
isMulti
/>
You can create your own custom MultiValueContainer component. To display comma separated options, we can do something like this:
render() {
const components = {
MultiValueContainer: ({ selectProps, data }) => {
const values = selectProps.value;
if (values) {
return values[values.length - 1].label === data.label
? data.label
: data.label + ", ";
} else return "";
}
};
return (
<Select
value={this.state.value}
onChange={this.handleChange}
isMulti
name="colors"
options={colourOptions}
className="basic-multi-select"
classNamePrefix="select"
components={components}
/>
);
}
here is the code sandbox link to see above code in action.
According to the React-Select docs, you can change the styles of the individual selected items.
But, they render as boxes with x buttons on them so that the user can choose to de-select any of the selected items.
I would suggest playing with the React-Select styles like this:
<Select
styles={customStyles}
/>

How to trigger validation in formik after rendering?

I have one question with formik. Basically, I will have a table which list all the Id of the forms which have errors. When user click on the Id of a form, it will show up the form itself. The requirement is the errors should be showing also when the form is rendered. Does anyone know how to do that with Formik ? Also, if user edit the field the field validation should works as normal.
I put the codesandbox link here. https://codesandbox.io/s/pensive-brattain-yyls2. Basically I want that when the form show up I should see the errors, not just when user move away from the field or changing it. Thank you.
import { Formik, Field, Form } from "formik";
import { TextField } from "formik-material-ui";
class Post0 extends React.Component {
validateEmptyName(value) {
if (!value) {
return "Invalid Name";
}
}
render() {
return (
<div>
<Formik
initialValues={{
email: "",
animal: ""
}}
onSubmit={values => {
this.props.nextStep(values);
}}
render={({ values, isSubmitting }) => (
<Form>
<Field
name="email"
type="email"
value={values.email}
component={TextField}
variant="outlined"
validate={this.validateEmptyName}
/>
<Field
name="animal"
value={values.animal}
component={TextField}
variant="outlined"
/>
<button type="submit">Submit</button>
</Form>
)}
/>
</div>
);
}
}
I made a basic demo version using a custom input component. Formik has no built-in options to do this so unfortunately you need to create your own field components to integrate with Formik's props and bypass the logic that won't show validations if the form's not touched.
const Input = ({ field, form }) => {
useEffect(() => {
form.validateForm();
}, []);
return (
<div>
<input
style={{
border: form.errors[field.name] ? "1px solid red" : "1px solid #ccc"
}}
name={field.name}
value={field.value}
onChange={field.onChange}
/>
{form.errors[field.name] && (
<span style={{ color: "red" }}>{form.errors[field.name]}</span>
)}
</div>
);
};
And then pass this as the component prop on your <Field/>.
Formik does provide an isInitialValid prop which you could set to false on the main Formik component, but again the library TextFields you're using won't display anything without the touched prop.
2021 update:
Use validateOnMount prop:
https://formik.org/docs/api/formik#validateonmount-boolean
validateOnMount works if you also add initialTouched, but it has limitation (...or better say bug) when it shows validation issues after submit which doesn't lead to different view or component.
I have found pretty elegant workaround which works as expected.
const formikRef = React.useRef(null);
React.useEffect(() => formikRef.current.validateForm(), []);
return (
<Formik
innerRef={formikRef}
initialValues={props.customer}
initialTouched={mapObject(props.customer, true)}
onSubmit={values => {
.
.
.
you can use isInitialValid or initialErrors to valid initial values.
check their official docs here.
I accomplished this using Yup's validateAtSync function while populating the initial values of my form from the querystring.
function generateInitialValues(tabs: TabType[], router: any, validationSchema: any) {
const initialValues: { [key: string]: number | string } = {};
_.forEach(tabs, (tab: TabType) => {
_.forEach(tab.formFields, (f: FormField) => {
let isFieldValid;
try {
// https://github.com/jquense/yup#mixedvalidatesyncatpath-string-value-any-options-object-any
console.log('validation schema validateAt: ', validationSchema.validateSyncAt(f.id, router.query[f.id]));
isFieldValid = validationSchema.validateSyncAt(f.id, router.query[f.id]);
} catch (e) {
// do nothing on purpose to stop exception from being thrown
// TODO: Consider doing something here, such as recording a metric
}
initialValues[f.id] = isFieldValid ? router.query[f.id] : f.defaultValue;
})
});
return initialValues;
}

Resources