如何将redux存储区中的封装ui状态映射到react-redux的道具?
我无法弄清楚如何编写ui窗口小部件缩减器并且同时对渲染树进行反应,这样我就可以稍后使用react-redux将缩减器的结果redux存储库映射到渲染树中的道具。如何将redux存储区中的封装ui状态映射到react-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,我将没有问题em:
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,
};
};
...
等等 - 在某种程度上取决于祖先combineReducers如何()来定义其不断宏伟的超级小工具和应用程序,后裔必须改变mapStateToProps()的补偿方式。这打破了代码的封装和可重用性,我看不到如何在react-redux中解决它。我怎样才能通过组合combineReducers()来定义小部件,然后将结果存储映射到这些小部件的道具上,这种方式在任何地方都是一次写入使用的?
(没有任何答案,似乎很奇怪重复的combineReducers()会创建一个自下而上的形状,但是mapStateToProps()似乎会采用自顶向下的“绝对路径”方法来查找该形状。
有趣的问题。我想知道它是否会满足您的目的,要求由父级生成GUID并通过道具传递,然后您只需将其用作您的窗口小部件实例的状态对象的索引。因此,无论何时您要创建此小部件的新实例,您都需要在父项中为其创建一个ID,然后在道具中传递该ID,然后将其用于connect
方法以用于mapStateToProps和mapDispatchToProps 。我想在理论上你甚至可以自己创建这个父组件,它在使用时可能是透明的,并且只需使用componentDidMount来生成一个新的GUID以存储在组件状态并传递给子组件。
谢谢约翰!我曾想过类似的东西,但我不知道如何“在道具中传递这些东西” - 你能详细说明一下吗?在我看来,connect()基本上是在我导入它返回的类型时调用的,而不是在我使用该类型时的render()中。我想我可以将connect()包装在函数中,然后在render()过程中动态调用它,并将该函数传递给您提到的ID;但我不确定每个调用渲染动态生成的类型的影响...... – jdowdell
所以我说你会有一个父组件,并在componentDidMount(实际上componentWillMount可能是更好的地方),你本质上调用'this.setState({childId:newGuid()})',然后在渲染方法中使用'
然后在传递函数mapStateToProps和mapDispatchToProps的参数时,在你的连接函数中,在_those_函数中使用它们的第二个参数(它是组件的道具)来使用id。例如,您的连接功能可能如下所示:https://jsbin.com/joyodaxazi/1/edit?js – John
我给了约翰这个问题的功劳,因为他基本上为答案提供了正确的魔法,通过mapStateToProps()的ownProps函数arg传递商店“路由”逻辑。但我更喜欢自己的旋律。从本质上讲,当你在EncapsulatingReducer.js combineReducers(),你要记住传递一个uiStorePath道具在EncapsulatingView.js:
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>
);
}
...
你打算在这个出口代码到另一个项目,或者您是否打算在同一个项目的另一个设置中重新使用该小部件? – Gennon
短期是项目特定的,但如果有一个好的模式/解决方案,希望它会支持这两种。 – jdowdell
你有没有看过[redux-form](http://redux-form.com/5.3.1/#/getting-started),他们使用一个定义的名称(表单)作为他们自己的root-reducer。这样,组件将查看所有值的state.form。 – Gennon