react-form: How to use multiple forms in combineReducers? - reactjs

Disclaimer: It is possible, that this question seems stupid because I might have overlooked anything obvious or self-evident in the docs. For now my reducer-setup looks like this:
import editBlogEntryForm from './blog/editBlogEntryForm';
import login from './login'; // <-- a directory with index.js
import blog from './blog'; // <-- a directory with index.js
export default combineReducers({
blog: blog,
login: login,
form: editBlogEntryForm
});
Where shall I put other forms in the future?
Learning more about seperating reducers, I moved the blog and login reducers into their own directories/files where I use reducer composition. But it seems I cannot move the form anywhere, it has to stay top-level, which does not make sense, if I later want to introduce a login-form or so. I hope I don't have to put everything into the same form-reducer, resulting in a quite big switch-statement?
I already tried moving the form: editBlogEntryForm "down" into the blog reducer, but the form stops working/updating then.
Here is the reduxForm call in my container-component for the form:
EditBlogEntryFormContainer = reduxForm({
form: 'EditBlogEntryForm',
validate
})(EditBlogEntryFormContainer);
Can someone please point me the right direction please?
From the redux-form-docs http://redux-form.com/6.6.3/docs/GettingStarted.md/ I got this:
Note that, by default, the key used to pass the redux-form reducer to
combineReducers should be named form. Although there is support for
custom key names, see getFormState config for more details.

Thanks to #Thijs Steel and his pointing me to the getFormState prop of reduxForm() (see docs http://redux-form.com/6.6.3/docs/api/ReduxForm.md/), I came to a solution that is working with verbose form name and state-location. Though I still cannot follow the docs saying about getFormState:
This functionality is rarely needed, and defaults to assuming that the
reducer is mounted under the form key.
I think we all want more than one form and form-reducer in our apps, so using getFormState rather seems the standard case to me. But still, I'm not sure, I might have missed anything obvious.
Solution is to change the connection of the form-component with the redux-form like this:
EditBlogEntryFormContainer = reduxForm({
form: 'EditBlogEntryForm',
getFormState: (state) => state.blog.editBlogEntryForm, // <-- your form reducer location
validate
})(EditBlogEntryFormContainer);
So any form can have its state from any location of the app's state making multiple forms possible.
Not using getFormState is defaulting to using the form-reducer from top-level of the state, which would result into this:
EditBlogEntryFormContainer = reduxForm({
form: 'EditBlogEntryForm',
getFormState: (state) => state.form, // <-- default of reduxForm
validate
})(EditBlogEntryFormContainer);

First of all, if you want to add another form in the Future you could call it form2: ... (Or some other, more apropriate name).
When you moved the form down, did you also use combinereducers to combine the lower level ones?. If so, bear in mind that to acces the state you would need to do state.blog.form and state.blog.main

Related

Unique name for redux form

So I'm trying to make generalised Form component that I export:
export default reduxForm({
form: 'form name', // I'd like to set this based on this.props.name
validate // & this.props.validate (but obviously props isn't accessible)
})(Form)
but obviously the export statement is outside the class component, so it doesn't have access to the name/validate props of this specific Form.
Is it possible to do? Or have I misunderstood how I should be structuring this?
This is an interesting question.
Based on reduxForm architect, of course it tries to setup a string, for instance, unique identifier, so that if you have multiple form, the values can be arranged in the right objects.
If you want to attach another string, then you are looking at the run-time variable of a form, which is exactly a field :)
Give you an example, form1.values["name"] = "Form1". Btw I might not give you the right syntax, since I'm not familiar with reduxForm, but the idea is similar.

Can I change state key names from reducer?

I apologise if this is a stupid question, but I'm not sure how to get the functionality I'd like and the docs seem to not include it.
My current rootReducer has the following state structure
export interface GameStatusState {
gameLevel: number,
playerHealth: number,
playerCoins: number,
playerMoves: number
}
However, I'd like it to be like so;
gameStatus: {
gameLevel: number,
playerHealth: number,
playerCoins: number,
playerMoves: number
}
I can achieve this using:
combineReducers({gameStatus: rootReducer})
But is there a more conventional way I can do this?
That is exactly how you're supposed to do it.
In Redux, we normally divide up our reducer logic based on "slices" of state. The root state structure is defined by whatever the root reducer function returns, and that root reducer is normally generated using combineReducers().
So, calling combineReducers({someFieldName: someReducerFunction}) says we want our root state object to have a state.someFieldName field, and the updates to that section of the state are handled by someReducerFunction.
For more details, see the Redux Initializing State and Using combineReducers docs pages, as well as the new "Redux Essentials" core docs tutorial.

redux-forms initialization not working after upgrade to 7.x

