我很难弄清楚如何组合ui小部件还原器和react渲染树,这样我以后就可以使用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});
(我预计剩下的代码包括connect()和createStore()用于封装*是显而易见的。)
只要所有封装视图中的所有操作都具有唯一的类型,这种几乎就可以工作。但问题是NameWidget的mapStateToProps;它需要从上面改变,以使用一条新的路径到它的商店部分:
...
const mapStateToProps = (state) => {
return {
name: state.nameWidget.name,
};
};
...
等等——取决于祖先如何组合Reducers()来定义他们更强大的超级小部件和应用程序,后代必须以某种方式改变他们映射StateToProps()的方式来进行补偿。这破坏了代码的封装和可重用性,我不知道如何在react redux中绕过它。如何通过组合combineReducers()来定义小部件,然后将生成的存储映射到这些小部件的道具,就像在任何地方写一次就用一样?
(毫无疑问,重复组合Reducers()会创建一个自下而上的形状,这似乎很奇怪,但mapStateToProps()似乎采用了自上而下的"绝对路径"方法来查看该形状。)
有趣的问题。我想知道要求父级生成一个guid并通过props传递,然后简单地将其用作小部件实例的状态对象的索引是否符合您的目的。因此,每当您要创建此小部件的新实例时,您都需要在父对象中为其创建一个ID,然后将其传递到道具中,然后connect
方法就可以在mapStateToProps和mapDispatchToProps中使用它。我想在理论上,你甚至可以自己创建这个父组件,当它被使用时,它可能是透明的,只需使用componentDidMount生成一个新的guid来存储在组件状态并传递给子组件。
我把这个问题归功于John,因为他本质上提供了正确的答案魔法,通过mapStateToProps()的ownProps函数arg传递存储"路由"逻辑。但我更喜欢我自己的说法。本质上,每当你在EncapsulatingReducer.js中组合Reducers()时,你必须记住在EncapsulationView.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>
);
}
...