I have a FileUpload component that is connected through a redux-form Field. It calls input.onChange and input.onBlur with the selected file as a base64 string when a file is selected in an input field.
I'm using the asyncValidator redux-form option to validate the dimensions of the image, and I would like the file to be uploaded to the my server after the async validation has finished.
There doesn't seem to be any sort of afterAsyncValidation hook documented. What is the best way to accomplish this in redux-form?
redux-form was designed with the idea that your data would be saved to the server on submit.
However, there's nothing stopping you from putting your own .then() clause after your async validation to do that. Something like this?
// async function you already have that is looking at your
// picture field and rejecting the promise with errors
import validateDimensions from './validateDimensions'
// async function to upload the image
import upload from './uploadImage'
const MyForm = reduxForm({
form: 'myForm',
asyncValidate: values =>
validateDimensions()
.then(() => {
// we know validation passed
upload(values.prettyPicture)
// ^ not "return upload(values.prettyPicture)" to let this async
// validation promise resolve and do upload asynchronously
// after it has resolved
})
},
asyncBlurFields: [ 'prettyPicture' ]
})(MyForm)
Related
I've got this on my routes:
{
path: "/user/login",
element: <Login />,
action: loginAction,
},
So, I'm reaching the login action correctly when I submit the form in the login page.
The issue is, I'm trying to store some data using reducers but inside the loginAction is giving me an error when try to use the
async function action({ request }) {
const formData = await request.formData();
const dispatch = useDispatch(); <-- ERROR here
action as been imported as loginAction
import { action as loginAction } from "./pages/Login";
This is the error I'm getting:
Line 38:20: React Hook "useDispatch" is called in function "action" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use" react-hooks/rules-of-hooks
Of course, if I change the "action" to "Action" it doesnt giving me any error (after fixing the import) but it just doesnt work.
Any clue how to use useDispatch inside a action function from React 6.4?
Thanks!
Use reducers in action function.
React Hook "useDispatch" is called in function "action" that is neither a React function component nor a custom React Hook function
as the error says, you cannot call a hook like this, inside a simple function ! you should call useDispatch:
const dispatch = useDispatch();
inside your function component and make sure that it is called at the top level.
when submitting remix form with <Form>element i can't clear inputs after submit. In my particular case, that form is sitting on child route within <Outlet /> component (using nested routes here)
When form is submitted, all is working fine, redirect in handler goes to parent, parent is refreshing but child doesn't and inputs remains with values entered, that's a problem.
form is quite regular, inputs and button, all manages action function in parent
export const action: ActionFunction = async ({ request }) => {
const formData = await request.formData()
const data = Object.fromEntries(formData)
await doSomething(data)
return redirect('/route/add')
}
When instead of remix <Form/>element i use regular <form> element (its the only change) - total form refresh happen - and its also working, but extra request processing, and is impossible to use tasty remix hooks, like useTransition
how i cut the corner
const submit = useSubmit()
function submitHandler(evt) {
evt.preventDefault()
// total shame
evt.target.title.value = ''
evt.target.body.value = ''
submit(evt.target, {
action: '/route/add',
method: 'post',
})
}
so question is: how clear inputs after submitting ?
This is actually more of a React question instead of a Remix one.
Remix doesn't unmount your Outlet on navigation.
Remix is simply fetching data from your loaders and React is then rendering. As you know, initial state and default value do not get reset on re-render. The component must unmount. The simplest way is to update the key prop to trigger React to remount.
I have an implementation for a file upload api using drag and drop multiple files. I am making a call within the for loop as below
uploadFile(file:any[]) {
file.forEach((file, index)=>{
const formData = new FormData();
formData.append('report_file', file);
this.fileService.uploadFile(this.configurations.uploadUrl, formData)
.subscribe(fileResp=>{
console.log('file', fileResp);
}, error=>{
console.log('error while uploading files', error);
});
});
}
This works while uploading. However, I have a requirement to be able to cancel one of the pending file if needed. so, how do I cancel a particular http request if user is opting to cancel that upload? for the bulk cancel I can do unsubscribe to my observable but in this case I need to make others still be uploading.
Here's an idea:
const files$ = from(files);
files$.pipe(
mergeMap(
file => this.fileService.uploadFile(/* ... */).pipe(
takeUntil(fromEvent(getButtonThatCorrespondsToFile(crtFile), 'click').pipe(take(1)))
)
)
)
function getButtonThatCorrespondsToFile (): HTMLButtonElement { }
mergeMap - will allow you to start making a request for each file at the same time
takeUntil - attached to the request's observable of each file; makes sure that when a button that corresponds to a certain file is clicked, it will cancel the current request
Now I guess a small problem is how you get a button that corresponds to a file, but I think that wouldn't be too difficult. You could use, for example, #ViewChildren to get all the buttons, and by using the index in the mergeMap's projection function(2nd argument; mergeMap((val, idx) => ...)), you could identify a certain file.
In my application, I am using actions to do all of my ajax calls. When the results come back, it dispatches them to the reducer, which then puts it in the store. My component is bound to the property and will then be able to get it from the store.
However, I am having an issue trying to figure out the best way to do form submissions. From a listing page, a user can click on a link from any row to open a modal. This modal has a form in it. When the form is filled out, it will then pass the data along to an action, which will submit it. The only response from a valid submission is a HTTP 200.
Without using callbacks, how would the modal know that the ajax call is complete, so it can close itself? As of now, I have a flag in the store called form.processing. This is default to false, and the action will set it to true when it begins and false when its done. The component watches this and then knows when it goes from true to false and knows everything is done. However, I feel like there should be a better way.
Or should I be using callback in forms, even though we don't follow that process for any other ajax call?
Here are following pseudo code can help you:
constructor () {
this.state = {
disaplyModalPopup: false;
}
}
handleSubmit = () => {
this.setState({disaplyModalPopup: true})
let payLoad = { 'Key':this.state.something }
this.props.hitAPI(payLoad).then((res) => {
if (res.data.success) {
this.setState({
'disaplyModalPopup': false
})
}else{
this.setState({
'disaplyModalPopup': true,
'errorMessage': 'something wend wrong'
})
}
})
}
rendor (){
let errorMessage = {this.state.errorMessage}
let disaplyModalPopup = {this.state.disaplyModalPopup}
return (
{disaplyModalPopup ? <modal> </modal> : ''}
{ errorMessage? 'errorMessage': ''}
)
}
Here I have handled your modalPopup with disaplyModalPopup state.
And After get in the response saved in reducer, it is changes as {disaplyModalPopup: false}
And modalPopUp HTML will disappear.
But in case your API get in failed while making response.
So that case: i have handle as error message in as text
errorMessage where you can show your error message. So that Modal is closed side by side.
I am using the example http://redux-form.com/6.0.5/examples/wizard/ to accomplish my own form.
But how can I reset the first and second form when I use the wizard style form?
You can clear your form by calling dispatch(reset('myForm')); after you have submitted the data ideally at the parent component.
You can read more about it here
https://redux-form.com/6.7.0/docs/faq/howtoclear.md/
The tricky part is to make sure that dispatch is available at the component where you are submit (aka the parent component).
One way to do that is to wrap your parent component with connect(). After that, your component will receive a dispatch function as a prop. You can then use
this.props.dispatch.
This works for me
submitForm(values) {
const { dispatch } = this.props
axios.post(`YOUR_URL_HERE`, values)
.then(response => {
console.log(response.data)
dispatch(reset('MY_FORM'));
})
.catch(error => {
console.log("Unexpected error", error);
});
}