Is there a way to have 2 onSubmit events in React? - reactjs

I was wondering if anyone could explain how I can have 2 onSubmit events in React? I have a button for clicking a form on submission, and it has a thank you message popping up, but I'm trying to clear the localStorage at the same the form is submitted. I have tried this:
const handleSubmit = (e) => {
e.preventDefault();
alert(`Thank you for your order! ^_^ Please check your texts for updates!`);
toggle()
};
const clearCartStorage = () => {
localStorage.clear();
}
<Form onSubmit={handleSubmit && clearCartStorage()}>
These work separately, but when I have the && only the clearCartStorage function will run without the handleSubmit pop up.

Make a new function which calls those other functions:
<Form onSubmit={(e) => {
handleSubmit(e);
clearCartStorage();
})>

Careful, you are invoking clearCartStorage when you create the Form component there.
<Form onSubmit={handleSubmit && clearCartStorage()}> ❌❌❌
It should take one function which can call multiple.
I would set it up like this. It's more common to keep the function definition out of the returned JSX.
const MyComponent = () => {
const handleSubmit = (e) => {
e.preventDefault();
clearCartStorage()
alertUser()
};
const clearCartStorage = () => {
localStorage.clear();
}
const alertUser = () => {
alert(`Thank you for your order! ^_^ Please check your texts for updates!`);
toggle()
}
return <Form onSubmit={handleSubmit}>
}

Neither of the responses worked. I should have posted my whole code, but what I did to fix it was create a separate component with the new function that runs the alert and clearStorage functions. Having everything on the same component within a modal div was not working.
So I basically have:
<Modal>
<FormComponent /> // my new function in here
</Modal>

Related

useRef in <Form /> Component | Informed | PWA Studio | React

I need to execute the form submit using useRef(). I'm trying to reference the component provided by 'informed' (PWA Studio forms lib), but the reference doesn't work.The reference isn't work to informed Form but I don't know why. I tried the same reference using normal html form <form ref={formRef}>... and in this case worked.
const formRef = useRef(null);
...
// 'informed Form'
<Form ref={formRef}>...</Form>
// It does't submit the form after function executed
const handleSubmit = () => {
formRef.current && formRef.current.submit();
};
In informed it is really not possible to refer to it through the useRef simple way. To achieve your goal you can use the getApi={formRef.current} prop in the Form component
// You can create your useRef() in this way
const formApiRef = useRef(null);
const setFormApi = useCallback(api => (formApiRef.current = api), []);
// In your form component set the apiForm
<Form getApi={setFormApi}> ... </Form>
// In your handleSubmit function in order to submit you can do this
const handleSubmit = useCallback(() => {
const { current: formApi } = formApiRef;
if (formApi) {
formApi.submitForm();
}
}, [applyCoupon, cartId]);

How to get propperly FormState (dirtyFields, prestine, etc.) in handleSubmit function using React-Final-Form

Basically, I just want to get only that fields, that were changed (dirty). How should I get them inside submit handler? (I need to post only changed fields to API)
const handleSubmitSettings = (fields, formApi) => {
console.log("Submitted with fields:", fields);
};
const handleValidate = (props) => {
console.log("Validated with props:", props);
};
return (
<Form
onSubmit={handleSubmitSettings}
validate={handleValidate}
>
{({handleSubmit,form: {submit, change}}) => (
<form onSubmit={handleSubmit} className={classes.formFlexContainer}>
<SettingsHeader/>
<SettingsContainer/>
</form>
)}
</Form>
);
In your submit handler you could retrieve all registered fields via the second argument formApi, then collect all fields marked dirty:
const onSubmit = async (values, api) => {
const dirtyOnly = {};
for (let field of api.getRegisteredFields()) {
const fs = api.getFieldState(field)
if (fs.dirty) {
dirtyOnly[fs.name] = fs.value;
}
}
// submit dirtyOnly
}
in the example above, dirtyOnly object would contain all values from dirty fields.
But such an approach seems questionable since it may not properly handle a case with more complex state shapes.

onChange antd Input component doesn't setState (hook) properly with list from axios get in React

I'm new to React, been working on it for the past week. I'm trying to make a simple app that has a 'product create' form and a list of products with a search bar (using Input component from antd); in the list I can click on any product to open the details page.
Right now I'm blocked by some not properly working logic or something I miss. When I tried the Input onChange with an Array I created in the code it worked fine, but now that I'm using a mock api (from fakestoreapi.com to be precise), I can't make it work.
ProductsList.tsx
function ProductsList() {
const [list, setList] = useState<Array<Product>>([]);
const [loading, setLoading] = useState<boolean>(true);
useEffect(() => { // I think something is wrong here
ProductService.getAll()
.then((res: any) => {
setList(res.data);
setLoading(false);
})
.catch((e: Error) => console.log(e));
}, []); // tried: 'query' const from state, 'filterList' from state
function onChange(e: React.ChangeEvent<HTMLInputElement>) { // Or here (or both here and in useEffect)
console.log('in onChange');
const filterList: Array<Product> = list.filter((item) =>
item.title.toLowerCase().startsWith(e.target.value.toLowerCase())
);
setList(filterList);
}
return (
<div>
<Spin spinning={loading}>
<List
header={
<Input
type="text"
placeholder="Search product"
allowClear
onChange={onChange}
/>
}
split
dataSource={list}
renderItem={(item) => (
<List.Item key={item.id}>
<Link to={`/products/${item.id}`}>{item.title}</Link>
</List.Item>
)}
></List>
</Spin>
</div>
);
}
export default ProductsList;
I tried adding some dependencies to the useEffect hook, but maybe they were the wrong ones. As I said, with a local array this worked, but now after loading the full list once, when I get to the Input and search something, the list is deleted. I think I spotted the problem in the fact that I don't reset the list to the full one, but I don't actually know how to do that (that's why I'm here). I tried to search something online but except for dependencies, I didn't find something specific to help me.
If needed, here is the ProductService.getAll() function:
function getAll() { // http is axios
return http.get<Array<Product>>(`/products`);
}
I'll be glad to add everything that could be helpful if needed.
const [list, setList] = useState<Array<Product>>([]); // The full list
const [filteredList, setFilteredList] = useState<Array<Product>>([]); // the list you display
function onChange(e: React.ChangeEvent<HTMLInputElement>) { // Or here (or both here and in useEffect)
console.log('in onChange');
const temp: Array<Product> = list.filter((item) => //keep the filter on the full list but only display the filtered list
item.title.toLowerCase().startsWith(e.target.value.toLowerCase())
);
setFilteredList(temp);
}
//the datasource: dataSource={filteredList}

