I'm new to Redux (and React as well) so this is probably a very elementar question, but I was unable to find a straight answer for it over the Internet.
There is a sign up screen on my app that I call SignUp
. It is pretty straightforward and have just three inputs (email
, password
and passwordCheck
) and one button (signup
).
Thing is, I want to make my user life as simple as possible, so as passwordCheck
is changed, I check it against password
. If them match, signup
is setted enabled and if them don't, signup
goes disabled and a message is show to the user.
When I was using only React things were pretty simple - I simply made a this.state
:
this.state: {
// ... stuff
password: "",
passwordIsValid: false, //test password against a predicate function
passwordMessage: "", //if the password is not valid there is a message
passwordStyle: "", //if password is wrong i put some classes here
passwordCheck: "",
passwordCheckMessage: "", //the 'passwords do not match' message goes here
passwordCheckStyle: "",
passwordsMatch: false
}
And then I handled the app's state inside SignUp
.
Now I'm using Redux as well, so I killed this.state
, moved everything to store
and started using reducers instead of handlers.
Everything works for password
, but passwordCheck
is a different story. Since I need to know Store's password
to check it against passwordCheck
I have been unable (so far?) to do it on passwordCheckReducer
. Instead, I removed passwordCheckMessage
, passwordCheckStyle
and passwordsMatch
from passwordCheckReducer
, calculating this values on SignUp
. It seems to me as a very wrong and ugly way to settle the whole thing down though.
So, instead, I would like a more elegant and Redux-like solution.
If I could get store
to be on passwordCheckReducer
's state
I would be able to set passwordsMatch
and the others in the reducer while keeping it pure. Unfortunately, I have been unable to do it (so far?).
So, I would like to know if what I want to do is possible or if there's others, more desirables, ways to do it.
OBS1: Wherever I look on the Internet, I found the Redux official documentation on the subject of initializing state1. I do not think preloadedState
is the way to resolve my problem, though, since it is used to iniatilize store
, not to make it avaiable on my reducers. Instead, I simply need to have store
- even when empty - visible to passwordCheckReducer
.
OBS2: I know I could pass store
as a key of action
, but since the reducer pattern includes a state
argument it seems redundant to me to define pass such in action
.
Here is my Store:
// store.js
import { applyMiddleware, createStore } from 'redux';
import { createLogger } from 'redux-logger';
import thunkMiddleare from 'redux-thunk';
import { Reducers } from '../reducers/reducers';
const logger = createLogger();
const middleware = applyMiddleware(thunkMiddleare, logger);
export const Store = createStore(Reducers, middleware);
I'm using combineReducers
:
// reducers.js
import { combineReducers } from 'redux';
import { emailReducer } from './emailReducer';
import { passwordReducer } from './passwordReducer';
import { passwordCheckReducer } from './passwordCheckReducer';
export const Reducers = combineReducers({
emailChange: emailReducer,
passwordChange: passwordReducer,
passwordCheckChange: passwordCheckReducer
});
And here is passwordCheckReducer
:
// passwordCheckReducer.js
import { CHANGE_PASSWORDCHECK } from '../actions/actionTypes';
const initialState = {
passwordCheck: ""
};
export const passwordCheckReducer = (state=initialState, action) => {
const { payload, type } = action;
switch(type) {
case CHANGE_PASSWORDCHECK:
const passwordCheck = payload;
return {
...state,
passwordCheck
};
default:
return state;
}
};
Last, this is the implementation of mapDispatchToProps
:
// SignUp.js
const mapDispatchToProps = dispatch => ({
handleEmailChange: e => dispatch(updateEmail(e.target.value)),
handlePasswordChange: e => dispatch(updatePassword(e.target.value)),
handlePasswordCheckChange: e => dispatch(updatePasswordCheck(e.target.value))
});
If I remember correctly, when you pass your reducer to combineReducers, that reducer will receive the redux module state (so, what the initialState was, not the entire app's root state object). You don't have access to state from other reducers.
You have a few options.
I think having separate reducers for password
and passwordCheck
is somewhat overkill. I would go with one reducer for the entire form:
const initialState = {
password: '',
passwordCheck: '',
// ....
}
export const signupReducer = (state=initialState, action) => {
const { payload, type } = action;
switch(type) {
case CHANGE_PASSWORD:
return { ...state, password: action.password}
case CHANGE_PASSWORDCHECK:
const passwordCheck = payload;
if (state.password != state.passwordCheck)
return { ...state, passwordCheck, error: "..."}; // or something like this
return {
...state,
passwordCheck
};
default:
return state;
}
};
If you want to split things up, you can always define reducers, and call them from a parent reducer, passing the whole parent-reducer state. Something like:
import passwordReducer from './passwordReducer'
import passwordCheckReducer form './passwordCheckReducer'
export const signupReducer(state = initialState, action) => {
state = passwordReducer(state, action)
state = passwordCheckReducer(state, action)
return state
}