I'm having trouble figuring out how to compose ui widget reducers and react render trees in tandem, in a way that I can later map the resulting redux store for the reducers to props in the render tree using react-redux.
Suppose a setup like this. I'm trying to make a NameWidget that I can use anywhere in any app:
NameView.jsx:
...
render() {
return <div> Name: {this.props.name} </div>;
}
...
====
NameAction.js:
...
export const CHANGE_NAME = 'CHANGE_NAME';
...
export function changeNameAction(newName) {
return {
type: CHANGE_NAME,
payload: newName,
};
}
====
NameReducer.js:
import { CHANGE_NAME } from './NameAction';
function ui(state = {name: ''}, action) {
switch(action.type) {
case(CHANGE_NAME):
return Object.assign({}, state, {name: action.payload});
break;
default:
return state;
}
}
export default ui;
====
NameWidget.js:
import { connect } from 'react-redux';
import { NameView } from './NameView';
const mapStateToProps = (state) => {
return {
name: state.name,
};
};
const NameWidget = connect(
mapStateToProps
)(NameView);
export default NameWidget;
If I use NameWidget directly I'll have no problem:
NameApp.jsx:
import { createStore } from 'redux';
import app from './NameReducers';
let store = createStore(app);
...
function render() {
return (
<Provider store={store}>
<NameWidget/>
</Provider>
);
}
However, I don't see how to make this modular. I can't actually put this Widget into anything else. Suppose I wanted to do this:
EncapsulatingView.jsx:
...
render() {
return (
<div>
<NameWidget/>
<SomeOtherReduxWidget/>
</div>
);
}
...
====
EncapsulatingReducer.js:
import { combineReducers } from 'redux'
import nameWidget from '../name/NameReducer';
import someOtherWidget from '../someOther/SomeOtherReduxWidgetReducer';
export default combineReducers({nameWidget, someOtherWidget});
(I expect that the remaining code including connect() and createStore() for Encapsulating* is obvious.)
This almost works, as long as all the actions across all encapsulated views have unique types. But the problem is NameWidget's mapStateToProps; it needs to change from above to use a new path to its section of the store:
...
const mapStateToProps = (state) => {
return {
name: state.nameWidget.name,
};
};
...
And so on - depending on how ancestors combineReducers() to define their ever-grander super widgets and apps, the descendants somehow have to change how they mapStateToProps() to compensate. This breaks encapsulation and reusability of code, and I don't see how to get around it in react-redux. How can I define widgets by composing combineReducers(), and then map the resulting store to those widgets' props, in a way that's write-once-use-anywhere?
(Apropos of nothing, it seems strange that repeated combineReducers() creates a shape that's bottom up, but that mapStateToProps() seems to assume instead a top-down "absolute path" approach to looking up into that shape.)