I have upgraded my recct-redux application to user redux-form 7.x (latest at this time). After update, initial values aren't getting populated in the form. (It used to work with redux-forms 2.x)
Is it a bug or am i doing something silly ?
Below is my code link
https://github.com/santhoshml/Bookbild-UI/blob/master/src/components/user_profile.js
[Update] : I found the problem (but I still don't have an answer) ,
I made 3 calls to the backend server and each one returned at it own time and each kept overwriting the other. If I keep refreshing, the values keep doing disco :)
Now I have wrapped all the 3 calls in Promise.all and I can see all the fields populated, BUT when I edit and submit, onClickHandler doesn't have any updated values.
To debug this, I printed the props in the render and it does not have the values returned from db. But surprisingly the values are populated in the textfields. This is amazingly confusing. Please help. Link below
https://github.com/santhoshml/Bookbild-UI/blob/master/src/components/user_profile.js
Thank you in advance,
Santhosh mL
Did you check this documentation out. According to that you will need to connect() to the Redux state yourself and map from the data reducer to the initialValues prop. So it should be something like this.
// Decorate with reduxForm(). It will read the initialValues prop provided by connect()
InitializeFromStateForm = reduxForm({
form: 'initializeFromState' // a unique identifier for this form
})(InitializeFromStateForm)
// You have to connect() to any reducers that you wish to connect to yourself
InitializeFromStateForm = connect(
state => ({
initialValues: state.account.data // pull initial values from account reducer
}),
{ load: loadAccount } // bind account loading action creator
)(InitializeFromStateForm)
export default InitializeFromStateForm
What you need to do is provide the action to load the data. Then map over the data and set it into the initialValues property.

Data modelling my store in Redux

Consider this:
I have an application that is going to end up being pretty large. It is a dashboard which will give you access to various utilities, one of which being a todo app.
If I was just going to build just a todo app, then my state object would look like so:
{ todos:[], completed:false, todoInput:''};
todoInput would be tied to a form field and and upon clicking add, it would alter the todos array and toggle the completed field. So my combineReducers() function would look like this.
combineReducers({todos,completed,todoInput});
This would make sense because all the state is relevant to the todo App because there is JUST a todo app.
Now because I am building a much more complicated application which also has a todo app, this is how my state would potentially look like:
{
otherState:'',evenMoreState:[]',evenMore:{},
todo:{ todos:[], completed:false, todoInput:''}
}
As you can see I have separated todos into a separate object now, so it is encapsulated and more organised. So I have 2 questions.
1) Is this a good idea? It seems like the logical move because my application will grow in size and I don't want all the pieces of state floating around as properties to the main state object. Have I gone about this correctly?
2) My combine reducers (as far as I know) cannot take a nested object. So it will now look like this.
combineReducers({ otherState,evenMoreState,evenMore,todo})
so now my reducer compositions will have to be done inside the reducer which handles the todo state. Is there a better/different way to do this?
Thanks
Yes, you're absolutely on the right track. It's also worth noting that you can use combineReducers multiple times, such as:
const rootReducer = combineReducers({
otherState : otherStateReducer,
todos : combineReducers({
todos : todosReducer,
completed : todosCompletedReducer,
todoInput : todoInputReducer
})
The overall todos section could be be defined separately, and imported and referenced in the top-level combineReducers call if desired.
});
You may want to read through the Redux docs section on "Structuring Reducers" for more information on ways to organize reducer logic, as well as the Redux FAQ on organizing nested state. In addition, the Redux Techniques and Redux Architecture sections of my React/Redux links list have links to a variety of articles about building real-world Redux applications.

CombineReducers and good structure in react-redux

I made calendar part of app, and also tried to make it can be reusable.
Then I also made another part using calendar.
So I used combineReducers function.
import calendar from '../../common/calendar/Reducer';
import insatester from './Reducer';
const reducerCombiner = combineReducers({
calendar,
insatester
});
export default reducerCombiner
Then, I used the combined reducer to make a store.
It seems to work fine first.
But the store and data was separated.
example)
store.calendar.data1 <- from calendar
store.insatester.data2 <- from insatester
Then I tried to get calendar event DB data from insatester, because I think reusable calendar doesn't do that.
But the problem is using stored db data from calendar app also need to access store.insatester, it doesn't make sense.
Question1. How can I combine reducers to make the store having same level?
example)
store.data1 <- from calendar
store.data2 <- from insatester
Question2. Is my structure having problems? How can I change it?
You need to write a reducer that can access the full state. As you've seen, combineReducers limits you to a single slice of the state. You can put your custom reducer in front to handle the actions that need data from both slices, and let the rest fall into the combineReducers part.
This page of the Redux docs explains: http://redux.js.org/docs/recipes/reducers/BeyondCombineReducers.html#sharing-data-between-slice-reducers

Resources