我想知道其他人如何处理将redux动作创建者从智能顶级组件传递到许多较低级别的哑组件而不会使他们的道具定义膨胀。
例如,关注this excellent tutorial on redux,如果我将动作创建者列表传递到道具中就像这样
import Voting from './Voting';
import * as actionCreators from '../action_creators';
...
export const VotingContainer = connect(
mapStateToProps,
actionCreators
)(Voting);
然后在我的Voting组件中,我可以访问actionCreators,这非常酷。
但是,如果我说20个用于投票的actionCreator及其所有子组件,例如。
Voting -> VotingContainer -> VotingDetail -> VotingFoo -> VotingBar
然后我最终得到看起来像这样的渲染函数
class Voting extends React.Component {
render(){
<VotingContainer
actionCreator1={this.props.actionCreator1}
.
.
.
actionCreator15={this.props.actionCreator15} />
}
}
class VotingContainer extends React.Component {
render(){
<VotingDetail
actionCreator1={this.props.actionCreator1}
.
.
.
actionCreator12={this.props.actionCreator12} />
}
}
.
.
.
class VotingFoo extends React.Component {
render(){
<VotingBar
actionCreator1={this.props.actionCreator1}
.
.
.
actionCreator6={this.props.actionCreator6} />
}
}
对于这种情况是否有最佳实践,这种方法可以将actionCreators组合在一起,而不会在每一步都没有大量的样板?我在任何教程/示例中都没有看到任何内容......
答案 0 :(得分:24)
只需将树下的组件连接到Redux即可 我们过分强调示例中的“顶部的一个容器” 当我们谈论非常简单的应用程序时,这是有道理的。
对于任何复杂的应用,一旦传递道具变得单调乏味,下面会connect()
组成。
我在免费视频中介绍了这一点:请参阅Extracting Container Components以及接下来的几个视频。
答案 1 :(得分:0)
我发现在大多数情况下,我在核心ui组件周围有很多愚蠢的包装器,在最嵌套的组件中需要来自顶部容器的大多数道具。 因此,ES6 ...语法有很多帮助。
你可以这样做:
<VotingBar {...this.props} />
这相当于:
<VotingBar
actionCreator1={this.props.actionCreator1}
.
.
.
actionCreator6={this.props.actionCreator6} />
答案 2 :(得分:0)
为避免将层级分配的资产传递到实际使用这些道具的地方,可以使用React Context用上下文(一些特定数据)包装顶部的Child。
这是一个简单的用例示例,其中定义了多个reducer,每个reducer负责自己的状态(在此示例中为计数器)
一个<Button>
位于<Modal>
内部,而两个 Modal 组件位于 App 内部,每个组件最终都应“广播”内部所做的更改(在最深层的组件中)到顶部组件( App ),后者实际上是在监听更改并对其进行操作。
只有 App 关心 Button 中的更改,但是由于可以有很多 Button 组件,因此 App 必须知道哪个深度组件采取了什么行动,并将正确的数据分配回 Redux 。
Modal 只是介于两者之间的一种东西,仅用于表示目的。它不在乎任何道具或状态。除了直接通过上下文发送给它的内容之外,按钮也无关紧要。他通过Consumer
React.createContext
方法收听更改
const { Provider:ContextProvider, Consumer:ContextConsumer } = React.createContext({});
const {connect, Provider } = ReactRedux;
const {createStore, compose, combineReducers} = Redux;
const {Component} = React;
//// REDUX STORE STUFF /////////////////////////////////////
function reducer_foo(state = {value:1}, action){
if( action.type == 'FOO__SET_VALUE') // type should be unique
return { ...state, value:action.value }
else return state
}
function reducer_bar(state = {value:1}, action){
if( action.type == 'BAR__SET_VALUE') // type should be unique
return { ...state, value:action.value }
else return state
}
const rootReducer = combineReducers({
foo: reducer_foo,
bar: reducer_bar
});
//// REACT STUFF /////////////////////////////////////
// 2nd depth-level
// This component's "job" is to simply take a value and return a manipulated value to
// whoever called it. This is a very simplifed component, but think of a datepicker instead.
const Button = () =>
<ContextConsumer>
{v => <button onClick={()=> v.action(v.value + 1, v.name)}>Click to INC: {v.value}</button>}
</ContextConsumer>
// 1st depth-level (in reality this level will be more complex)
const Modal = () => <p><Button /></p>
// top depth-level component
class App extends Component {
constructor(props) {
super(props);
this.state = {};
}
// The deepest component will pass the value and the name for which to dispatch to
updateValue = ( value, name ) => {
this.props.dispatch({type:`${name.toUpperCase()}__SET_VALUE`, value})
}
render(){
const {foo, bar} = this.props;
return (
<React.Fragment>
<ContextProvider value={{value:foo.value, action:this.updateValue, name:'foo'}}>
<Modal />
</ContextProvider>
<ContextProvider value={{value:bar.value, action:this.updateValue, name:'bar'}}>
<Modal />
</ContextProvider>
</React.Fragment>
)
}
}
function mapStateToProps(state){
return state // in this example let's just pass the whole state for simplicity
}
const ConnectedApp = connect(mapStateToProps)(App)
const store = createStore(rootReducer);
ReactDOM.render(
<Provider store={store}>
<ConnectedApp />
</Provider>,
document.getElementById('root')
)
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.0/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/5.0.7/react-redux.min.js"></script>
<div id="root"></div>
答案 3 :(得分:-1)
当然,有多种方法可以解决这个问题。
最近,我开始在链条中跳过整个动作创建者功能,转而支持只需要商店和我的动作创建者直接在任何需要的地方并从那里派遣,例如
package chapter1.arrays.strings;
public class CountsOfRepeatedChars {
public static void main(String[] args) {
String str = "aabcccccaaa";
StringBuilder sb = new StringBuilder();
char[] arr = str.toCharArray();
char previous = ' ';
int count = 0;
int i = 0;
for (char c : arr) {
i += 1;
if (c == previous) {
count += 1;
} else {
if (count > 1) {
sb.append(count);
sb.append(c);
count = 1;
} else {
count = 1;
if (previous != ' ' && previous != c) {
sb.append(1);
}
sb.append(c);
}
}
if (arr.length == i) {
sb.append(count);
}
previous = c;
}
System.out.println(sb.toString());
}
}
// Output is a2b1c5a3
只需确保您的动作创建者的结果(即新状态)通过您的顶级组件向下传递。这使您的数据流保持单向且易于理解。