clickOutside hook triggers on inside select

I have a card component which consists of 2 selects and a button, select1 is always shown and select2 is invisible until you press the button changing the state. I also have an onClickOutside hook that reverts the state and hides select2 when you click outside the card.
The problem Im having is that in the case when select2 is visible, if you use any select and click on an option it registers as a click outside the card and hides select2, how can I fix this?
Heres the relevant code from my card component:
const divRef = useRef() as React.MutableRefObject<HTMLInputElement>;
const [disableSelect2, setDisableSelect2] = useState(true);
const handleActionButtonClick = () => {
setDisableSelect2(!disableSelect2)
}
useOutsideClick(divRef, () => {
if (!disableSelect2) {
setDisableSelect2(!disableSelect2);
}
});
return (
<div ref={divRef}>
<Card>
<Select1>[options]</Select1>
!disableSelect2 ?
<Select2>[options]</Select2>
: null
<div
className="d-c_r_action-button"
onClick={handleActionButtonClick}
>
</Card>
</div>
);
};
And this is my useoutsideClick hook
const useOutsideClick = (ref:React.MutableRefObject<HTMLInputElement>, callback:any) => {
const handleClick = (e:any) => {
if (ref.current && !ref.current.contains(e.target)) {
callback();
}
};
useEffect(() => {
document.addEventListener("click", handleClick);
return () => {
document.removeEventListener("click", handleClick);
};
});
};
Extra informtaion: Im using customized antd components and cant use MaterialUI
I tried to recreate your case from the code you shared. But the version I 'built' works.
Perhaps you can make it fail by adding in other special features from your case and then raise the issue again, or perhaps you could use the working code from there to fix yours?
See the draft of your problem I made at https://codesandbox.io/s/serverless-dust-njw0f?file=/src/Component.tsx

How to pass value via `props` in export function in react-hooks

How to use props in export function in react-hooks. I would like to export a function to feed onSubmit() in Profile.js.
I would like to get the companyName value from CompanyDetails and passed to Profile and use in onSubmit() via props. I am getting 'companyName' undefined error now.
import CompanyDetails from "./CompanyDetails";
const Profile = () =>{
const onSubmit = (companyName) =>{
const profileData = async () => {
try {
const res = await axios.put('http://localhost:8000/service/company', companyName);
if (res.data.success) {
// push do the rest of code after save here...
}
else {
console.log(res.data.message);
}
} catch (e) {
console.log(e.response.data.message);
}
}
//return () => { profileData() }
profileData();
}
return (
<div className="profile_wrapper">
<div className="wrap">
<h1>Details:</h1>
</div>
<CompanyDetails
onSubmit={onSubmit}
/>
</div>
);
}
export default Profile;
export default function CompanyDetails ({onSubmit}) {
const [companyName, setCompanyName] = useState('');
const handleChange = (e) => {
e.persist();
const {value} = e.target.value
setCompanyName(value);
}
return (
<div className="container">
<Select defaultValue={'DEFAULT'} name="company" onChange={e =>handleChange(e)} id="select">
<option value="DEFAULT" disabled>Choose an option</option>
<option value="company1">Company 1</option>
<option value="company2">Company 2</option>
<Select>
<Button onClick={onSubmit(companyName)} color="primary">
Save
</Button>
</div>
);
}
Ok, so I finally got it working. Although I had to basically rewrite everything from scratch to try and catch all the weird little bugs that would come up. But I believe the crux of the problem was making several improper function calls. You can take a look at the changes and see where I did things differently.
Here is a Codesandbox with the working code.
I had to mock making an axios request, but since the mock returns a promise just like an axios request would, you should be able to replace it with a working API on your end.
If things still aren't working, then leave a public API endpoint in the comments and I will test everything without the mocks.
Also note that sometimes I encountered an error with importing the material-ui components. But this seems to be an issue with Codesandbox and not with the components themselves.
at your CompanyDetails you need a handler for onClick like:
const onClickHandler = () => onSubmit(companyName)
<Button onClick={onClickHandler} color="primary">Save</Button>
or pass as an arrow function:
<Button onClick={) => onSubmit(companyName)} color="primary">Save</Button>
there is also issues regarding your onSubmit function at Profile component. when you call onSubmit you return another function return () => { profileData() }, when you should have called return profileData(). though you could just mark your onSubmit function as async and avoid declaring other function inside it imho.

Resources