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
Related
My tech lead has given me a challenge to engineer a way only load only parts of the store that is needed for the UI that is loaded in a single page application. This is a big data application so that is why this is important. The idea is that entire store does not need to be loaded because the amount of data.
I implemented similar recently and found How to dynamically load reducers for code splitting in a Redux application? which features a link to http://nicolasgallagher.com/redux-modules-and-code-splitting/ where Nicolas describes how they did it at Twitter.
TL;DR You want lazy-loaded reducers for this. The approach described there is to have a class as a "reducer-registry". You register your reducer/s when you need to use it/them. The registry then calls a listener with a combined reducer which includes all the currently registered reducers. You attach a listener to the registry which calls replaceReducer on your store to update it's reducer.
My implementation is here.. https://github.com/lecstor/redux-helpers/blob/master/src/reducer-registry.ts
In your mapStateToProps you can select the keys of the redux store you need in your component.
For eg.
function mapStateToProps(state) {
const { key1, key2 } = state;
const {subKey, ...restKeys} = key1;
return {
remainder: ...restKeys,
subKey,
key2,
};
}
Now this data can be accessed in the component with this.props.remainder or this.props.subKey or this.props.key2
I have simple Redux reducer, but for some actions I need to show notifications, in order to do so I need to trigger my custom notification function within the reducer so:
case REDUCER_ACTION_NAME:
notificationDisplay.success("Message", {
additionalStuff: extraOptions
});
What's even worse I'm using react-intl for translations and I need the "Message"o be translate-ready so I'm adding this to the mix:
case REDUCER_ACTION_NAME:
notificationDisplay.success(<FormattedMessage id="message" defaultMessage="Message" />, {
additionalStuff: extraOptions
});
It creates a translation in span and requires react so my reducer starts with these imports:
import React from 'react';
import notificationDisplay from 'my-notifications';
import { FormattedMessage } from 'react-intl';
import {
// all the actions
} from './actions.jsx';
// reducer
Is it okay? I feel something here is off - like importing React in reducers is an anti-pattern, because all reducer examples I could find are so clean and sleek and there are no external libs there whatsoever.
Am I right or am I wrong and my code is perfectly fine?
You should not do any kind of computations in reducer. It should change the state and nothing else. The way you are using it is a complete anti-pattern. Because it is doing some UI actions. And Redux is nothing to do with the UI. It should be used as the store and only the store.
But you can use Actions which is way better than doing it in reducer.
Best way to achieve your goal is to use your reducer to just push the messages into an array in the redux store. And create a Container that uses that messages array to show success or error messages. And create a timer that removes the message from the array after some time.
Just look at the https://github.com/diegoddox/react-redux-toastr repo they are doing it very well.
Thanks
Akhil P
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.
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
I've been using redux for a few weeks now, and I've used flux before too, but I want to make sure I use redux the right way. All my research so far has pointed toward having a single store. This is fine, but most explanations and examples discuss a single page application. What happens when an application grows to more than 1 'single-page app'?
For example, a collection of single page applications, each organized as a module folder, each having nothing to do with each other. Is this a case where multiple stores are an option? So each module would have its own store, reducers, etc... Or is it still considered the redux way to have 1 store shared between all the modules?
BTW - by 'single-page app' I don't literally mean 1 page, but a module, which could consist of 1-5 actual pages that share data and other features
having a single store
This is exactly how Redux works. You have a single store and many actions. Each action, when dispatched, updates the store via reducer.
When you create store, you create a single store:
import { createStore } from 'redux';
import { render } from 'react-dom';
export default render(
<Provider store={createStore(...)}>
...
</Provider>
, document.getElementById('...'));
The createStore call returns a single store that will then be used by Provider component to pass data from the store to connected components (aka containers).
a collection of single page applications, each organized as a module folder, each having nothing to do with each other.
Each individual part can use its own part of the store. You can divide them using combineReducers function:
import { createStore } from 'redux';
import { render } from 'react-dom';
import moduleA from './reducers/moduleA';
import moduleB from './reducers/moduleB';
import moduleC from './reducers/moduleC';
const store = createStore(combineReducers({
moduleA,
moduleB,
moduleC
});
export default render(
<Provider store={store}>
...
</Provider>
, document.getElementById('...'));
and have store as object that has as many properties as there are reducers. Each reducer is now responsible for its own part of the state, therefore module. None of reducers, when done correctly, can mutate the part of the state different from the one this reducer is responsible for.
You should take a look at any boilerplate, there are many. For example, erikras/react-redux-universal-hot-example.