-
-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Question: Integration with redux-form #161
Comments
A temporary solution could be:
|
I have not fully wrapped my head around |
@yelouafi Could you please change the label to 'feedback wanted'. |
@rosendi onSubmit: (values) => {
return new Promise((resolve, reject) => {
dispatch(someActionCreator({ values, resolve, reject }))
});
} In saga: function* saga() {
while (true) {
const { payload: { values, resolve, reject } } = yield take(TYPE)
// use resolve() or reject() here
}
} |
@tokenvolt brilliant! Thanks so much for sharing that tip, totally solved the problem. You should blog/gist it somewhere. May be do an add-a-note PR here or over at redux-form. It really helps address the interaction between redux-sagas and redux-form. |
A 'gotcha' for the solution via @tokenvolt is that
works great, whereas
will error on submit asking for a handleSubmit() function. |
|
Ahh right, I see that the older version 5 exposes (or at least replicates |
@timothyallan really? Interesting, wonder what the new mechanism is, or lack thereof. Thanks for the heads up! |
It's much nicer actually :) http://redux-form.com/6.0.0-alpha.15/docs/api/ReduxForm.md/ |
@tokenvolt Thanks, it kinda works. I see the I tried reject({ username: 'Username doesn\'t exist' });
yield call(reject, 'Username doesn\'t exist'); None of them worked. (I'm on redux-form 6 alpha) For now my solution is to use import { stopSubmit } from 'redux-form';
. . .
yield put(stopSubmit('Login', { username: 'Username doesn\'t exist', password: 'And your password sucks' })); Is there a better solution? |
Nevermind, I've figured it out. This is the right way to do it: import { SubmissionError } from 'redux-form';
. . .
reject(new SubmissionError({ username: 'Username doesn\'t exist', password: 'Please enter your password' }));
// or yield for better testability
yield call(reject, new SubmissionError({ username: 'Username doesn\'t exist', password: 'Please enter your password' })); Thanks for your comment, it helped a lot! |
I've refined the solution of @tokenvolt with the help of a saga that would listen for success/failure actions and resolve/reject a promise returned to the redux-form. The idea being that one needs to just tell the form what action it should dispatch on submit and what actions it should wait for on success or failure. It's used like that.
The submit action's payload is the fields object and the payload for the failure action is the field errors object expected from redux-form. And here is the full
|
@angelyordanov This works great, thanks :) Do note that V6 of redux-form expects an instance of e.g. |
Using
This allows actions to be fired with just a quick:
The saga signature then simply looks like:
Because there are noop
So, calling the action creator without the promise still works:
The last piece is a little function that that I called
The complete pattern then looks like:
Hope this helps someone. |
Did someone try redux-form-submit-saga lib? |
@nktssh I have tried and worked pretty well. |
I do not know if these action creators are new or have some downside, but could we not just use the startSubmit and stopSubmit action creators and pass the form name with the submit action? Example:function* createEntity(entity, apiFn, formId, newEntity) {
yield put( entity.request() )
yield put( startSubmit(formId) )
const {response, error} = yield call(apiFn, newEntity)
if(response) {
yield put( entity.success(response) )
yield put( reset(formId) )
yield put( stopSubmit(formId) )
}
else {
yield put( entity.failure(error) )
// handle and format errors from api
yield put( stopSubmit(formId, serverValidationErrors) )
}
} Quite new to both redux-form and redux-saga, so there could be something i am missing here. |
@oanylund |
Agreed. I just came across this issue when i was facing the same challenge and thought i would share the solution i ended up with. |
After giving a quick look around this issue, I think the solution by @oanylund is the cleanest possible. |
@oanylund but what about validation when clicking 'Submit'? |
Seems like this is not the proper place to discuss this, but to answer shortly... stopSubmit sets the submitting flag to false, and you can also pass a new error object with your server errors to the form. |
@nktssh if you want to do client-side validation before you // validateFn returns an empty object if no errors
// otherwise, an object like { _error: 'errorMessage' }
// where _error is the redux-form key for form-wide errors
const errors = validateFn(values);
if (errors) reject(new SubmissionError(errors)) |
Here's my variation on @oanylund's answer. I have a module with a utility called formSaga: import { put, call } from 'redux-saga/effects'
import { startSubmit, stopSubmit, reset, SubmissionError } from 'redux-form'
export default function* formSaga(formId, apiSaga, ...apiSagaArgs) {
// Start form submit
yield put(startSubmit(formId))
try {
yield call(apiSaga, ...apiSagaArgs)
// Success
yield put(reset(formId))
yield put(stopSubmit(formId))
} catch (err) {
if (err instanceof SubmissionError) {
yield put(stopSubmit(formId, err.errors))
} else {
console.error(err) // eslint-disable-line no-console
yield put(stopSubmit(formId, { _error: err.message }))
}
}
} Example usage: for something like login, I want to be able to login either with an action directly (login/loginSaga), or through a form submission (submitLoginForm/submitLoginFormSaga). // Actions
export const login = createAction('Login', (email, password) => ({ email, password }))
export const submitLoginForm = createAction('Submit Login Form', (fields) => fields)
// Sagas
export function* loginSaga({ payload: { email, password } }) {
// call api, etc...
try {
yield call(request, { endpoint: '/auth/login', method: 'post', data: { email, password } })
} catch (e) {
// could throw a redux-form/SubmissionError here and it'll show up in the view!
// etc...
}
}
export function* submitLoginFormSaga({ payload: { email, password } }) {
// the ...apiSagaArgs in the util keep things pretty generic, but in this case
// (and most cases) we can just generate a "fake" action using the action creator
yield call(formSaga, 'login', loginSaga, login(email, password))
}
export function* watcherSaga() {
yield [
takeLatest(login.getType(), loginSaga),
takeLatest(submitLoginForm.getType(), submitLoginFormSaga),
]
} ** Might not be idiomatic, this is my first day with redux-saga. |
@andrewsuzuki How are you passing values from your form to your saga? |
i really think this line of code could be removed: redux-form/redux-form@7e07256#diff-28d8f38ee02f29d2bc406450f6c0d870R27 or at least could be added some option to turn this automatic setSubmitSuccess() off... |
+1 for new option to not calling Maybe, we can do this as plugin? |
I thought that maybe someone would be interested in my solution of this. I built it through the base pattern of @jcheroske - thanks for this! My solution mainly consists of some helper functions. (I wrote it in Typescript, so you may have to read a bit around type declarations) I created the same metaCreator for the use with
Then I could create the equivalent of
Edit: for this to work you need This can be used simply instead of
In addition, i created another helper - a function which wraps the original saga and catches errors from the original saga and rejects the error through the promise. It calls
This can be used like:
Where In
I hope this helps somebody :-) thanks for your help to create this! |
Following your suggestions, we have created a simple helper:
Then we import it with a simple
|
In case someone's interested, here's A not-that-simple-but-fully-working-way of handling Redux Forms with Sagas. |
Can't figure out the
onSubmit
method. Let's say I have the following form:From the redux-form docs:
The saga could look like:
Then:
How could I intercept the
CREATE_TODO_SUCCESS
andCREATE_TODO_FAILURE
actions in the promise? I'm really stuck here./cc @erikras
The text was updated successfully, but these errors were encountered: