我无法弄清楚如何组合ui小部件缩减器并对渲染树进行串联反应,以后我可以使用react-redux将渲染树中生成的redux存储器映射到渲染树中的道具。
假设这样的设置。我正在尝试创建一个可以在任何应用程序中随处使用的NameWidget:
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;
如果我直接使用NameWidget,我就没问题了:
NameApp.jsx:
import { createStore } from 'redux';
import app from './NameReducers';
let store = createStore(app);
...
function render() {
return (
<Provider store={store}>
<NameWidget/>
</Provider>
);
}
但是,我不知道如何制作这种模块化。我实际上无法把这个Widget放到其他任何东西上。假设我想这样做:
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});
(我希望其余代码包括Encapsulating *的connect()和createStore()是显而易见的。)
只要所有封装视图中的所有操作都具有唯一类型,几乎就可以正常工作。但问题是NameWidget的mapStateToProps;它需要从上面改变以使用到其商店部分的新路径:
...
const mapStateToProps = (state) => {
return {
name: state.nameWidget.name,
};
};
...
依此类推 - 取决于祖先如何将Reducers()定义为更宏伟的超级小部件和应用程序,后代不得不改变mapStateToProps()的补偿方式。这打破了代码的封装和可重用性,我不知道如何在react-redux中解决它。如何通过组合combineReducers()来定义小部件,然后将生成的商店映射到那些小部件的道具,以一种一次性使用的方式在任何地方使用?
(没有任何结果,重复的combineReducers()创建一个自下而上的形状似乎很奇怪,但是mapStateToProps()似乎假设采用自上而下的“绝对路径”方法来查找该形状。)< / p>
答案 0 :(得分:1)
有趣的问题。我想知道它是否符合您的目的,要求父进程生成guid并通过props传递,然后您只需将其用作您的小部件实例的状态对象的索引。因此,每当您要创建此小部件的新实例时,您需要在父级中为其创建一个ID,然后在道具中将其传递下来,然后它将可供connect
方法使用mapStateToProps和mapDispatchToProps。我认为理论上你甚至可以自己创建这个父组件,它在使用时可能是透明的,只需使用componentDidMount生成一个新的guid来存储在组件状态并传递给子组件。
答案 1 :(得分:0)
我给了John这个问题的功劳,因为他基本上为答案提供了正确的魔力,通过mapStateToProps()的ownProps函数arg传递商店“路由”逻辑。但我更喜欢自己的旋转。基本上,每当你在EncapsulatingReducer.js中组合Reducers()时,你必须记住在EncapsulatingView.js中传递一个uiStorePath道具:
New NameWidget.js:
const _ = require('lodash');
import { connect } from 'react-redux';
import { NameView } from './NameView';
const mapStateToProps = (state, props) => {
const uiStore = (
(ownProps && _.has(ownProps, 'uiStorePath') && ownProps.uiStorePath &&
ownProps.uiStorePath.length) ?
_.get(state, ownProps.uiStorePath) : // deep get...old versions of lodash would _.deepGet()
state
);
return {
name: uiStore.name,
};
};
const NameWidget = connect(
mapStateToProps
)(NameView);
export default NameWidget;
====
EncapsulatingView.js:
...
render() {
const uiStorePathBase = ((this.props.uiStorePath &&
this.props.uiStorePath.length) ?
this.props.uiStorePath + '.' :
''
);
return (
<div>
<NameWidget uiStorePath={uiStorePathBase+"nameWidget"}/>
<SomeOtherWidget uiStorePath={uiStorePathBase+"someOtherWidget"/>
</div>
);
}
...