-
-
Notifications
You must be signed in to change notification settings - Fork 15.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
How to create nested reducers? #316
Comments
@michalkvasnicak You can try this https://github.com/lapanoid/redux-delegator works for me :) |
Can you give a slightly larger example? I'd write some code but because |
I don't know if this help but I work on simple testing tool. I have test system state reducer: import { START, FINISH } from '../constants/systemActionsTypes';
import { TEST_PASSED, TEST_FAILED, TEST_TIMED_OUT } from '../constants/testsActionsTypes';
const initialState = {
state: 'initial',
total: 0,
nextTest: 0,
passed: 0,
failed: 0,
timedout: 0,
duration: null
};
export default function system(state = initialState, action) {
switch (action.type) {
case START:
return {
...state,
total: action.total
};
case FINISH:
return {
...state,
state: 'finished',
duration: action.duration
};
case TEST_PASSED:
return {
...state,
nextTest: state.nextTest + 1,
passed: state.passed + 1
};
case TEST_TIMED_OUT:
return {
...state,
failed: state.failed + 1,
nextTest: state.nextTest + 1,
timedout: state.timedout + 1
};
case TEST_FAILED:
return {
...state,
nextTest: state.nextTest + 1,
failed: state.failed + 1
};
default:
return state;
}
} And I have tests reducer: import {
TEST_REGISTERED, TEST_STARTED, TEST_PASSED, TEST_FAILED, TEST_TIMED_OUT
} from '../constants/testsActionsTypes';
export default function tests(state = [], action) {
let sliced;
let test;
switch (action.type) {
case TEST_REGISTERED:
return state.slice().push({
number: action.number,
description: action.description
});
case TEST_STARTED:
sliced = state.slice();
test = sliced[action.number - 1];
test.state = 'started';
return sliced;
case TEST_PASSED:
sliced = state.slice();
test = sliced[action.number - 1];
test.state = 'passed';
return sliced;
case TEST_FAILED:
sliced = state.slice();
test = sliced[action.number - 1];
test.state = 'failed';
test.error = action.error
return sliced;
case TEST_TIMED_OUT:
sliced = state.slice();
test = sliced[action.number - 1];
test.state = 'timed_out';
test.error = action.error;
return sliced;
default:
return state;
}
} What I want is to have a reducer for given test so I don't need to do this array things but every test will have its own reducer so if test number is equal, it will change its state. And ideally to have tests reducer as child of system reducer too. this code is mess, I know |
You can compose it in such way
|
Why I didn't think of this? :) Thank you, but are there any other possible implementations? Because now I need to keep a fall-through for actions just to dispatch them to test reducer. |
Try https://github.com/acdlite/redux-actions#handleactiontype-reducer--reducermap, this can reduce some boilerplate but still it did not solve "carring actions" problem. I build https://github.com/lapanoid/redux-delegator for that purpose, but it sticked to immutable js currently, do not have time to fix this:( |
I'll take a look at this later. One single thing I want to say for now is that you should try hard to keep your data normalized and, if possible, flat. For example, if you have a list of reorderable articles, and each article has an author, your data shape shouldn't be See also https://github.com/gaearon/normalizr elaborating on this approach and implementing such normalization for nested API responses. |
Yes I know, normally I would store them in hash |
I just don't want to have all logic in single reducer if it is possible. |
The point is that, if you keep data normalized, you can use |
Ok so instead of nesting the data I should just keep flat structure and for example create new test reducer just for an test of given number? const state = {
system: { state: 'finished', total: 2, passed: 2},
tests: [/* array of tests e.g. for rendering of lists*/],
test1: { state: 'passed', duration: 100 /* */ },
test2: { /* .... */}
} |
I'll take a look later. If you could share a sample project to make sure I can run the code and play with it that would help. |
Here is project where I want to use it (now it uses only code from above). https://github.com/michalkvasnicak/prever/tree/develop It is dev version, not refactored, tests are missing, etc. |
I'm struggling with a similar case, normalized stores do help with fetching deeply nested data, but it gets tricky after the merged objects are part of the store. Then, the nested |
I don't think I understand. Can you describe the shape of your state before and after? |
When the reducer returns something like
Before |
We're probably talking about different things. Please show your data shape before and after. |
Will take a look, thanks! Updated above comment with data shape. |
Oh, okay, then my musings there should help. |
Your data shape should be
Similarly, for nested objects: {
feed: [id, id, ...],
articlesById: { id -> article: { id, name, authorId }, id -> article: { id, name, authorId }, ... },
authorsById: { id -> author, id -> author, ... }
} |
For stores which have arrays in them, like so
the action is dispatched as Is this the recommendation? or is there a way the |
I'd do it like this:
Then you can always pass |
Thanks, that project helped a lot, from: https://github.com/gaearon/flux-react-router-example/blob/master/scripts/pages/UserPage.js#L36 function getState(props) {
...
const starredOwners = starred.map(repo => UserStore.get(repo.owner)); In redux, should that logic be part of |
@gaearon |
any way I can avoid sending the |
Why avoid it? You want to resize a specific row. The reducer can't guess which one. It makes sense that |
good question. nothing wrong and you are not missing anything also :) Store shape
Table.jsx
Actions.js
Row.jsx
if you see the Now I have leaked the id into I saw the TODO example and the way it handles arrays and I convinced myself this is the right way, but now I had to use |
@rrag you don't need to pass <SearchBar query={this.props.query} onSearch={actions.search.bind(null, this.props.id)} />
// or
<SearchBar query={this.props.query} onSearch={query => actions.search(this.props.id, query)} /> |
Async Actions example shows how network request state reducer can be dynamically used. Real world example goes further by demonstrating how you can create a reducer factory for reusable reducers. I'm closing because these are the patterns solving the problem described in this issue. |
@gaearon for posterity, the current Async Actions link :) |
...and current reducer factory link |
Maybe I am missing something but how can I define reducer that will handle actions for instance of some resource which is stored in parent.
Simple example:
And I want to increase views for the article with id 1 but I don't want to have all logic in articles store.
I know I can handle it in articles store, but can it be done in the way I described? That I can dynamically define reducers and not only in one level but nested?
The text was updated successfully, but these errors were encountered: